Files
mama-box/demo/occupy/index.html

212 lines
13 KiB
HTML
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.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
<title>宝宝今天占领首页 · 创意预览</title>
<!-- 2026-10-03 创意预览(不进正式首页):「宝宝今天占领首页」。
头图拆成几层(背景 / 倒计时胶囊 / 「30周+3天」/ 进度条,均为首页预览版截图抠出来的透明层),
卡通宝宝用代码画(SVG)。时间线:1s 后宝宝从右下角爬出来 → 把「30周+3天」顶歪 → 跳上去坐着、戴上小皇冠 →
冒一句「今天这里归我管。」→ 几秒后跳下来爬走,数字弹回原位。
整段由 frame(t) 一个纯函数按时间算出全部位置,?rec=1 时由 __set(t) 逐帧驱动(生成动图用)。 -->
<style>
html,body{margin:0;background:#F8F3EF;-webkit-tap-highlight-color:transparent;font-family:-apple-system,"PingFang SC","Noto Sans SC",sans-serif}
.page{position:relative;max-width:480px;margin:0 auto;overflow:hidden}
.hero-wrap{position:relative;width:100%;overflow:hidden}
.stage{position:absolute;left:0;top:0;width:375px;height:270px;transform-origin:0 0;overflow:hidden}
.stage>*{position:absolute}
.bg{left:0;top:0;width:375px;height:270px}
.pill{left:14px;top:52px;width:159.69px;height:42px}
.num{left:121.6px;top:63px;width:16px;height:20px;line-height:20px;font:900 13.5px/20px -apple-system,"PingFang SC","Noto Sans SC",sans-serif;
color:#FFF3C4;text-shadow:0 1px 3px rgba(120,30,10,.28);text-align:center}
.week{left:14px;top:86px;width:110.9px;height:70px;transform-origin:14px 60px;will-change:transform}
.prog{left:14px;top:150px;width:192px;height:36px}
.baby{filter:drop-shadow(0 0 .8px #fff) drop-shadow(0 0 .8px #fff) drop-shadow(0 0 .6px #fff) drop-shadow(0 3px 3px rgba(120,40,20,.3));will-change:transform;opacity:0}
#crawl{width:104px;height:80px;left:0;top:0}
#sit{width:74px;height:81px;left:0;top:0;transform-origin:50% 80%}
.bubble{left:104px;top:30px;padding:7px 12px 8px;border-radius:14px 14px 14px 4px;background:#fff;white-space:nowrap;
font-size:13px;font-weight:800;color:#D9583A;letter-spacing:.3px;box-shadow:0 6px 16px rgba(150,60,30,.22);
transform-origin:0 100%;opacity:0}
.bubble::after{content:"";position:absolute;left:-5px;bottom:4px;border:6px solid transparent;border-right-color:#fff;border-left:0}
.below{display:block;width:100%}
.replay{position:absolute;left:50%;transform:translateX(-50%);z-index:5;border:0;padding:7px 16px;border-radius:999px;
font:600 12.5px -apple-system,"PingFang SC",sans-serif;color:#C2563B;background:rgba(255,255,255,.85);
-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 6px 16px rgba(180,80,50,.16),inset 0 0 0 1px #fff;
opacity:0;pointer-events:none;transition:opacity .3s}
.replay.on{opacity:1;pointer-events:auto}
</style>
</head>
<body>
<div class="page">
<div class="hero-wrap" id="wrap">
<div class="stage" id="stage">
<img class="bg" src="../_hero/bg.jpg" alt="">
<img class="pill" src="../_hero/pill.png" alt="">
<div class="num">67</div>
<img class="prog" src="../_hero/prog.png" alt="">
<img class="week" id="week" src="../_hero/week.png" alt="">
<!-- 爬行宝宝(侧面,头朝左)。四肢用粗圆头线段画,好绕肩/髋转动 -->
<svg class="baby" id="crawl" viewBox="0 0 130 100" aria-hidden="true">
<g stroke-linecap="round" stroke-linejoin="round" fill="none">
<g id="cLegB"><path d="M90 66 L97 88 L113 89" stroke="#EDB293" stroke-width="14"/></g>
<g id="cArmB"><path d="M47 60 L43 87" stroke="#EDB293" stroke-width="12"/></g>
</g>
<ellipse cx="72" cy="58" rx="29" ry="20" fill="#FFD9C4"/>
<path d="M58 44 q14 -6 28 0" stroke="#F7C4A9" stroke-width="3" fill="none" stroke-linecap="round"/>
<ellipse cx="95" cy="59" rx="15" ry="18" fill="#fff"/>
<path d="M85 44 q-6 15 0 30" stroke="#F3D7CB" stroke-width="2.4" fill="none" stroke-linecap="round"/>
<circle cx="99" cy="52" r="2.2" fill="#FFC2B4"/><circle cx="104" cy="62" r="2.2" fill="#FFC2B4"/><circle cx="96" cy="67" r="2.2" fill="#FFC2B4"/>
<g stroke-linecap="round" stroke-linejoin="round" fill="none">
<g id="cLegF"><path d="M86 68 L91 91 L108 93" stroke="#FFD2BA" stroke-width="15"/></g>
<g id="cArmF"><path d="M54 62 L51 90" stroke="#FFD2BA" stroke-width="13"/></g>
</g>
<g id="cHead">
<circle cx="34" cy="40" r="25" fill="#FFDCC8"/>
<circle cx="50" cy="42" r="5.5" fill="#FFCDB4"/>
<path d="M28 16 q4 -9 12 -5 q-7 1 -6 7" fill="#8E5A44"/>
<path d="M37 15.5 q6 -5 11 1" stroke="#8E5A44" stroke-width="2.4" fill="none" stroke-linecap="round"/>
<circle cx="21" cy="38" r="3.4" fill="#4A2A20"/><circle cx="22.2" cy="36.8" r="1.2" fill="#fff"/>
<ellipse cx="25" cy="48" rx="5.5" ry="3.8" fill="#FF9C93" opacity=".55"/>
<path d="M11 47 q4 4 8 1" stroke="#B45A42" stroke-width="2.2" fill="none" stroke-linecap="round"/>
</g>
</svg>
<!-- 坐着的宝宝(正面),一手叉腰一手举拳,头上小皇冠 -->
<svg class="baby" id="sit" viewBox="0 0 110 120" aria-hidden="true">
<g stroke-linecap="round" stroke-linejoin="round" fill="none">
<g id="sLegL"><path d="M43 90 L38 110" stroke="#FFD2BA" stroke-width="15"/></g>
<g id="sLegR"><path d="M67 90 L72 110" stroke="#FFD2BA" stroke-width="15"/></g>
</g>
<ellipse cx="37" cy="113" rx="9" ry="5.5" fill="#FFCDB4"/><ellipse cx="73" cy="113" rx="9" ry="5.5" fill="#FFCDB4"/>
<ellipse cx="55" cy="80" rx="24" ry="21" fill="#FFD9C4"/>
<path d="M31 84 q24 22 48 0 q2 14 -24 16 q-26 -2 -24 -16z" fill="#fff"/>
<path d="M32 84 q23 12 46 0" stroke="#F3D7CB" stroke-width="2.4" fill="none" stroke-linecap="round"/>
<g stroke-linecap="round" stroke-linejoin="round" fill="none">
<path d="M35 70 L22 79 L33 89" stroke="#FFD2BA" stroke-width="11"/>
<g id="sArmR"><path d="M75 70 L88 58 L90 44" stroke="#FFD2BA" stroke-width="11"/></g>
</g>
<g id="sFist"><circle cx="90" cy="41" r="7" fill="#FFCDB4"/></g>
<circle cx="55" cy="40" r="27" fill="#FFDCC8"/>
<circle cx="29" cy="42" r="5" fill="#FFCDB4"/><circle cx="81" cy="42" r="5" fill="#FFCDB4"/>
<path d="M50 14 q4 -9 12 -5 q-7 1 -6 7" fill="#8E5A44"/>
<path d="M44 41 q2.5 -4 5 0" stroke="#4A2A20" stroke-width="2.6" fill="none" stroke-linecap="round"/>
<path d="M61 41 q2.5 -4 5 0" stroke="#4A2A20" stroke-width="2.6" fill="none" stroke-linecap="round"/>
<path d="M42 33 l7 2 M68 33 l-7 2" stroke="#8E5A44" stroke-width="2" stroke-linecap="round"/>
<ellipse cx="40" cy="49" rx="5.5" ry="3.6" fill="#FF9C93" opacity=".55"/><ellipse cx="70" cy="49" rx="5.5" ry="3.6" fill="#FF9C93" opacity=".55"/>
<path d="M49 50 q6 7 12 0 z" fill="#C2563B" stroke="#C2563B" stroke-width="1.6" stroke-linejoin="round"/>
<g id="crown" transform="rotate(-10 55 12)">
<path d="M42 18 L40 4 L48 11 L55 1 L62 11 L70 4 L68 18 Z" fill="#FFD25A" stroke="#F0A93A" stroke-width="1.6" stroke-linejoin="round"/>
<circle cx="55" cy="13" r="2" fill="#F06A45"/>
</g>
</svg>
<div class="bubble" id="bubble">今天这里归我管。</div>
</div>
<button class="replay" id="replay">再看一次</button>
</div>
<img class="below" src="../_hero/below.jpg" alt="">
</div>
<script>
(function () {
const REC = /[?&]rec=1/.test(location.search);
const $ = id => document.getElementById(id);
const wrap = $('wrap'), stage = $('stage'), week = $('week'), crawl = $('crawl'), sit = $('sit'), bubble = $('bubble'), replay = $('replay');
const crown = $('crown');
function fit() {
const w = Math.min(wrap.parentNode.clientWidth, 480), s = w / 375;
stage.style.transform = 'scale(' + s + ')'; wrap.style.height = 250 * s + 'px';
replay.style.top = 214 * s + 'px';
}
fit(); addEventListener('resize', fit);
const cl = (x, a = 0, b = 1) => Math.max(a, Math.min(b, x));
const seg = (t, a, b) => cl((t - a) / (b - a));
const lerp = (a, b, u) => a + (b - a) * u;
const eio = u => u < .5 ? 2 * u * u : 1 - Math.pow(-2 * u + 2, 2) / 2;
const eo = u => 1 - Math.pow(1 - u, 3);
const spring = u => u <= 0 ? 0 : 1 - Math.exp(-7 * u) * Math.cos(13 * u); // 带回弹的 0→1
const rot = (id, a, cx, cy) => $(id).setAttribute('transform', 'rotate(' + a.toFixed(2) + ' ' + cx + ' ' + cy + ')');
const pop = (u) => u <= 0 ? 0 : 1 + Math.exp(-6 * u) * Math.sin(14 * u) * .35 - Math.exp(-14 * u) * 1;
// 关键位置(逻辑像素,375 宽头图坐标)
const C0 = [384, 152], C1 = [114, 84]; // 爬行起点(右下角外)→ 顶到「30周」右侧
const SIT = [26, 33]; // 坐姿宝宝左上角(坐在「30」顶上)
const T = 9.6; // 一轮总时长
function frame(t) {
// ---- 爬进来 1.0 → 2.7 ----
let cx = C0[0], cy = C0[1], cOp = 0, flip = 1, gait = 0;
const uIn = seg(t, 1.0, 2.7);
if (t >= 1.0 && t < 3.05) {
const u = eo(uIn);
cx = lerp(C0[0], C1[0], u); cy = lerp(C0[1], C1[1], u);
cOp = 1; gait = t * 9;
if (t > 2.7) { const p = seg(t, 2.7, 2.95); cx = C1[0] - 9 * Math.sin(p * Math.PI * .5); gait = 2.7 * 9 + p * 3; } // 用头顶一下
}
// ---- 被顶歪:2.75 起数字块向左倒 -9°,带回弹 ----
let wa = 0, wx = 0;
if (t >= 2.75) {
const s = spring(seg(t, 2.75, 3.6) * 1.15);
wa = -9 * s; wx = -5 * s;
}
// ---- 跳上去 3.05 → 3.45(爬姿换坐姿,抛物线)----
let sx = SIT[0], sy = SIT[1], sOp = 0, sRot = -6, sSq = 1;
if (t >= 3.05 && t < 7.0) {
const u = seg(t, 3.05, 3.45);
const fx = C1[0] - 9 - 4, fy = C1[1] - 14;
sx = lerp(fx, SIT[0], eio(u)); sy = lerp(fy, SIT[1], eio(u)) - Math.sin(u * Math.PI) * 34;
sOp = 1;
if (u >= 1) { const q = seg(t, 3.45, 3.85); sSq = 1 - Math.exp(-8 * q) * Math.sin(q * 18) * .12; } // 落座压扁回弹
}
// ---- 坐着:晃腿、举拳 ----
const sit_t = t - 3.45;
rot('sLegL', Math.sin(t * 7) * 14, 43, 90); rot('sLegR', Math.sin(t * 7 + 2.2) * 14, 67, 90);
const fist = sit_t > 0 ? Math.sin(sit_t * 9) * 8 * Math.exp(-Math.max(0, sit_t - 1.4)) : 0;
rot('sArmR', fist, 75, 70); rot('sFist', fist, 75, 70);
const cr = seg(t, 3.55, 3.85);
crown.setAttribute('transform', 'translate(0 ' + ((1 - eo(cr)) * -16).toFixed(1) + ') rotate(-10 55 12)');
crown.style.opacity = cr;
// ---- 气泡 3.75 → 6.6 ----
const bIn = seg(t, 3.75, 4.25), bOut = seg(t, 6.45, 6.7);
bubble.style.opacity = Math.min(cl(bIn * 3), 1 - bOut);
bubble.style.transform = 'scale(' + (bIn > 0 ? Math.max(0, pop(bIn * 1.5)) * (1 - bOut * .2) : 0).toFixed(3) + ')';
// ---- 跳下来 6.75 → 7.15,再爬走 7.15 → 8.5,数字块弹回 ----
if (t >= 6.75 && t < 7.15) {
const u = seg(t, 6.75, 7.15);
sx = lerp(SIT[0], 132, eio(u)); sy = lerp(SIT[1], 112, eio(u)) - Math.sin(u * Math.PI) * 30;
sOp = 1; sRot = lerp(-6, 8, u);
}
if (t >= 7.15 && t < 8.6) {
const u = seg(t, 7.15, 8.5);
cx = lerp(128, 396, eio(u)); cy = lerp(118, 158, eio(u)); cOp = 1; flip = -1; gait = t * 10; sOp = 0;
}
if (t >= 7.0 && t < 7.15) sOp = 1;
if (t >= 6.8) {
const s = spring(seg(t, 6.8, 7.8) * 1.15);
wa = -9 * (1 - s); wx = -5 * (1 - s);
}
if (t < 3.05 || t >= 7.15) sOp = 0;
if (t >= 3.05 && t < 7.15) cOp = 0;
week.style.transform = 'translateX(' + wx.toFixed(2) + 'px) rotate(' + wa.toFixed(2) + 'deg)';
crawl.style.opacity = cOp;
const bob = Math.abs(Math.sin(gait)) * -2.4;
crawl.style.transform = 'translate(' + cx.toFixed(1) + 'px,' + (cy + bob).toFixed(1) + 'px) scaleX(' + flip + ')';
rot('cArmF', Math.sin(gait) * 20, 54, 62); rot('cArmB', -Math.sin(gait) * 20, 47, 60);
rot('cLegF', -Math.sin(gait) * 16, 86, 68); rot('cLegB', Math.sin(gait) * 16, 90, 66);
rot('cHead', Math.sin(gait * 2) * 2.5, 40, 55);
sit.style.opacity = sOp;
sit.style.transform = 'translate(' + sx.toFixed(1) + 'px,' + sy.toFixed(1) + 'px) rotate(' + sRot + 'deg) scale(' + (1 / sSq).toFixed(3) + ',' + sSq.toFixed(3) + ')';
replay.classList.toggle('on', !REC && t > T - .4);
}
if (REC) { window.__set = frame; window.__T = T; frame(0); window.__ready = true; return; }
let start = performance.now() - 0;
(function loop(now) { const t = (now - start) / 1000; frame(Math.min(t, T)); requestAnimationFrame(loop); })(start);
replay.addEventListener('click', () => { start = performance.now() - 300; });
})();
</script>
</body>
</html>