创意预览页:宝宝占领首页(demo/occupy)、宝宝把倒计时改了(demo/countdown),头图分层素材 demo/_hero

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 15:30:11 +00:00
parent b017cccfae
commit c692d25576
7 changed files with 353 additions and 0 deletions

BIN
demo/_hero/below.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

BIN
demo/_hero/bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
demo/_hero/pill.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
demo/_hero/prog.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
demo/_hero/week.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

142
demo/countdown/index.html Normal file
View File

@@ -0,0 +1,142 @@
<!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 创意预览(不进正式首页):「宝宝把倒计时改了」—— 设计为每天一次的小彩蛋。
头图拆层同 demo/occupy(胶囊里的「67」单独用 HTML 渲染,才能做翻牌)。
时间线:1s 后一只小胖手从羊水囊那边伸过来 → 手指按住「67」往下拨,个位 7 滚成 6(66)→
手停住、冒个问号(发现不对)→ 偷偷往上拨回 67 → 手飞快缩回去 → 胶囊旁冒一句「我有点着急。」带一滴汗。
正式上线时按北京时间「每天首次打开首页」播一次(localStorage 记日期);本预览页每次打开都播,并给「再看一次」。
?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}
.week{left:14px;top:86px;width:110.9px;height:70px}
.prog{left:14px;top:150px;width:192px;height:36px}
/* 「67」:十位固定,个位是一条竖排数字带,translateY 滚动 */
.num{left:120.6px;top:63px;height:20px;transform-origin:50% 60%;display:flex;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)}
.num .win{display:inline-block;height:20px;overflow:hidden;position:relative;width:.62em}
.num .reel{display:flex;flex-direction:column;will-change:transform}
.num .reel i{font-style:normal;height:20px;text-align:center}
.num.hl{color:#fff}
.hand{left:0;top:0;width:150px;height:46px;transform-origin:9px 14px;will-change:transform;
filter:drop-shadow(0 0 .8px #fff) drop-shadow(0 0 .6px #fff) drop-shadow(0 3px 3px rgba(120,40,20,.28))}
.q{left:0;top:0;font:900 16px/1 -apple-system,"PingFang SC",sans-serif;color:#fff;text-shadow:0 1px 3px rgba(120,30,10,.35);opacity:0}
.bubble{left:152px;top:90px;padding:7px 12px 8px 12px;border-radius:4px 14px 14px 14px;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 0;opacity:0;display:flex;align-items:center;gap:5px}
.bubble::after{content:"";position:absolute;left:8px;top:-5px;border:6px solid transparent;border-bottom-color:#fff;border-top:0}
.bubble svg{width:12px;height:14px}
.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" id="num"><span>6</span><span class="win"><span class="reel" id="reel"><i>8</i><i>7</i><i>6</i></span></span></div>
<img class="week" src="../_hero/week.png" alt="">
<img class="prog" src="../_hero/prog.png" alt="">
<!-- 小胖手(手指朝左):前臂从右边伸进来,食指伸直,其余手指蜷着 -->
<svg class="hand" id="hand" viewBox="0 0 150 46" aria-hidden="true">
<path d="M150 14 L58 17 Q50 18 50 23 Q50 28 58 29 L150 33 Z" fill="#FFD6BF"/>
<path d="M86 18.5 q2 4.5 0 9 M96 18.2 q2 4.8 0 9.6" stroke="#F4BCA0" stroke-width="1.8" fill="none" stroke-linecap="round"/>
<ellipse cx="44" cy="25" rx="16" ry="13" fill="#FFDCC8"/>
<ellipse cx="38" cy="34" rx="7" ry="5" fill="#FFD2BA"/>
<ellipse cx="47" cy="36" rx="6.5" ry="4.6" fill="#FFD2BA"/>
<path d="M32 31 q-3 3 1 6" stroke="#F4BCA0" stroke-width="1.6" fill="none" stroke-linecap="round"/>
<path d="M33 19 L10 15" stroke="#FFDCC8" stroke-width="9" stroke-linecap="round"/>
<ellipse cx="9.5" cy="14.5" rx="2.6" ry="2.2" fill="#FFF1E8"/>
<path d="M44 13 q6 -4 10 2" stroke="#FFDCC8" stroke-width="7" fill="none" stroke-linecap="round"/>
</svg>
<div class="q" id="q">?</div>
<div class="bubble" id="bubble">
<span>我有点着急。</span>
<svg viewBox="0 0 12 14" aria-hidden="true"><path d="M6 1 C6 1 1.5 7 1.5 9.3 A4.5 4.5 0 0 0 10.5 9.3 C10.5 7 6 1 6 1z" fill="#8FD0F2"/><ellipse cx="4.6" cy="9.4" rx="1.1" ry="1.6" fill="#fff" opacity=".8"/></svg>
</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'), hand = $('hand'), reel = $('reel'), num = $('num'), q = $('q'), bubble = $('bubble'), replay = $('replay');
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 pop = u => u <= 0 ? 0 : 1 + Math.exp(-6 * u) * Math.sin(14 * u) * .35 - Math.exp(-14 * u);
const T = 8.4;
// 指尖坐标(头图坐标系)。手的 SVG 指尖在 (9,14)
const TIP_HOME = [236, 74], TIP_UP = [135, 61], TIP_DN = [135, 71];
function frame(t) {
let tip = TIP_HOME.slice(), ang = 0, digit = 1; // reel 位置:0=8,1=7,2=6
// 1.0–1.25 从羊水囊边探出一点,停一下;1.25–1.9 伸到「67」上方
if (t < 1.0) tip = [400, 74];
else if (t < 1.25) tip = [lerp(400, 250, eo(seg(t, 1.0, 1.25))), 74];
else if (t < 1.4) tip = [250, 74];
else if (t < 1.95) { const u = eio(seg(t, 1.4, 1.95)); tip = [lerp(250, TIP_UP[0], u), lerp(74, TIP_UP[1], u) - Math.sin(u * Math.PI) * 6]; }
// 1.95–2.35 往下拨:7 → 6
else if (t < 2.35) { const u = eio(seg(t, 1.95, 2.35)); tip = [TIP_UP[0], lerp(TIP_UP[1], TIP_DN[1], u)]; digit = 1 + u; ang = -8 * u; }
// 2.35–3.3 停住,看一眼……发现不对(问号、手抖两下)
else if (t < 3.3) { tip = TIP_DN.slice(); digit = 2; ang = -8; const w = seg(t, 2.8, 3.25); tip[0] += Math.sin(w * Math.PI * 6) * 1.6 * (1 - w); }
// 3.3–3.55 手指绕到下面,3.55–3.85 偷偷往上拨回 7
else if (t < 3.55) { const u = eio(seg(t, 3.3, 3.55)); tip = [lerp(TIP_DN[0], TIP_DN[0] + 2, u), lerp(TIP_DN[1], TIP_DN[1] + 3, u)]; digit = 2; ang = lerp(-8, 6, u); }
else if (t < 3.85) { const u = eio(seg(t, 3.55, 3.85)); tip = [TIP_DN[0] + 2, lerp(TIP_DN[1] + 3, TIP_UP[1], u)]; digit = 2 - u; ang = lerp(6, 10, u); }
// 3.85–4.25 飞快缩回去
else if (t < 4.3) { const u = seg(t, 3.9, 4.25); tip = [lerp(TIP_UP[0], 420, u * u), lerp(TIP_UP[1], 70, u)]; ang = lerp(10, 0, u); }
else tip = [420, 70];
hand.style.transform = 'translate(' + (tip[0] - 9).toFixed(1) + 'px,' + (tip[1] - 14).toFixed(1) + 'px) rotate(' + ang.toFixed(1) + 'deg)';
reel.style.transform = 'translateY(' + (-digit * 20).toFixed(2) + 'px)';
num.classList.toggle('hl', t > 1.95 && t < 3.85);
const hk = seg(t, 1.85, 2.05) * (1 - seg(t, 3.9, 4.15)); // 被拨的时候数字微微放大,好让人看见
num.style.transform = 'scale(' + (1 + .22 * hk).toFixed(3) + ')';
// 问号
const qi = seg(t, 2.55, 2.75), qo = seg(t, 3.25, 3.4);
q.style.opacity = Math.min(qi, 1 - qo);
q.style.transform = 'translate(' + (TIP_DN[0] + 16) + 'px,' + (38 - qi * 4) + 'px) scale(' + (.6 + .4 * pop(qi)).toFixed(3) + ')';
// 气泡 4.35 → 7.4
const bIn = seg(t, 4.35, 4.85), bOut = seg(t, 7.2, 7.45);
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) + ')';
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();
(function loop(now) { frame(Math.min((now - start) / 1000, T)); requestAnimationFrame(loop); })(start);
replay.addEventListener('click', () => { start = performance.now() - 300; });
})();
</script>
</body>
</html>

211
demo/occupy/index.html Normal file
View File

@@ -0,0 +1,211 @@
<!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>