首页孕妈广场第五版「广场地面」:话题气泡之字形错落、头像站在气泡下、虚线小路串起、可横滑;v26110125

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
deploy
2026-11-01 23:06:10 +00:00
parent e961048616
commit 175d15240a
3 changed files with 89 additions and 19 deletions

View File

@@ -402,8 +402,8 @@ 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=26110124"></script>
<script src="data.js?v=26110124"></script>
<script src="topics.js?v=26110125"></script>
<script src="data.js?v=26110125"></script>
<script>
(function () {
'use strict';

View File

@@ -7,7 +7,7 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>兜爸心选 · 孕育陪伴</title>
<!-- V:26110124 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
<!-- V:26110125 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
<link rel="stylesheet" href="mama-box.css">
<link rel="stylesheet" href="night.css">
<!-- 夜晚模式:首屏绘制前就按上次的选择加 class,避免先白后黑闪一下(开关在首页右上角 #nightBtn,逻辑见 app.js) -->
@@ -530,7 +530,7 @@
<button type="button" class="gch-more" id="gchMore">去逛逛<i aria-hidden="true">›</i></button>
</div>
<div class="gch-who"><span class="gch-avs" id="gchAvs" aria-hidden="true"></span><span id="gchCnt"></span></div>
<div class="gch-row" id="gchRow"></div>
<div class="gch-row gb" id="gchRow"></div>
<style>
#homeBody>section.gc-home{--gt:255,120,150}
.gc-home{margin:16px -26px 0;padding:16px 26px 16px;border-radius:26px;position:relative;z-index:0;background-color:#fff}
@@ -580,6 +580,43 @@
.gch-card.all .ga{width:40px;height:40px;border-radius:50%;display:grid;place-items:center;background:#fff;color:#E2553A;font-size:22px;font-weight:700;box-shadow:0 4px 10px rgba(226,85,58,.15)}
.gch-card.all b{font-size:13.5px;font-weight:800;color:#8A5A4C}
.gch-card.all small{font-size:11px;color:#B39184}
/* —— 11-01 第五版「广场地面」 —— */
.gch-row.gb{display:block;height:250px;margin:10px -26px 0;padding:0;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain;scrollbar-width:none}
.gch-row.gb::after{display:none}
.gb-map{position:relative;height:250px;min-width:100%;width:max-content;display:flex;align-items:flex-start;gap:8px;padding:8px 26px 0;box-sizing:border-box;
background:radial-gradient(circle at 50% 50%,rgba(255,206,190,.22) 0 2px,transparent 2.5px) 0 0/22px 22px,linear-gradient(180deg,#FFF8F4 0%,#FFF1EA 100%)}
.gb-path{position:absolute;left:0;top:0;pointer-events:none}
.gb-path path{fill:none;stroke:#F6C9B8;stroke-width:2;stroke-dasharray:2 7;stroke-linecap:round}
.gb-w{position:absolute;width:18px;height:18px;border-radius:50%;object-fit:cover;border:2px solid #fff;box-shadow:0 2px 6px rgba(200,120,100,.25);opacity:.85;animation:gbWalk 6s ease-in-out infinite alternate}
.gb-w:nth-of-type(2n){animation-duration:7.5s;animation-direction:alternate-reverse}
@keyframes gbWalk{from{transform:translateX(-6px)}to{transform:translateX(8px)}}
.gb-col{position:relative;z-index:1;flex:none;width:128px;display:flex;flex-direction:column}
.gb-col.lo{padding-top:78px}
.gb-b{position:relative;display:block;width:128px;padding:0 0 26px;border:0;background:none;font:inherit;text-align:left;animation:gbBob 4.2s ease-in-out infinite;animation-delay:var(--d)}
@keyframes gbBob{0%,100%{transform:translateY(0)}50%{transform:translateY(-5px)}}
.gb-b:active .gb-in{transform:scale(.96)}
.gb-in{position:relative;display:flex;flex-direction:column;gap:5px;padding:10px 11px 10px;border-radius:20px 20px 20px 6px;background:#fff;color:var(--ink);
box-shadow:0 10px 20px -12px var(--ink),0 0 0 1.5px var(--c2) inset;transition:transform .15s}
/* 气泡尾巴:左下角一个小三角,指向下面的头像 */
.gb-in::after{content:"";position:absolute;left:12px;bottom:-5px;width:10px;height:10px;background:inherit;background:#fff;transform:rotate(45deg);border-radius:0 0 3px 0;box-shadow:1.5px 1.5px 0 0 var(--c2)}
.gb-b:nth-child(odd) .gb-in{background:linear-gradient(150deg,#fff 40%,var(--c1))}
.gb-a{position:absolute;left:5px;bottom:-2px;width:24px;height:24px;border-radius:50%;object-fit:cover;border:2px solid #fff;box-shadow:0 3px 8px rgba(0,0,0,.12);background:#FFE3D3}
.gb-ph{display:block;width:calc(100% + 22px);height:60px;margin:-10px -11px 2px;border-radius:20px 20px 0 0;object-fit:cover}
.gb-k{display:flex;align-items:center;gap:4px;font-size:10px;font-weight:800;letter-spacing:.5px;opacity:.85}
.gb-k em{font-style:normal;height:15px;padding:0 5px;border-radius:5px;background:linear-gradient(135deg,#FF7A45,#F4433C);color:#fff;font-size:9.5px;line-height:15px}
.gb-n{font-size:13.5px;font-weight:900;line-height:1.38;color:#2F2424;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.gb-b.ph .gb-n{-webkit-line-clamp:2}
.gb-m{display:flex;align-items:center;gap:5px;font-size:10.5px;font-weight:700;opacity:.8;white-space:nowrap}
.gb-m i{width:6px;height:6px;border-radius:50%;background:#2DBE6C;box-shadow:0 0 0 0 rgba(45,190,108,.5);animation:gbLive 1.8s ease-out infinite}
@keyframes gbLive{to{box-shadow:0 0 0 6px rgba(45,190,108,0)}}
.gb-end{justify-content:center;align-self:stretch;padding-right:26px}
.gb-all{width:112px;height:112px;margin:0 auto;border-radius:50%;border:1.5px dashed #F2B9A6;background:radial-gradient(circle at 35% 30%,#fff,#FFE9E0);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;font:inherit;color:#B9654E;box-shadow:0 10px 22px -14px #E2553A}
.gb-all b{font-size:26px;font-weight:900;line-height:1;color:#E2553A;font-family:"DIN Alternate","Helvetica Neue",Arial,sans-serif}
.gb-all span{font-size:11px;line-height:1.35;text-align:center}
.gb-all i{margin-top:3px;font-style:normal;font-size:11.5px;font-weight:800;color:#E2553A}
@media (prefers-reduced-motion:reduce){.gb-b,.gb-w,.gb-m i{animation:none}}
html.night .gb-map{background:radial-gradient(circle at 50% 50%,rgba(255,255,255,.06) 0 2px,transparent 2.5px) 0 0/22px 22px,transparent}
html.night .gb-path path{stroke:rgba(255,255,255,.18)}
html.night .gch-title{color:#F1E6EE}
html.night .gch-cat{background:rgba(255,255,255,.08)}
</style>
@@ -2432,7 +2469,7 @@
<script src="/_bridge/avatar-picker.js?v=26101105"></script>
<script src="/_bridge/hand-icons.js?v=26101202"></script>
<script src="js/zodiac-art.js"></script>
<script src="guangchang/topics.js?v=26110124"></script>
<script src="guangchang/topics.js?v=26110125"></script>
<script src="js/app.js"></script>
<!-- 2026-10-03 首页彩蛋:宝宝在肚皮底下动(独立文件,删掉这一行即整体下线;开关在「隐私权限」) -->
<script src="js/bump.js"></script>
@@ -2441,7 +2478,7 @@
<!-- 2026-10-05 首页头图:宝宝时不时动一动小手小脚(独立文件,删掉这一行即下线) -->
<script src="js/baby-limbs.js?v=26101201"></script>
<script src="js/coach.js?v=26101211"></script>
<script src="js/birth-flow.js?v=26110124"></script>
<script src="js/birth-flow.js?v=26110125"></script>
<!-- 禁止下拉回弹(2026-10-11 统一规则,与孕期体重同一段):竖向拖动时从手指下往上找还能朝该方向滚的容器,找不到才拦;横滑、双指、可滚列表不受影响 -->
<script>

View File

@@ -3,7 +3,7 @@
(function () {
'use strict';
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
var VER = '26110124';
var VER = '26110125';
const $ = (s, c) => (c || document).querySelector(s);
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
@@ -4206,21 +4206,54 @@
for (let r = 0; r < 5; r++) by.forEach(l => { if (l[r]) out.push(l[r]); });
return out;
}
/* 11-01 第五版「广场地面」(用户:重新调一版,要有广场那种创意):
一块可横滑的地面,话题是一个个说话气泡、气泡下站着一位妈妈的头像,一高一低「之」字形排开、轻轻浮动;
背景一条虚线小路从每个头像脚下穿过,空地上零星散着几个在逛的小头像。隔一个气泡带照片(topics.js 的 ph);「全部」里都是热门,不再逐个挂「热」。 */
const CAT_CN = { family: '家庭', marriage: '婚姻', work: '职场', baby: '母婴' };
const POOL = AVA_FEED.concat(AVA_TOP).filter(n => /^(p|m|mom-)\d/.test(n)); // 只用妈妈头像,k/s/baby 是孩子和风景
function bubble(t, i, allHot) {
const ph = i % 2 === 0 && t.ph;
return '<button type="button" class="gb-b' + (ph ? ' ph' : '') + (t.hot ? ' hot' : '') + '" data-t="' + esc(t.id) + '" style="--c1:' + t.c[0] + ';--c2:' + t.c[1] + ';--ink:' + t.ink + ';--d:' + (i % 5) * -.8 + 's">' +
'<span class="gb-in">' +
(ph ? '<img class="gb-ph" src="' + esc(t.ph) + '" alt="" loading="lazy">' : '') +
'<span class="gb-k">' + (CAT_CN[t.cat] || '') + (t.hot && !allHot ? '<em>热</em>' : '') + '</span>' +
'<span class="gb-n">' + esc(t.name) + '</span>' +
'<span class="gb-m"><i></i>' + kfmt(t.talk) + ' 人在聊</span></span>' +
'<img class="gb-a" src="' + avaSrc(POOL[(i * 7 + 3) % POOL.length]) + '" alt="" loading="lazy"></button>';
}
function paint(cat) {
const list = pick(cat);
/* 隔一张换成真实照片卡(11-01 用户:首页广场也放一些真实图),照片取 topics.js 的 ph;没图的照旧渐变卡 */
row.innerHTML = list.map((t, i) => {
const ph = i % 2 === 0 && t.ph;
return '<button type="button" class="gch-card' + (ph ? ' ph' : '') + '" data-t="' + esc(t.id) + '" style="--c1:' + t.c[0] + ';--c2:' + t.c[1] + ';--ink:' + t.ink + '">' +
(ph ? '<img class="gp" src="' + esc(t.ph) + '" alt="" loading="lazy">' : '<i class="gb" aria-hidden="true"></i><span class="gg" aria-hidden="true">' + esc(t.g) + '</span>') +
(t.hot ? '<span class="gh">热</span>' : '') +
'<span class="gk">' + ({ family: '家庭', marriage: '婚姻', work: '职场', baby: '母婴' }[t.cat] || '') + '</span>' +
'<span class="gn">' + esc(t.name) + '</span>' +
'<span class="gm">' + kfmt(t.n) + ' 篇 · ' + kfmt(t.talk) + ' 人在聊</span></button>';
}).join('') +
'<button type="button" class="gch-card all" data-t=""><span class="ga" aria-hidden="true">›</span><b>全部话题</b><small>' + T.length + ' 个话题</small></button>';
row.innerHTML = '<div class="gb-map"><svg class="gb-path" aria-hidden="true"><path/></svg>' +
list.map((t, i) => '<div class="gb-col' + (i % 2 ? ' lo' : '') + '">' + bubble(t, i, !cat) + '</div>').join('') +
'<div class="gb-col gb-end"><button type="button" class="gb-all" data-t=""><b>' + T.length + '</b><span>个话题<br>都在广场</span><i>去逛逛 ›</i></button></div></div>';
row.scrollLeft = 0;
requestAnimationFrame(walk);
}
/* 排好之后量一遍:小路穿过每个头像的脚下(平滑曲线),地面高度按最低的气泡收口,几个闲逛的小头像放在「低」气泡头顶的空地上 */
function walk() {
const map = row.firstElementChild;
if (!map || !map.offsetWidth) return;
const o = map.getBoundingClientRect(), W = map.scrollWidth;
const pts = $$('.gb-a', map).map(a => { const r = a.getBoundingClientRect(); return [r.left - o.left + r.width / 2, r.top - o.top + r.height / 2]; });
if (!pts.length) return;
const H = Math.max.apply(null, $$('.gb-col', map).map(c => c.getBoundingClientRect().bottom - o.top)) + 14;
map.style.height = row.style.height = H + 'px';
const P = [[0, pts[0][1]]].concat(pts, [[W, pts[pts.length - 1][1]]]);
let d = 'M' + P[0][0] + ' ' + P[0][1];
for (let i = 1; i < P.length; i++) { const mx = (P[i - 1][0] + P[i][0]) / 2; d += ' C' + mx + ' ' + P[i - 1][1] + ' ' + mx + ' ' + P[i][1] + ' ' + P[i][0] + ' ' + P[i][1]; }
const svg = $('.gb-path', map);
svg.setAttribute('width', W); svg.setAttribute('height', H); svg.firstElementChild.setAttribute('d', d);
$$('.gb-w', map).forEach(x => x.remove());
$$('.gb-col.lo', map).forEach((c, k) => {
if (k % 2) return;
const r = c.getBoundingClientRect(), img = document.createElement('img');
img.className = 'gb-w'; img.alt = ''; img.setAttribute('aria-hidden', 'true'); img.loading = 'lazy';
img.src = avaSrc(POOL[(k * 5 + 11) % POOL.length]);
img.style.left = (r.left - o.left + 18 + (k % 4 ? 50 : 0)) + 'px'; img.style.top = (k % 4 ? 22 : 12) + 'px';
map.appendChild(img);
});
}
window.addEventListener('resize', () => requestAnimationFrame(walk));
paint('');
const talk = T.reduce((a, t) => a + (t.talk || 0), 0);
$('#gchCnt').innerHTML = '<b>' + fmtNum(talk) + '</b> 位妈妈正在聊';
@@ -4242,7 +4275,7 @@
});
$('.gch-who', sec).before(b);
}).catch(() => {});
row.addEventListener('click', e => { const b = e.target.closest('.gch-card'); if (b) go(b.dataset.t); });
row.addEventListener('click', e => { const b = e.target.closest('.gb-b,.gb-all'); if (b) go(b.dataset.t); });
$('#gchMore').addEventListener('click', () => go(''));
$('#gchCat').addEventListener('click', e => {
const b = e.target.closest('button[data-cat]');