孕妈广场左右切分类:内容跟手拖动、胶囊同步挪,松手滑出滑入/不够回弹,两头阻尼;点标签同样滑入

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
deploy
2026-11-02 10:37:30 +00:00
parent 9cd77be0e1
commit 273fa9ec09
4 changed files with 77 additions and 29 deletions

View File

@@ -51,9 +51,6 @@ button{font:inherit;color:inherit;border:0;background:none;padding:0;cursor:poin
transition:transform .32s cubic-bezier(.3,1.3,.5,1),width .32s cubic-bezier(.3,1.3,.5,1),background .25s;pointer-events:none}
.cats::after{content:"";position:absolute;left:6px;right:6px;bottom:0;height:1px;background:linear-gradient(90deg,transparent,#EADAD3 20%,#EADAD3 80%,transparent)}
.cats button:active{transform:scale(.96)}
.wf.swl{animation:swl .32s ease-out}.wf.swr{animation:swr .32s ease-out}
@keyframes swl{from{opacity:.2;transform:translateX(36px)}to{opacity:1;transform:none}}
@keyframes swr{from{opacity:.2;transform:translateX(-36px)}to{opacity:1;transform:none}}
.cats .c-all{--cf:#F4593C}
/* 四块分类色(11-01 第四版:按用户选题清单分 家庭 / 婚姻 / 职场 / 母婴):kf 实色 · kt 深字 · kl/kl2 浅底两色 */
.k-family,.c-family{--kf:#E0566F;--kt:#C23A55;--kl:#FFF0F3;--kl2:#FFD9E0}
@@ -460,11 +457,13 @@ button{font:inherit;color:inherit;border:0;background:none;padding:0;cursor:poin
</div>
<div class="cats" id="cats" role="tablist" aria-label="分类"></div>
</div>
<div id="pane">
<div id="head"></div>
<div class="sh" id="fh"></div>
<div class="wf" id="wf"><div class="col"></div><div class="col"></div></div>
<div id="more"></div>
<div class="end"><b>— 到底啦 —</b><small>内容为示例占位,正式版将替换为妈妈们的真实分享</small></div>
</div>
</div>
<div id="dt" aria-hidden="true">
@@ -499,9 +498,9 @@ button{font:inherit;color:inherit;border:0;background:none;padding:0;cursor:poin
<div id="sheet" role="dialog" aria-modal="true"><i class="sg"></i><div class="sbd" id="sbd"></div></div>
<div class="toast" id="toast"></div>
<script src="topics.js?v=26110215"></script>
<script src="data.js?v=26110215"></script>
<script src="feed.js?v=26110215"></script>
<script src="topics.js?v=26110216"></script>
<script src="data.js?v=26110216"></script>
<script src="feed.js?v=26110216"></script>
<script>
(function () {
'use strict';
@@ -781,26 +780,75 @@ button{font:inherit;color:inherit;border:0;background:none;padding:0;cursor:poin
$('#cats').addEventListener('click', function (e) {
var b = e.target.closest('button[data-c]');
if (!b || (b.dataset.c === catF && !cur)) return;
catF = b.dataset.c; haptic();
selectTopic(''); /* 换分类回到该类「全部」,避免停在别类话题上 */
if (paneBusy) return;
var o = catOrder(), d = o.indexOf(b.dataset.c) - o.indexOf(catF);
if (!d) { catF = b.dataset.c; haptic(); selectTopic(''); return; }
slideTo(b.dataset.c, d > 0 ? 1 : -1); /* 点标签也滑入,和手势一致 */
});
/* 11-02 用户:「广场首页左右滑动,可以切到家庭婚姻、职场、经验」→ 横向滑动超过 60px 且明显比竖向大,就切到相邻分类;横滑列表、弹层里不触发 */
(function () {
var sx = 0, sy = 0, ok = false, sc = $('#sc');
sc.addEventListener('touchstart', function (e) {
var t = e.touches[0]; sx = t.clientX; sy = t.clientY;
ok = e.touches.length === 1 && !e.target.closest('.tcs,.hot,.htl .jn,#sheet,#note,.nd') && !document.body.classList.contains('lock');
}, { passive: true });
sc.addEventListener('touchend', function (e) {
if (!ok) return; ok = false;
var t = e.changedTouches[0], dx = t.clientX - sx, dy = t.clientY - sy;
if (Math.abs(dx) < 60 || Math.abs(dx) < Math.abs(dy) * 1.6) return;
var order = CATS.map(function (c) { return c[0]; }), i = order.indexOf(catF) + (dx < 0 ? 1 : -1);
if (i < 0 || i >= order.length) return;
catF = order[i]; haptic();
var wf = $('#wf'); wf.classList.remove('swl', 'swr'); void wf.offsetWidth; wf.classList.add(dx < 0 ? 'swl' : 'swr');
/* 11-02 用户:「广场首页左右滑动,可以切到家庭婚姻、职场、经验」+「突然就滑过去了,要有动效、让人感觉是被划过去的」
→ 手指横拖时整块内容(#pane)跟手走、顶部胶囊同步往目标分类挪;松手过 22% 屏宽(或甩得快)就整块滑出、换内容、从另一侧滑入;
不够就弹回。第一个 / 最后一个分类往外拖有阻尼。点分类标签也走同一套滑入。横滑列表、弹层里不触发。 */
var paneBusy = false;
function catOrder() { return CATS.map(function (c) { return c[0]; }); }
function barBetween(a, b, p) { /* 胶囊在 a、b 两个标签间按进度 p 插值 */
var bar = $('#cats .bar'); if (!bar || !a) return;
var wa = a.offsetWidth - 20, xa = a.offsetLeft + 10, wb = b ? b.offsetWidth - 20 : wa, xb = b ? b.offsetLeft + 10 : xa;
bar.style.transition = 'none';
bar.style.width = (wa + (wb - wa) * p) + 'px'; bar.style.transform = 'translateX(' + (xa + (xb - xa) * p) + 'px)';
}
function slideTo(c, dir) { /* dir 1 = 往左滑(去下一个),-1 = 往右滑 */
var pane = $('#pane'), W = $('#sc').clientWidth;
paneBusy = true; haptic();
pane.style.transition = 'transform .2s cubic-bezier(.4,0,1,1),opacity .2s';
pane.style.transform = 'translateX(' + (-dir * W * .6) + 'px)'; pane.style.opacity = '.15';
var bar = $('#cats .bar'); if (bar) bar.style.transition = '';
catF = c; renderCats();
setTimeout(function () {
selectTopic('');
pane.style.transition = 'none'; pane.style.transform = 'translateX(' + (dir * W * .35) + 'px)';
void pane.offsetWidth;
pane.style.transition = 'transform .38s cubic-bezier(.2,1.25,.4,1),opacity .25s ease-out';
pane.style.transform = 'translateX(0)'; pane.style.opacity = '1';
setTimeout(function () { pane.style.transition = ''; pane.style.transform = ''; pane.style.opacity = ''; paneBusy = false; }, 400);
}, 200);
}
(function () {
var sx = 0, sy = 0, t0 = 0, mode = '', ok = false, dx = 0, sc = $('#sc'), pane = $('#pane');
sc.addEventListener('touchstart', function (e) {
var t = e.touches[0]; sx = t.clientX; sy = t.clientY; t0 = Date.now(); mode = ''; dx = 0;
ok = !paneBusy && e.touches.length === 1 && !e.target.closest('.tcs,.hot,.cats,#sheet,#note,.nd') && !document.body.classList.contains('lock');
}, { passive: true });
sc.addEventListener('touchmove', function (e) {
if (!ok) return;
var t = e.touches[0], mx = t.clientX - sx, my = t.clientY - sy;
if (!mode) {
if (Math.abs(mx) < 8 && Math.abs(my) < 8) return;
mode = Math.abs(mx) > Math.abs(my) * 1.2 ? 'h' : 'v';
if (mode === 'h') pane.style.transition = 'none';
}
if (mode !== 'h') return;
if (e.cancelable) e.preventDefault();
var order = catOrder(), i = order.indexOf(catF), j = i + (mx < 0 ? 1 : -1), W = sc.clientWidth;
var has = j >= 0 && j < order.length;
dx = has ? mx : mx * .28; /* 两头没有下一页:阻尼 */
pane.style.transform = 'translateX(' + dx + 'px)';
pane.style.opacity = String(1 - Math.min(.45, Math.abs(dx) / W * .6));
var bs = $('#cats').querySelectorAll('button[data-c]');
barBetween(bs[i], has ? bs[j] : null, Math.min(1, Math.abs(dx) / W));
}, { passive: false });
sc.addEventListener('touchend', function () {
if (!ok || mode !== 'h') { ok = false; return; }
ok = false;
var order = catOrder(), i = order.indexOf(catF), dir = dx < 0 ? 1 : -1, j = i + dir, W = sc.clientWidth;
var v = Math.abs(dx) / Math.max(1, Date.now() - t0);
if (j >= 0 && j < order.length && (Math.abs(dx) > W * .22 || (v > .5 && Math.abs(dx) > 30))) { slideTo(order[j], dir); return; }
/* 不够:弹回 */
pane.style.transition = 'transform .35s cubic-bezier(.2,1.3,.4,1),opacity .25s';
pane.style.transform = 'translateX(0)'; pane.style.opacity = '1';
var bar = $('#cats .bar'); if (bar) bar.style.transition = ''; moveBar();
setTimeout(function () { if (!paneBusy) { pane.style.transition = ''; pane.style.transform = ''; pane.style.opacity = ''; } }, 360);
}, { passive: true });
sc.addEventListener('touchcancel', function () { if (mode === 'h') { pane.style.transform = ''; pane.style.opacity = ''; moveBar(); } ok = false; }, { passive: true });
})();
$('#fh').addEventListener('click', function (e) {
var b = e.target.closest('[data-t]'); if (!b || b.dataset.t === cur) return;