212 lines
13 KiB
HTML
212 lines
13 KiB
HTML
<!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>
|