Files
mama-box/demo/gch/v4.js

169 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 11-02 首页「孕妈广场」效果稿 V4 · 故事卡堆(翻一翻)
一叠微微错开的真实照片卡,左右拖/轻扫/点箭头翻页,最后一张是「轮到你了」投稿卡;下方一行 #话题 入口。
只在 ?gcv=4 时由 index.html 加载;笔记是从 guangchang/data.js 摘的精选(占位示例)。 */
(function () {
var sec = document.getElementById('gcHome'), T = window.GC_TOPICS;
if (!sec || !Array.isArray(T) || !T.length) return;
var esc = function (s) { return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]; }); };
var kfmt = function (n) { return n >= 10000 ? (n / 10000).toFixed(1).replace(/\.0$/, '') + '万' : n >= 1000 ? (n / 1000).toFixed(1).replace(/\.0$/, '') + 'k' : String(n); };
var ava = function (n) { return 'assets/img/mom/' + n + '.jpg'; };
var reduce = window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches;
var CAT_CN = { family: '家庭婚姻', work: '职场', baby: '经验' };
var TOP = {}; T.forEach(function (t) { TOP[t.id] = t; });
/* 精选笔记(摘自 guangchang/data.js):[话题id, 标题, 作者, 孕周, 点赞, 照片] */
var N = {
family: [['f01', '怀孕后我妈每天三个电话,第四个是我爸打的', '栗子慢慢', '孕22周', 1286, 'd9d596acde1f'], ['f03', '表姑说我肚子尖肯定是男孩,我回家对着镜子看了半天', '一颗糖糖', '孕24周', 1680, 'ed263579a24d'], ['f04', '我家清淡婆家重口,孕期一桌菜吃出了两种味道', '桃子味汽水', '孕26周', 1940, 'bcfe0afdde7c'], ['f05', '关心太密的时候,我试着给自己留出了一点空间', '夏天的柚子', '孕31周', 1730, 'e2736880c759'], ['f06', '婆婆远在老家,每天给我转发十几条养胎文章', '远方的橙', '孕23周', 1460, 'cbd9db78f705'], ['f07', '我想顺其自然,我妈要我天天躺着保胎,我们僵了一周', '半杯温水', '孕19周', 1390, '9ed29de3419c'], ['f02', '奶奶让我别伸手晾衣服,说会绕脐带,我差点当场反驳', '青柠小满', '孕25周', 1520, '5cc12b5ca19d']],
work: [['w04', '赶方案到晚上九点,第一次感觉到胎动是在工位上', '七月的汽水', '孕21周', 2410, '6b374c98a040'], ['w01', '孕 11 周还没告诉领导,周会上差点没忍住吐出来', '青柠半杯', '孕16周', 1862, '874abee3d741'], ['w02', '第一次请产检假,我在领导门口来回走了三趟', '芋圆奶冻', '孕19周', 1544, '41ceb30d229a'], ['w03', '孕 20 周,会议室的椅子让我第一次意识到这件事', '半糖布丁', '孕24周', 1398, '0eb3d4ebc595'], ['w05', '孕 15 周在电梯里头晕,我还笑着说是没吃早饭', '蓝莓小山', '孕18周', 1683, 'bd8a5ddc39f5'], ['w06', '每天被问「今天还好吗」,我开始害怕走进办公室', '芒果西米露', '孕23周', 1576, '657bd5779003'], ['w08', '交接会上讲完最后一页,我在洗手间待了十分钟', '红豆沙沙', '孕35周', 2130, 'b88db204c69f']],
baby: [['b07', '我的顺产经历:对我来说最难熬的不是疼,是等', '小船靠岸', '宝宝3个月', 3126, '6440eaf30b5a'], ['b03', '孕20周半夜刷到一个案例,我躺在床上哭到凌晨', '不刷了周周', '孕22周', 2734, 'e4ff8785f404'], ['b02', '孕28周翻了下记账本,零碎小钱加起来吓我一跳', '记账的鱼', '孕28周', 2106, 'c5443c035a02'], ['b04', '孕30周囤了一柜子小衣服,宝宝出生后一半没穿上', '囤囤收手了', '宝宝5个月', 2251, '269846ead50f'], ['b05', '每次产检称体重前,我都会偷偷把外套脱掉', '秤上的梨', '孕26周', 1978, '81541ebb328d'], ['b06', '关于留意胎动,医生教的方法让我不再整天瞎想', '夜里数星星', '孕30周', 1687, 'fba0aa35ada9'], ['b01', '烧心鼻塞加腿抽筋,同事一句「都这样」让我闭了嘴', '豆豆妈咪', '孕21周', 1862, 'b9f151713ad0']]
};
var MOMS = ['m03', 'p02', 'm07', 'p05', 'm11', 'p09', 'm14', 'm16', 'p12', 'm19', 'm21', 'p07'];
function notes(cat) {
var l = cat ? N[cat] : [N.family[0], N.work[0], N.baby[0], N.family[1], N.work[1], N.baby[1], N.family[2]];
return l.map(function (a, i) { return { t: a[0], title: a[1], author: a[2], stage: a[3], likes: a[4], photo: 'guangchang/img/' + a[5] + '.jpg', av: MOMS[(i * 5 + (cat ? cat.length : 0)) % MOMS.length] }; });
}
function topics(cat) {
var l = T.filter(function (t) { return !cat || t.cat === cat; });
var hot = l.filter(function (t) { return t.hot; });
return (cat ? hot : hot.filter(function (_, i) { return i % 3 === 0; })).concat(l.filter(function (t) { return !t.hot; })).slice(0, 3);
}
function go(id) {
location.href = 'guangchang/' + (id ? '?t=' + encodeURIComponent(id) : '') + location.hash;
}
sec.className = sec.className.replace(/\bgv\d\b/g, '') + ' gv4';
sec.innerHTML =
'<div class="g4-head"><h2 class="g4-title"><span>孕妈广场</span></h2>' +
'<span class="g4-cat" role="tablist" aria-label="话题分类"><button type="button" class="on" data-cat="">全部</button><button type="button" data-cat="family">家庭婚姻</button><button type="button" data-cat="work">职场</button><button type="button" data-cat="baby">经验</button></span></div>' +
'<div class="g4-sub"><span class="g4-avs" aria-hidden="true">' + ['p05', 'm07', 'p11', 'm16'].map(function (n) { return '<img src="' + ava(n) + '" alt="" loading="lazy">'; }).join('') + '</span><span>翻一翻,看看其他妈妈的故事</span></div>' +
'<div class="g4-stage"><div class="g4-deck" aria-live="polite"></div>' +
'<div class="g4-rail"><span class="g4-pg"><b>1</b><i>/8</i></span>' +
'<button type="button" class="g4-arr prev" aria-label="上一张">‹</button><button type="button" class="g4-arr next" aria-label="下一张">›</button></div></div>' +
'<div class="g4-tags"></div>' +
'<div class="g4-foot"><button type="button" class="g4-all">去广场看全部 ' + T.length + ' 个话题 ›</button><span class="g4-mark">效果稿 V4</span></div>';
sec.classList.remove('hide');
var deck = sec.querySelector('.g4-deck'), pgB = sec.querySelector('.g4-pg b'), pgI = sec.querySelector('.g4-pg i'), tags = sec.querySelector('.g4-tags');
var cards = [], idx = 0, cur = '';
function storyCard(n) {
var tp = TOP[n.t] || {}, cat = tp.cat || 'family';
return '<div class="g4-card st c-' + cat + '" data-t="' + esc(n.t) + '" role="button" tabindex="0">' +
'<img class="g4-ph" src="' + esc(n.photo) + '" alt="" loading="lazy" decoding="async" draggable="false">' +
'<span class="g4-k">' + CAT_CN[cat] + '</span>' +
'<span class="g4-bot"><b class="g4-tt">' + esc(n.title) + '</b>' +
'<span class="g4-by"><img src="' + ava(n.av) + '" alt="" loading="lazy" draggable="false"><em>' + esc(n.author) + '</em><i>' + esc(n.stage) + '</i>' +
'<span class="g4-like"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5s-7.5-4.6-7.5-10.2A4.3 4.3 0 0112 7.6a4.3 4.3 0 017.5 2.7c0 5.6-7.5 10.2-7.5 10.2z"/></svg>' + kfmt(n.likes) + '</span></span></span></div>';
}
function submitCard() {
return '<div class="g4-card sub" data-sub="1" role="button" tabindex="0">' +
'<span class="g4-s-k">投稿征集</span>' +
'<b class="g4-s-t">轮到你了</b>' +
'<span class="g4-s-d">你的孕期故事,正是别的妈妈想听的</span>' +
'<span class="g4-s-r"><span><b>¥30</b><i>现金</i></span><em>+</em><span><b>500</b><i>兜豆</i></span></span>' +
'<span class="g4-s-n">被选中即可获得</span>' +
'<span class="g4-s-go">去投稿 ›</span>' +
'<span class="g4-s-avs" aria-hidden="true">' + ['m02', 'p04', 'm09', 'p13', 'm18'].map(function (n) { return '<img src="' + ava(n) + '" alt="" loading="lazy" draggable="false">'; }).join('') + '<i>本周已有 326 位妈妈投稿</i></span></div>';
}
function paint(cat, first) {
cur = cat; idx = 0;
deck.innerHTML = notes(cat).map(storyCard).join('') + submitCard();
cards = [].slice.call(deck.children);
if (!first && !reduce) {
deck.classList.add('deal');
cards.forEach(function (c, i) { c.style.transitionDelay = (Math.min(i, 3) * 60) + 'ms'; });
layout();
requestAnimationFrame(function () { requestAnimationFrame(function () { deck.classList.remove('deal'); layout(); setTimeout(function () { cards.forEach(function (c) { c.style.transitionDelay = ''; }); }, 500); }); });
} else layout();
tags.innerHTML = '<span class="g4-tl">大家在聊</span>' + topics(cat).map(function (t) { return '<button type="button" data-t="' + esc(t.id) + '">#' + esc(t.name) + '</button>'; }).join('');
tags.scrollLeft = 0;
}
/* depth:0 顶层;1、2 在后面错开;>2 藏在后面;<0 已翻走(左侧飞出) */
function layout() {
cards.forEach(function (c, i) {
var d = i - idx;
c.setAttribute('data-d', d < 0 ? 'out' : d > 3 ? 3 : d);
c.style.zIndex = 20 - Math.abs(d);
c.style.transform = '';
c.setAttribute('aria-hidden', d === 0 ? 'false' : 'true');
c.tabIndex = d === 0 ? 0 : -1;
});
pgB.textContent = idx + 1; pgI.textContent = '/' + cards.length;
sec.querySelector('.g4-arr.prev').disabled = idx === 0;
}
function next() {
if (idx < cards.length - 1) { idx++; layout(); return; }
/* 最后一张再往后 → 整叠收回,从第 1 张重新开始 */
idx = 0; deck.classList.add('reset'); layout();
setTimeout(function () { deck.classList.remove('reset'); }, 380);
}
function prev() { if (idx > 0) { idx--; layout(); } else bounce(1); }
function bounce(dir) {
var c = cards[idx]; if (!c || reduce) return;
c.style.transform = 'translateX(' + dir * 14 + 'px) rotate(' + dir * 1.5 + 'deg)';
setTimeout(function () { c.style.transform = ''; }, 160);
}
/* 拖动:方向锁 —— 先判断横 / 竖,竖向直接放行给页面滚动,横向才拦截(touchmove passive:false 才能 preventDefault) */
var drag = null, moved = false;
function start(x, y) { var c = cards[idx]; if (!c) return; drag = { x: x, y: y, dx: 0, lock: '', t: Date.now(), c: c }; moved = false; }
function move(x, y, e) {
if (!drag) return;
var dx = x - drag.x, dy = y - drag.y;
if (!drag.lock) {
if (Math.abs(dx) < 6 && Math.abs(dy) < 6) return;
drag.lock = Math.abs(dx) > Math.abs(dy) ? 'x' : 'y';
if (drag.lock === 'y') { drag = null; return; }
drag.c.classList.add('drag'); deck.classList.add('dragging');
}
if (e && e.cancelable) e.preventDefault();
if (e) e.stopPropagation();
moved = true;
if ((idx === 0 && dx > 0)) dx = dx * 0.3; // 第一张往右拖:阻尼
drag.dx = dx;
drag.c.style.transform = 'translateX(' + dx + 'px) translateY(' + Math.abs(dx) * 0.04 + 'px) rotate(' + dx / 18 + 'deg)';
}
function end() {
if (!drag) return false;
var d = drag; drag = null;
d.c.classList.remove('drag'); deck.classList.remove('dragging');
if (!d.lock) return false;
var v = Math.abs(d.dx) / Math.max(1, Date.now() - d.t);
if (d.dx < -70 || (d.dx < -24 && v > 0.45)) next();
else if ((d.dx > 70 || (d.dx > 24 && v > 0.45)) && idx > 0) prev();
else d.c.style.transform = '';
return true;
}
deck.addEventListener('touchstart', function (e) { if (e.touches.length === 1) start(e.touches[0].clientX, e.touches[0].clientY); else drag = null; }, { passive: true });
deck.addEventListener('touchmove', function (e) { if (drag && e.touches.length === 1) move(e.touches[0].clientX, e.touches[0].clientY, e); }, { passive: false });
/* 首页在 document 上有「左右滑切底部 tab」的手势(app.js touchend):卡堆横拖结束时截住 touchend,别让它把页面切走 */
deck.addEventListener('touchend', function (e) { if (end()) e.stopPropagation(); });
deck.addEventListener('touchcancel', end);
deck.addEventListener('mousedown', function (e) {
if (e.button) return; start(e.clientX, e.clientY); e.preventDefault();
var mm = function (ev) { move(ev.clientX, ev.clientY, ev); }, mu = function () { end(); document.removeEventListener('mousemove', mm); document.removeEventListener('mouseup', mu); };
document.addEventListener('mousemove', mm); document.addEventListener('mouseup', mu);
});
deck.addEventListener('click', function (e) {
if (moved) { moved = false; return; }
var c = e.target.closest('.g4-card');
if (!c) return;
if (c !== cards[idx]) { next(); return; } // 点到后面露出的卡角 → 翻到下一张
go(c.dataset.sub ? '' : c.dataset.t);
});
deck.addEventListener('keydown', function (e) {
if (e.key === 'ArrowRight') next(); else if (e.key === 'ArrowLeft') prev();
else if (e.key === 'Enter') { var c = cards[idx]; if (c) go(c.dataset.sub ? '' : c.dataset.t); }
});
sec.querySelector('.g4-arr.next').addEventListener('click', next);
sec.querySelector('.g4-arr.prev').addEventListener('click', prev);
tags.addEventListener('click', function (e) { var b = e.target.closest('button[data-t]'); if (b) go(b.dataset.t); });
sec.querySelector('.g4-all').addEventListener('click', function () { go(''); });
sec.querySelector('.g4-cat').addEventListener('click', function (e) {
var b = e.target.closest('button[data-cat]');
if (!b || b.classList.contains('on')) return;
[].forEach.call(sec.querySelectorAll('.g4-cat button'), function (x) { x.classList.toggle('on', x === b); });
paint(b.dataset.cat);
});
paint('', true);
})();