150 lines
8.0 KiB
JavaScript
150 lines
8.0 KiB
JavaScript
/* 首页「孕妈广场」效果稿 V2 · 广场人群:一块俯视的广场空地,十几位妈妈近大远小站着,3 位头上冒话题气泡轮换;
|
||
下方分类胶囊切换时人群换一批;右下角一个虚线「空位」是投稿入口。只在 ?gcv=2 时加载。 */
|
||
(function () {
|
||
var sec = document.getElementById('gcHome');
|
||
var ALL = window.GC_TOPICS;
|
||
if (!sec || !ALL || !ALL.length) return;
|
||
|
||
var esc = function (s) { return String(s).replace(/[&<>"']/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; }); };
|
||
var fmt = function (n) { return n >= 1000 ? (n / 1000).toFixed(1).replace(/\.0$/, '') + 'k' : String(n); };
|
||
var go = function (id) { location.href = (id ? 'guangchang/?t=' + id : 'guangchang/') + location.hash; };
|
||
var CATS = [['', '全部'], ['family', '家庭婚姻'], ['work', '职场'], ['baby', '经验']];
|
||
var CATN = { family: '家庭婚姻', work: '职场', baby: '经验' };
|
||
var MOMS = [];
|
||
for (var i = 1; i <= 22; i++) MOMS.push('m' + (i < 10 ? '0' : '') + i);
|
||
for (i = 1; i <= 14; i++) MOMS.push('p' + (i < 10 ? '0' : '') + i);
|
||
var reduce = window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||
|
||
/* 站位(x 为宽度百分比,y 为头像中心距空地顶 px,s 为直径):上远小、下近大;z 为气泡区 A 左上 / B 右上 / C 前排 */
|
||
var SLOTS = [
|
||
{ x: 9, y: 22, s: 24 }, { x: 33, y: 16, s: 22 }, { x: 54, y: 30, s: 26 }, { x: 76, y: 15, s: 22 },
|
||
{ x: 20, y: 74, s: 28 }, { x: 78, y: 82, s: 28 },
|
||
{ x: 11, y: 148, s: 40, z: 'A' }, { x: 36, y: 154, s: 40, z: 'A' }, { x: 63, y: 140, s: 40, z: 'B' }, { x: 88, y: 152, s: 40, z: 'B' },
|
||
{ x: 58, y: 222, s: 40 }, { x: 87, y: 212, s: 34 },
|
||
{ x: 12, y: 278, s: 50, z: 'C' }, { x: 35, y: 264, s: 48, z: 'C' }
|
||
];
|
||
var BW = 158, PAD = 5; // 气泡宽(按空地宽度再收窄)、离空地边距
|
||
|
||
var hash = function (s) { var x = 7; for (var k = 0; k < s.length; k++) x = (x * 31 + s.charCodeAt(k)) | 0; return Math.abs(x); };
|
||
var shuffle = function (arr, seed) { var a = arr.slice(), r = seed || 1; for (var k = a.length - 1; k > 0; k--) { r = (r * 9301 + 49297) % 233280; var j = Math.floor(r / 233280 * (k + 1)); var t = a[k]; a[k] = a[j]; a[j] = t; } return a; };
|
||
|
||
var cat = '', pool = [], qi = 0, faces = [], topicOf = [], speak = { A: -1, B: -1, C: -1 }, timer = 0, turn = 0, batch = 0;
|
||
|
||
sec.classList.add('gv2');
|
||
sec.classList.remove('hide');
|
||
sec.innerHTML =
|
||
'<div class="g2-head">' +
|
||
'<h2 class="g2-title"><span>孕妈广场</span></h2>' +
|
||
'<button type="button" class="g2-more" data-go="">去逛逛<i aria-hidden="true">›</i></button>' +
|
||
'</div>' +
|
||
'<div class="g2-sub"><i class="g2-live" aria-hidden="true"></i><b id="g2Cnt"></b> 位妈妈正在广场上聊<span class="g2-mark">效果稿 V2</span></div>' +
|
||
'<div class="g2-ground" id="g2Ground">' +
|
||
'<div class="g2-floor" aria-hidden="true"></div>' +
|
||
SLOTS.map(function (p, k) {
|
||
return '<button type="button" class="g2-mom' + (p.z ? ' near' : '') + '" data-k="' + k + '" style="left:' + p.x + '%;top:' + p.y + 'px;--s:' + p.s + 'px;--d:' + (2.6 + (k % 5) * 0.45).toFixed(2) + 's;--dl:-' + (k * 0.37 % 2.4).toFixed(2) + 's">' +
|
||
'<i class="g2-shadow" aria-hidden="true"></i><span class="g2-face"><img alt="" loading="lazy" decoding="async"></span></button>';
|
||
}).join('') +
|
||
'<div class="g2-bub" data-z="A"></div><div class="g2-bub" data-z="B"></div><div class="g2-bub" data-z="C"></div>' +
|
||
'<button type="button" class="g2-seat" data-go=""><span class="g2-seat-o">你</span><span class="g2-seat-t"><b>坐下来说说</b><em>¥30+500兜豆</em></span></button>' +
|
||
'</div>' +
|
||
'<div class="g2-foot">' +
|
||
'<div class="g2-cats" role="tablist" aria-label="话题分类">' +
|
||
CATS.map(function (c) { return '<button type="button" role="tab" data-cat="' + c[0] + '"' + (c[0] ? ' style="--ck:' + ({ family: '#E0566F', work: '#3A72B8', baby: '#2E8A7C' })[c[0]] + '"' : '') + '>' + c[1] + '</button>'; }).join('') +
|
||
'</div>' +
|
||
'</div>';
|
||
|
||
var ground = sec.querySelector('#g2Ground');
|
||
var moms = [].slice.call(sec.querySelectorAll('.g2-mom'));
|
||
var bubs = {}; [].forEach.call(sec.querySelectorAll('.g2-bub'), function (b) { bubs[b.getAttribute('data-z')] = b; });
|
||
|
||
function nextTopic() { var t = pool[qi % pool.length]; qi++; return t; }
|
||
|
||
function bubbleHTML(t) {
|
||
return '<span class="g2-bub-cat" style="color:' + t.ink + '">' + CATN[t.cat] + ' · ' + fmt(t.talk) + ' 在聊</span>' +
|
||
'<span class="g2-bub-t">' + esc(t.name) + '</span><i class="g2-tail" aria-hidden="true"></i>';
|
||
}
|
||
|
||
function place(z, k) {
|
||
var b = bubs[z], p = SLOTS[k], W = ground.clientWidth || 340, cx = W * p.x / 100;
|
||
BW = Math.min(158, Math.floor((W - PAD * 3) / 2));
|
||
b.style.width = BW + 'px';
|
||
var left = z === 'A' ? PAD : z === 'B' ? W - PAD - BW : Math.max(PAD, Math.min(W - PAD - BW, cx - BW / 2));
|
||
var t = topicOf[k];
|
||
b.innerHTML = bubbleHTML(t);
|
||
b.style.left = left + 'px';
|
||
b.style.bottom = (ground.clientHeight - (p.y - p.s / 2) + 7) + 'px';
|
||
b.style.setProperty('--tx', Math.max(14, Math.min(BW - 14, cx - left)) + 'px');
|
||
b.style.setProperty('--bc', t.c[1]);
|
||
b.style.setProperty('--bg', t.c[0]);
|
||
b.setAttribute('data-k', k);
|
||
b.setAttribute('role', 'button');
|
||
b.setAttribute('aria-label', t.name);
|
||
moms.forEach(function (m) { if (+m.getAttribute('data-k') === k) { m.classList.add('talk'); m.style.setProperty('--ring', t.ink); } });
|
||
}
|
||
|
||
function candidates(z) { var r = []; SLOTS.forEach(function (p, k) { if (p.z === z) r.push(k); }); return r; }
|
||
|
||
function rotate(z, first) {
|
||
var b = bubs[z], old = speak[z], c = candidates(z), k;
|
||
do { k = c[Math.floor(Math.random() * c.length)]; } while (c.length > 1 && k === old);
|
||
if (first) k = c[(batch + (z === 'B' ? 1 : 0)) % c.length];
|
||
var show = function () {
|
||
if (old >= 0) moms[old].classList.remove('talk');
|
||
topicOf[k] = nextTopic();
|
||
speak[z] = k;
|
||
place(z, k);
|
||
b.classList.add('on');
|
||
};
|
||
if (first || reduce) { show(); return; }
|
||
b.classList.remove('on');
|
||
setTimeout(show, 420);
|
||
}
|
||
|
||
function setCat(c, first) {
|
||
cat = c;
|
||
batch++;
|
||
pool = shuffle(ALL.filter(function (t) { return !c || t.cat === c; }), hash(c || 'all') + batch);
|
||
qi = 0;
|
||
var picks = shuffle(MOMS, hash('m' + c) + batch * 7);
|
||
[].forEach.call(sec.querySelectorAll('.g2-cats button'), function (b) { var on = b.getAttribute('data-cat') === c; b.classList.toggle('on', on); b.setAttribute('aria-selected', on ? 'true' : 'false'); });
|
||
sec.querySelector('#g2Cnt').textContent = pool.reduce(function (a, t) { return a + t.talk; }, 0).toLocaleString('en-US');
|
||
var apply = function () {
|
||
moms.forEach(function (m, k) {
|
||
faces[k] = picks[k % picks.length];
|
||
topicOf[k] = nextTopic();
|
||
m.querySelector('img').src = 'assets/img/mom/' + faces[k] + '.jpg';
|
||
m.classList.remove('talk');
|
||
m.setAttribute('aria-label', topicOf[k].name);
|
||
});
|
||
speak = { A: -1, B: -1, C: -1 };
|
||
['A', 'B', 'C'].forEach(function (z) { rotate(z, true); });
|
||
ground.classList.remove('swap');
|
||
};
|
||
if (first || reduce) { apply(); return; }
|
||
ground.classList.add('swap');
|
||
['A', 'B', 'C'].forEach(function (z) { bubs[z].classList.remove('on'); });
|
||
setTimeout(apply, 380);
|
||
}
|
||
|
||
function loop() {
|
||
clearInterval(timer);
|
||
if (reduce) return;
|
||
timer = setInterval(function () {
|
||
if (document.hidden) return;
|
||
rotate(['A', 'C', 'B'][turn++ % 3]);
|
||
}, 3200);
|
||
}
|
||
|
||
sec.addEventListener('click', function (e) {
|
||
var el = e.target.closest('[data-cat],[data-go],.g2-mom,.g2-bub');
|
||
if (!el || !sec.contains(el)) return;
|
||
if (el.hasAttribute('data-cat')) { var c = el.getAttribute('data-cat'); if (c !== cat) { setCat(c); loop(); } return; }
|
||
if (el.hasAttribute('data-go')) { go(''); return; }
|
||
var k = +el.getAttribute('data-k');
|
||
if (topicOf[k]) go(topicOf[k].id);
|
||
});
|
||
|
||
setCat('', true);
|
||
loop();
|
||
})();
|