• 隔壁老陈
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
    <title>鹈鹕骑车 · 黄昏栈桥</title>
    <style>
      :root{
        --sky-top:#2b2152;
        --sea-deep:#2b2c60;
        box-sizing:border-box;
        padding-top:env(safe-area-inset-top,0px);
        padding-bottom:env(safe-area-inset-bottom,0px);
        background:var(--sky-top);
      }
      html,body{height:100%;margin:0}
      html{scroll-padding-top:env(safe-area-inset-top,0px)}
      body{overflow:hidden;background:linear-gradient(var(--sky-top) 0 50%, var(--sea-deep) 50% 100%)}
      #scene{display:block;width:100%;height:100%}
      body::after{content:"";position:fixed;inset:0;pointer-events:none;
        background:radial-gradient(ellipse at 50% 50%, rgba(0,0,0,0) 55%, rgba(22,12,42,.5) 100%)}
      .tw{animation:tw 3s ease-in-out infinite}
      @keyframes tw{0%,100%{opacity:.15}50%{opacity:.9}}
    </style>
    </head>
    <body>
    <svg id="scene" viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="黄昏的海边栈桥上,一只鹈鹕骑着自行车">
    <defs>
      <linearGradient id="gSky" x1="0" y1="0" x2="0" y2="1">
        <stop offset="0" stop-color="#2b2152"/>
        <stop offset=".32" stop-color="#5e3b78"/>
        <stop offset=".6" stop-color="#c9607e"/>
        <stop offset=".82" stop-color="#f7955f"/>
        <stop offset="1" stop-color="#ffd48e"/>
      </linearGradient>
      <radialGradient id="gGlow">
        <stop offset="0" stop-color="#fff2c0" stop-opacity=".95"/>
        <stop offset=".22" stop-color="#ffd07a" stop-opacity=".6"/>
        <stop offset=".55" stop-color="#ff9a6a" stop-opacity=".18"/>
        <stop offset="1" stop-color="#ff8a6a" stop-opacity="0"/>
      </radialGradient>
      <linearGradient id="gSun" x1="0" y1="0" x2="0" y2="1">
        <stop offset="0" stop-color="#fff6d2"/>
        <stop offset="1" stop-color="#ffb457"/>
      </linearGradient>
      <linearGradient id="gSea" x1="0" y1="0" x2="0" y2="1">
        <stop offset="0" stop-color="#f7a47c"/>
        <stop offset=".15" stop-color="#dc7680"/>
        <stop offset=".5" stop-color="#8b5588"/>
        <stop offset="1" stop-color="#34366e"/>
      </linearGradient>
      <radialGradient id="gLamp">
        <stop offset="0" stop-color="#ffe9a8" stop-opacity=".85"/>
        <stop offset=".4" stop-color="#ffc977" stop-opacity=".25"/>
        <stop offset="1" stop-color="#ffb060" stop-opacity="0"/>
      </radialGradient>
      <linearGradient id="gFeather" x1="1" y1="0" x2="0" y2="1">
        <stop offset="0" stop-color="#fffaf0"/>
        <stop offset=".55" stop-color="#f4eadd"/>
        <stop offset="1" stop-color="#d6c3c2"/>
      </linearGradient>
      <linearGradient id="gBeak" x1="0" y1="0" x2="1" y2="0">
        <stop offset="0" stop-color="#ffd65e"/>
        <stop offset="1" stop-color="#f59e35"/>
      </linearGradient>
      <linearGradient id="gPouch" x1="0" y1="0" x2="0" y2="1">
        <stop offset="0" stop-color="#f7954a"/>
        <stop offset="1" stop-color="#dd6238"/>
      </linearGradient>
      <linearGradient id="gDeck" x1="0" y1="0" x2="0" y2="1">
        <stop offset="0" stop-color="#e4ad72"/>
        <stop offset="1" stop-color="#b87a47"/>
      </linearGradient>
    </defs>
    
    <!-- 天空 -->
    <rect x="-1400" y="-1600" width="4000" height="1601" fill="#2b2152"/>
    <rect x="-1400" y="0" width="4000" height="422" fill="url(#gSky)"/>
    <g id="stars"></g>
    <circle cx="840" cy="400" r="320" fill="url(#gGlow)"/>
    <circle cx="840" cy="402" r="78" fill="url(#gSun)"/>
    <g id="clouds"></g>
    <g id="hillsFar" opacity=".9"></g>
    <g id="hillsNear"></g>
    
    <!-- 海 -->
    <rect x="-1400" y="420" width="4000" height="281" fill="url(#gSea)"/>
    <rect x="-1400" y="700" width="4000" height="1600" fill="#34366e"/>
    <ellipse cx="840" cy="424" rx="300" ry="42" fill="url(#gGlow)" opacity=".75"/>
    <rect x="-1400" y="419.4" width="4000" height="1.4" fill="#ffe6b0" opacity=".6"/>
    <g id="reflect"></g>
    <g id="boats"></g>
    <g id="wavesFar"></g>
    <g id="wavesNear"></g>
    <g id="gulls"></g>
    
    <!-- 栈桥 -->
    <g id="lamps"></g>
    <g id="rail"></g>
    <g id="pilings"></g>
    <rect x="-1400" y="662" width="4000" height="1640" fill="#2b2c60" opacity=".88"/>
    <rect x="-1400" y="661.5" width="4000" height="1.5" fill="#ffcdbf" opacity=".35"/>
    <g id="foam"></g>
    <rect x="-1400" y="562" width="4000" height="10" fill="url(#gDeck)"/>
    <rect x="-1400" y="572" width="4000" height="30" fill="#7a4c32"/>
    <rect x="-1400" y="572" width="4000" height="2.5" fill="#5a3525"/>
    <rect x="-1400" y="586" width="4000" height="1.2" fill="#5c3827" opacity=".8"/>
    <rect x="-1400" y="599" width="4000" height="3" fill="#4b2c1f"/>
    <g id="seams"></g>
    <ellipse cx="545" cy="565" rx="190" ry="6" fill="#3b1c2a" opacity=".3"/>
    
    <!-- 车轮 -->
    <g>
      <circle cx="430" cy="490" r="70" fill="none" stroke="#231c2c" stroke-width="9"/>
      <circle cx="430" cy="490" r="65.5" fill="none" stroke="#efe2cc" stroke-width="1.6" opacity=".75"/>
      <circle cx="430" cy="490" r="61" fill="none" stroke="#c7cfd6" stroke-width="3"/>
      <circle cx="430" cy="490" r="60" fill="#e6ebef" opacity=".07"/>
      <g id="spokesR"></g>
      <circle cx="430" cy="490" r="7" fill="#a7b0b8" stroke="#5b636c" stroke-width="2"/>
    </g>
    <g>
      <circle cx="650" cy="490" r="70" fill="none" stroke="#231c2c" stroke-width="9"/>
      <circle cx="650" cy="490" r="65.5" fill="none" stroke="#efe2cc" stroke-width="1.6" opacity=".75"/>
      <circle cx="650" cy="490" r="61" fill="none" stroke="#c7cfd6" stroke-width="3"/>
      <circle cx="650" cy="490" r="60" fill="#e6ebef" opacity=".07"/>
      <g id="spokesF"></g>
      <circle cx="650" cy="490" r="7" fill="#a7b0b8" stroke="#5b636c" stroke-width="2"/>
    </g>
    <path d="M353.2 503.5 A78 78 0 0 1 456.7 416.7" fill="none" stroke="#1f9e97" stroke-width="6" stroke-linecap="round"/>
    <path d="M590.3 439.9 A78 78 0 0 1 726.8 476.4" fill="none" stroke="#1f9e97" stroke-width="6" stroke-linecap="round"/>
    
    <!-- 远侧腿 -->
    <g stroke-linecap="round">
      <line id="fShin" stroke="#bf672a" stroke-width="7"/>
      <line id="fThighO" stroke="#b9aaa3" stroke-width="16"/>
      <line id="fThigh" stroke="#d3c6bd" stroke-width="13"/>
      <rect id="fPedal" width="22" height="6" rx="2" fill="#241f2b"/>
      <g id="fFoot">
        <path d="M-5 -2 C2 -5 12 -4 22 -2 C27 0 26 3 22 4 C26 6 25 9 21 9 C13 9 3 8 -5 6 C-8 4 -8 0 -5 -2 Z" fill="#b95f27"/>
      </g>
      <line id="fCrank" stroke="#7b848d" stroke-width="6"/>
    </g>
    
    <!-- 车架 -->
    <g fill="none" stroke-linecap="round" stroke-linejoin="round">
      <path d="M430 490 L530 500 L500 392 Z" stroke="#1f9e97" stroke-width="7"/>
      <path d="M500 392 L622 392 M530 500 L632 425" stroke="#1f9e97" stroke-width="8"/>
      <path d="M621 388 L633 428" stroke="#177f7a" stroke-width="12"/>
      <path d="M632 426 C639 455 643 475 650 490" stroke="#1f9e97" stroke-width="6"/>
      <path d="M506 389 L618 389 M536 492 L626 426" stroke="#86ddd5" stroke-width="2" opacity=".7"/>
      <path d="M500 392 L497 377" stroke="#aeb7bf" stroke-width="5"/>
      <path d="M621 390 L617 366" stroke="#aeb7bf" stroke-width="5"/>
      <path d="M617 366 C611 355 600 353 588 359" stroke="#c9d0d6" stroke-width="5"/>
      <path d="M592 357 L583 362" stroke="#6a3a2a" stroke-width="8"/>
      <circle cx="430" cy="490" r="8" stroke="#8d969e" stroke-width="3"/>
      <path id="chain" d="M530 483 L430 482 A8 8 0 0 0 430 498 L530 517 A17 17 0 0 0 530 483" stroke="#4c4452" stroke-width="2.5" stroke-dasharray="3 2.5"/>
    </g>
    <g id="ring">
      <circle cx="530" cy="500" r="18" fill="none" stroke="#b9c1c8" stroke-width="4" stroke-dasharray="2.2 1.6"/>
      <circle cx="530" cy="500" r="15" fill="none" stroke="#9aa3ab" stroke-width="2"/>
      <path d="M530 500 L530 486 M530 500 L543.3 495.7 M530 500 L538.2 511.3 M530 500 L521.8 511.3 M530 500 L516.7 495.7" stroke="#9aa3ab" stroke-width="3" stroke-linecap="round"/>
      <circle cx="530" cy="500" r="5" fill="#6f7880"/>
    </g>
    <path d="M476 373 C478 365 494 364 506 366 L522 369 C528 372 524 378 516 379 L486 380 C477 380 474 377 476 373 Z" fill="#6b3a26"/>
    
    <!-- 车篮与鱼 -->
    <g>
      <path d="M628 398 L640 398" stroke="#aeb7bf" stroke-width="3" stroke-linecap="round"/>
      <g id="fish1" transform="translate(652 360) rotate(-12)">
        <path d="M0 -16 C7 -10 8 8 0 26 C-8 8 -7 -10 0 -16 Z" fill="#9cc3d6"/>
        <path d="M0 -14 L-9 -29 L0 -23 L9 -29 Z" fill="#86afc6"/>
        <path d="M0 -15 C3 -6 3 8 0 22" fill="none" stroke="#6f98b3" stroke-width="1.6"/>
      </g>
      <g id="fish2" transform="translate(678 359) rotate(15)">
        <path d="M0 -16 C7 -10 8 8 0 26 C-8 8 -7 -10 0 -16 Z" fill="#b1d0de"/>
        <path d="M0 -14 L-9 -29 L0 -23 L9 -29 Z" fill="#93b8cc"/>
        <path d="M0 -15 C3 -6 3 8 0 22" fill="none" stroke="#7aa2bb" stroke-width="1.6"/>
      </g>
      <path d="M636 362 L694 362 L688 408 L642 408 Z" fill="#c98c4d" stroke="#8b5a2e" stroke-width="2" stroke-linejoin="round"/>
      <path d="M637 373 L693 373 M638.5 385 L691.5 385 M640 397 L690 397" stroke="#9a6534" stroke-width="1.6"/>
      <path d="M650 362 L652 408 M665 362 L665 408 M680 362 L678 408" stroke="#a86f3b" stroke-width="1.4"/>
      <rect x="633" y="357" width="64" height="7" rx="3.5" fill="#a86d38"/>
    </g>
    
    <!-- 鹈鹕:身体(后层) -->
    <g id="pBack">
      <g id="wingF">
        <path d="M505 276 C548 260 584 272 598 300 C606 318 606 338 604 356 C596 346 584 336 570 330 Z" fill="#d2c3bb"/>
        <path d="M586 304 C598 320 604 338 604 356 C596 348 588 340 578 334 C584 326 588 314 586 304 Z" fill="#1f1b26"/>
      </g>
      <path d="M428 300 L384 286 L394 301 L380 314 L396 316 L386 328 L432 326 Z" fill="#e3d6ca"/>
      <path d="M420 300 C430 255 520 245 565 265 C600 280 595 340 560 360 C520 382 460 372 435 345 C422 332 415 315 420 300 Z" fill="url(#gFeather)" stroke="#cdbab4" stroke-width="1.5"/>
      <path d="M566 270 C594 286 592 334 562 356 C578 332 582 298 562 274 Z" fill="#ffd6a0" opacity=".7"/>
    </g>
    
    <!-- 近侧腿 -->
    <g stroke-linecap="round">
      <line id="nCrank" stroke="#b3bcc4" stroke-width="6"/>
      <circle cx="530" cy="500" r="4.5" fill="#8a939b"/>
      <rect id="nPedal" width="22" height="6" rx="2" fill="#3a3340"/>
      <line id="nShin" stroke="#f08d38" stroke-width="7.5"/>
      <g id="nFoot">
        <path d="M-5 -2 C2 -5 12 -4 22 -2 C27 0 26 3 22 4 C26 6 25 9 21 9 C13 9 3 8 -5 6 C-8 4 -8 0 -5 -2 Z" fill="#f08632"/>
        <path d="M1 2 L20 1 M1 5 L19 7" fill="none" stroke="#c9621f" stroke-width="1"/>
      </g>
      <line id="nThighO" stroke="#cdbdb3" stroke-width="17"/>
      <line id="nThigh" stroke="#f3eadf" stroke-width="14"/>
    </g>
    
    <!-- 鹈鹕:翅膀、围巾、头(前层) -->
    <g id="pFront">
      <g id="wingN">
        <path d="M462 292 C500 262 552 266 578 296 C590 314 595 340 596 364 C584 354 568 342 548 336 C515 328 480 318 462 292 Z" fill="#f5ede3" stroke="#d6c7bd" stroke-width="1.5"/>
        <path d="M568 300 C586 318 594 342 596 364 C586 356 574 346 562 338 C570 326 572 312 568 300 Z" fill="#26212e"/>
        <path d="M486 298 C514 296 540 304 560 318 M496 310 C522 311 544 320 556 330" fill="none" stroke="#dccfc4" stroke-width="2" stroke-linecap="round"/>
      </g>
      <path id="scarfB" fill="#b02a43"/>
      <path id="scarfA" fill="#d83a4f"/>
      <g id="headG">
        <path d="M548 292 C538 256 576 242 571 212 C568 196 574 184 588 180" fill="none" stroke="#f6eee4" stroke-width="30" stroke-linecap="round"/>
        <path d="M559 286 C553 256 587 244 582 214" fill="none" stroke="#ffd8a8" stroke-width="4.5" stroke-linecap="round" opacity=".55"/>
        <path d="M534 252 Q551 262 569 250 L570 262 Q551 274 533 264 Z" fill="#d83a4f"/>
        <circle cx="534" cy="259" r="6" fill="#b82e46"/>
        <path d="M574 164 C562 152 552 160 546 150 C552 166 562 172 572 172 Z" fill="#efe3d4"/>
        <ellipse cx="593" cy="176" rx="25" ry="21" fill="#f7efe5"/>
        <ellipse cx="588" cy="163" rx="15" ry="7" fill="#f7dc98" opacity=".55"/>
        <g id="pouch">
          <path d="M611 192 C652 201 700 213 745 222 C724 246 684 264 650 256 C624 250 600 232 590 205 C596 198 604 194 611 192 Z" fill="url(#gPouch)"/>
          <path d="M628 214 C650 226 682 234 712 232" fill="none" stroke="#ffbf85" stroke-width="3" stroke-linecap="round" opacity=".6"/>
        </g>
        <path d="M606 163 C650 170 702 187 744 204 C755 209 755 221 745 223 C700 213 652 201 611 192 Z" fill="url(#gBeak)"/>
        <path d="M611 192 C652 201 700 213 745 223" fill="none" stroke="#d2702e" stroke-width="2"/>
        <path d="M613 172 C660 180 704 194 738 207" fill="none" stroke="#e38c30" stroke-width="1.4" opacity=".8"/>
        <path d="M742 204 C755 208 757 222 745 224 C749 216 748 209 742 204 Z" fill="#d6503b"/>
        <circle cx="600" cy="170" r="8" fill="#f2c39c"/>
        <g id="eye">
          <circle cx="600" cy="170" r="4.3" fill="#2a1b1f"/>
          <circle cx="601.6" cy="168.4" r="1.4" fill="#fff"/>
        </g>
      </g>
    </g>
    </svg>
    
    <script>
    (() => {
      const NS = 'http://www.w3.org/2000/svg';
      const $ = id => document.getElementById(id);
      const el = (tag, attrs, parent) => {
        const e = document.createElementNS(NS, tag);
        for (const k in attrs) e.setAttribute(k, attrs[k]);
        if (parent) parent.appendChild(e);
        return e;
      };
      const TAU = Math.PI * 2, DEG = 180 / Math.PI;
      let seed = 20260922;
      const rnd = () => (seed = (seed * 16807) % 2147483647) / 2147483647;
      const X0 = -1400, X1 = 2600;
    
      // 运动参数:踏频、齿比 → 车轮角速度 → 地面速度
      const CRANK_W = TAU / 1.25;
      const WHEEL_W = CRANK_W * 1.45;
      const V = WHEEL_W * 70;
    
      // 星星
      const stars = $('stars');
      for (let i = 0; i < 150; i++) {
        const x = X0 + rnd() * (X1 - X0);
        const y = i < 90 ? 6 + Math.pow(rnd(), 1.7) * 230 : -1500 + rnd() * 1500;
        const c = el('circle', {cx: x.toFixed(1), cy: y.toFixed(1), r: (0.5 + rnd() * 1.2).toFixed(2), fill: '#fff4e2', class: 'tw'}, stars);
        c.style.animationDuration = (2 + rnd() * 3).toFixed(2) + 's';
        c.style.animationDelay = (-rnd() * 5).toFixed(2) + 's';
      }
    
      // 可平铺滚动图层
      const layers = [];
      function layer(id, speed, tile, build) {
        const g = el('g', {}, $(id));
        const k0 = Math.floor(X0 / tile) - 1, k1 = Math.ceil(X1 / tile) + 1;
        for (let k = k0; k <= k1; k++) build(el('g', {transform: `translate(${k * tile} 0)`}, g));
        layers.push({g, speed, tile});
      }
    
      // 云
      const cloudDefs = [];
      for (let i = 0; i < 6; i++) cloudDefs.push({x: i * 265 + rnd() * 60, y: 70 + rnd() * 240, w: 130 + rnd() * 170});
      function cloud(p, d) {
        const high = d.y < 190;
        const body = high ? '#b0679a' : '#f2968e';
        const lit = high ? '#f0a4ae' : '#ffd29f';
        const h = 13 + d.w * 0.03;
        const g = el('g', {opacity: high ? 0.75 : 0.85}, p);
        el('rect', {x: d.x, y: d.y, width: d.w, height: h, rx: h / 2, fill: body}, g);
        el('ellipse', {cx: d.x + d.w * 0.3, cy: d.y + 2, rx: d.w * 0.16, ry: h * 0.95, fill: body}, g);
        el('ellipse', {cx: d.x + d.w * 0.55, cy: d.y - 1, rx: d.w * 0.19, ry: h * 1.3, fill: body}, g);
        el('ellipse', {cx: d.x + d.w * 0.77, cy: d.y + 3, rx: d.w * 0.11, ry: h * 0.8, fill: body}, g);
        el('rect', {x: d.x + h * 0.5, y: d.y + h - 3.5, width: d.w - h, height: 3, rx: 1.5, fill: lit}, g);
      }
      layer('clouds', 14, 1600, s => cloudDefs.forEach(d => cloud(s, d)));
    
      // 远山与岬角
      function hillPath(T, f) {
        const N = Math.round(T / 8);
        let d = 'M-2 422';
        for (let i = 0; i <= N; i++) {
          const x = -2 + i * (T + 4) / N;
          d += ` L${x.toFixed(1)} ${(420 - Math.max(0, f(x))).toFixed(1)}`;
        }
        return d + ` L${T + 2} 422 Z`;
      }
      const farF = x => 26 * Math.sin(TAU * 2 * x / 2400 + 1.3) + 10 * Math.sin(TAU * 5 * x / 2400 + 0.5) + 3;
      const nearF = x => 42 * Math.sin(TAU * 3 * x / 3000 + 0.4) + 14 * Math.sin(TAU * 8 * x / 3000 + 2) - 6;
      layer('hillsFar', 5, 2400, s => el('path', {d: hillPath(2400, farF), fill: '#c27b9c'}, s));
    
      let xm = 0, hm = -1;
      for (let x = 0; x < 3000; x += 3) { const h = nearF(x); if (h > hm) { hm = h; xm = x; } }
      const beams = [];
      layer('hillsNear', 11, 3000, s => {
        el('path', {d: hillPath(3000, nearF), fill: '#6d4677'}, s);
        const x = xm, b = 420 - hm + 5, ly = b - 45;
        const beam = el('path', {d: `M${x} ${ly} L${x - 240} ${ly - 20} L${x - 240} ${ly + 20} Z`, fill: '#fff2c4', opacity: 0.25}, s);
        el('path', {d: `M${x + 11} ${b} L${x + 11} ${b - 9} L${x + 18} ${b - 15} L${x + 25} ${b - 9} L${x + 25} ${b} Z`, fill: '#553660'}, s);
        el('path', {d: `M${x - 8} ${b} L${x + 8} ${b} L${x + 5} ${b - 40} L${x - 5} ${b - 40} Z`, fill: '#efe2e8'}, s);
        el('path', {d: `M${x - 7.3} ${b - 9} L${x + 7.3} ${b - 9} L${x + 6.5} ${b - 17} L${x - 6.5} ${b - 17} Z M${x - 5.9} ${b - 25} L${x + 5.9} ${b - 25} L${x + 5.3} ${b - 32} L${x - 5.3} ${b - 32} Z`, fill: '#c9465d'}, s);
        el('rect', {x: x - 8, y: b - 42, width: 16, height: 3, fill: '#3b2847'}, s);
        el('rect', {x: x - 5, y: b - 50, width: 10, height: 9, fill: '#ffe9a6'}, s);
        el('path', {d: `M${x - 7.5} ${b - 50} L${x} ${b - 58} L${x + 7.5} ${b - 50} Z`, fill: '#3b2847'}, s);
        const flash = el('circle', {cx: x, cy: ly, r: 18, fill: 'url(#gLamp)', opacity: 0}, s);
        beams.push({beam, flash, x, y: ly});
      });
    
      // 海面倒影
      const refl = $('reflect'), refLines = [];
      for (let i = 0; i < 16; i++) {
        const y = 425 + i * 6 + i * i * 0.18;
        const w = (36 + i * 7) * (0.55 + rnd() * 0.6);
        const x = 840 + (rnd() - 0.5) * (20 + i * 3);
        const e = el('line', {x1: (x - w / 2).toFixed(1), y1: y.toFixed(1), x2: (x + w / 2).toFixed(1), y2: y.toFixed(1), stroke: '#ffe3a6', 'stroke-width': (2 + i * 0.12).toFixed(2), 'stroke-linecap': 'round'}, refl);
        refLines.push({e, ph: rnd() * TAU, sp: 2 + rnd() * 3});
      }
    
      // 帆船
      function boat(p, x, y, k) {
        const g = el('g', {transform: `translate(${x} ${y}) scale(${k})`, fill: '#4b3161'}, p);
        el('path', {d: 'M-22 -5 L22 -5 L16 2 L-16 2 Z'}, g);
        el('path', {d: 'M-1 -7 L-1 -48 L-21 -9 Z'}, g);
        el('path', {d: 'M2 -9 L2 -42 L18 -9 Z'}, g);
      }
      layer('boats', 7, 1900, s => { boat(s, 320, 420, 0.9); boat(s, 1260, 421, 0.6); });
    
      // 波纹
      function waveLayer(id, speed, tile, n, y0, y1, col) {
        const defs = [];
        for (let i = 0; i < n; i++) {
          const y = y0 + rnd() * (y1 - y0);
          defs.push({x: rnd() * (tile - 40), y, w: 5 + (y - 420) * 0.12});
        }
        layer(id, speed, tile, s => defs.forEach(d => el('path', {
          d: `M${d.x.toFixed(1)} ${d.y.toFixed(1)} q${(d.w / 2).toFixed(1)} ${(-d.w * 0.35).toFixed(1)} ${d.w.toFixed(1)} 0 q${(d.w / 2).toFixed(1)} ${(-d.w * 0.35).toFixed(1)} ${d.w.toFixed(1)} 0`,
          fill: 'none', stroke: col, 'stroke-width': 1.3, 'stroke-linecap': 'round', opacity: (0.18 + (d.y - 420) / 500).toFixed(2)
        }, s)));
      }
      waveLayer('wavesFar', 16, 480, 12, 428, 478, '#ffe0cc');
      waveLayer('wavesNear', 55, 420, 12, 478, 552, '#ffd3c6');
    
      // 海鸥
      const gulls = [];
      [[160, 150, 1, 34], [520, 105, 0.72, 24], [980, 205, 0.9, 42], [760, 62, 0.58, 18], [1320, 170, 0.8, 30]].forEach(([x, y, k, sp], i) => {
        const g = el('g', {}, $('gulls'));
        const p = el('path', {fill: 'none', stroke: '#3a2650', 'stroke-width': 2.6, 'stroke-linecap': 'round', 'stroke-linejoin': 'round'}, g);
        gulls.push({g, p, x, y, k, sp, ph: i * 1.9, fl: 6.5 + i * 0.7});
      });
    
      // 栈桥:路灯、绳栏、桩、浪花、木板缝(与地面同速)
      layer('lamps', V, 760, s => {
        const x = 380;
        el('circle', {cx: x, cy: 330, r: 62, fill: 'url(#gLamp)'}, s);
        el('rect', {x: x - 3, y: 342, width: 6, height: 210, fill: '#30243c'}, s);
        el('rect', {x: x - 9, y: 538, width: 18, height: 24, rx: 3, fill: '#30243c'}, s);
        el('rect', {x: x - 7, y: 360, width: 14, height: 4, rx: 2, fill: '#30243c'}, s);
        el('path', {d: `M${x - 9} 318 L${x + 9} 318 L${x + 6} 342 L${x - 6} 342 Z`, fill: '#ffe7a2'}, s);
        el('path', {d: `M${x} 318 L${x} 342`, stroke: '#30243c', 'stroke-width': 1.6}, s);
        el('path', {d: `M${x - 13} 319 L${x} 304 L${x + 13} 319 Z`, fill: '#30243c'}, s);
        el('circle', {cx: x, cy: 301, r: 3, fill: '#30243c'}, s);
        el('rect', {x: x - 8, y: 341, width: 16, height: 4, rx: 2, fill: '#30243c'}, s);
      });
      layer('rail', V, 175, s => {
        el('path', {d: 'M0 525 Q87.5 546 175 525', fill: 'none', stroke: '#e3c392', 'stroke-width': 2.6}, s);
        el('rect', {x: -5, y: 516, width: 10, height: 48, rx: 2, fill: '#6a422d'}, s);
        el('rect', {x: -6.5, y: 512, width: 13, height: 6, rx: 3, fill: '#8d5c3c'}, s);
      });
      layer('pilings', V, 175, s => {
        el('path', {d: 'M12 610 L163 656 M12 656 L163 610', stroke: '#48291d', 'stroke-width': 6}, s);
        el('rect', {x: -12, y: 598, width: 24, height: 110, fill: '#5a3727'}, s);
        el('rect', {x: -12, y: 598, width: 7, height: 110, fill: '#6f4633'}, s);
      });
      layer('foam', V, 175, s => {
        el('ellipse', {cx: 0, cy: 663, rx: 20, ry: 3, fill: '#ffd6cb', opacity: 0.5}, s);
        el('path', {d: 'M48 676 q10 -3 20 0 M112 688 q12 -3 24 0', fill: 'none', stroke: '#ffd6cb', 'stroke-width': 1.3, opacity: 0.3, 'stroke-linecap': 'round'}, s);
      });
      layer('seams', V, 44, s => {
        el('rect', {x: -0.8, y: 574, width: 1.6, height: 25, fill: '#5a3625'}, s);
        el('rect', {x: 21.4, y: 563, width: 1.2, height: 8, fill: '#a96f40'}, s);
        el('circle', {cx: 5, cy: 580, r: 1.1, fill: '#4a2b1e'}, s);
        el('circle', {cx: 5, cy: 593, r: 1.1, fill: '#4a2b1e'}, s);
      });
    
      // 辐条
      function spokes(id, cx, cy) {
        const g = $(id);
        for (let i = 0; i < 18; i++) {
          const a = i / 18 * TAU;
          el('line', {x1: cx, y1: cy, x2: (cx + 60 * Math.cos(a)).toFixed(2), y2: (cy + 60 * Math.sin(a)).toFixed(2), stroke: '#dde3e8', 'stroke-width': 1.1}, g);
        }
        el('rect', {x: cx + 34, y: cy - 2.5, width: 10, height: 5, rx: 1.5, fill: '#ff8a3d'}, g);
        return g;
      }
      const spR = spokes('spokesR', 430, 490), spF = spokes('spokesF', 650, 490);
    
      // 腿:两段式反向运动学,脚始终踩在踏板上
      const BB = {x: 530, y: 500}, CR = 28, LA = 84, LB = 94;
      const mk = (p, hx, hy, off) => ({
        crank: $(p + 'Crank'), pedal: $(p + 'Pedal'), shin: $(p + 'Shin'), foot: $(p + 'Foot'),
        thighs: [$(p + 'ThighO'), $(p + 'Thigh')], hx, hy, off
      });
      const legN = mk('n', 522, 348, 0), legF = mk('f', 512, 348, Math.PI);
      const setLine = (e, x1, y1, x2, y2) => {
        e.setAttribute('x1', x1.toFixed(2)); e.setAttribute('y1', y1.toFixed(2));
        e.setAttribute('x2', x2.toFixed(2)); e.setAttribute('y2', y2.toFixed(2));
      };
      function leg(L, th, bob) {
        const a = th + L.off;
        const px = BB.x + CR * Math.cos(a), py = BB.y + CR * Math.sin(a);
        setLine(L.crank, BB.x, BB.y, px, py);
        L.pedal.setAttribute('x', (px - 11).toFixed(2));
        L.pedal.setAttribute('y', (py - 3).toFixed(2));
        const tx = px - 5, ty = py - 9, hx = L.hx, hy = L.hy + bob;
        let d = Math.hypot(tx - hx, ty - hy);
        d = Math.min(Math.max(d, Math.abs(LA - LB) + 1), (LA + LB) * 0.999);
        const phi = Math.atan2(ty - hy, tx - hx);
        const al = Math.acos((LA * LA + d * d - LB * LB) / (2 * LA * d));
        const kx = hx + LA * Math.cos(phi - al), ky = hy + LA * Math.sin(phi - al);
        for (const e of L.thighs) setLine(e, hx, hy, kx, ky);
        setLine(L.shin, kx, ky, tx, ty);
        L.foot.setAttribute('transform', `translate(${tx.toFixed(2)} ${ty.toFixed(2)}) rotate(${(7 * Math.sin(a)).toFixed(2)})`);
      }
    
      // 围巾飘带
      function ribbon(x0, y0, len, w0, droop, amp, freq, ph, t) {
        const N = 14, top = [], bot = [];
        for (let i = 0; i <= N; i++) {
          const s = i / N;
          const x = x0 - len * s + 4 * s * Math.sin(t * 9 + ph + s * 3);
          const y = y0 + droop * s * s + amp * s * Math.sin(s * freq - t * 13 + ph);
          const w = w0 * (1 - 0.55 * s);
          top.push(`${x.toFixed(1)} ${(y - w / 2).toFixed(1)}`);
          bot.push(`${x.toFixed(1)} ${(y + w / 2).toFixed(1)}`);
        }
        return 'M' + top.join(' L') + ' L' + bot.reverse().join(' L') + ' Z';
      }
    
      const pBack = $('pBack'), pFront = $('pFront'), wingN = $('wingN'), wingF = $('wingF');
      const headG = $('headG'), pouch = $('pouch'), eye = $('eye');
      const scarfA = $('scarfA'), scarfB = $('scarfB');
      const fish1 = $('fish1'), fish2 = $('fish2');
      const ring = $('ring'), chain = $('chain');
      let nextBlink = 2;
    
      function frame(now) {
        const t = now / 1000;
        const th = t * CRANK_W;
    
        // 身体随踩踏轻微起伏,翅膀反向微转保持握把
        const bob = 1.6 * Math.sin(2 * th + 0.8);
        const pt = `translate(0 ${bob.toFixed(2)})`;
        pBack.setAttribute('transform', pt);
        pFront.setAttribute('transform', pt);
        const wr = (-bob / 96 * DEG).toFixed(2);
        wingN.setAttribute('transform', `rotate(${wr} 500 285)`);
        wingF.setAttribute('transform', `rotate(${wr} 505 280)`);
    
        leg(legF, th, bob);
        leg(legN, th, bob);
    
        ring.setAttribute('transform', `rotate(${(th * DEG % 360).toFixed(2)} 530 500)`);
        chain.setAttribute('stroke-dashoffset', ((th * 18) % 5.5).toFixed(2));
        const wa = (t * WHEEL_W * DEG % 360).toFixed(2);
        spR.setAttribute('transform', `rotate(${wa} 430 490)`);
        spF.setAttribute('transform', `rotate(${wa} 650 490)`);
    
        headG.setAttribute('transform', `rotate(${(1.6 * Math.sin(2 * th + 2)).toFixed(2)} 550 288)`);
        const ps = 1 + 0.07 * Math.sin(2 * th + 2.6);
        pouch.setAttribute('transform', `translate(0 ${(195 * (1 - ps)).toFixed(2)}) scale(1 ${ps.toFixed(3)})`);
    
        let es = 1;
        const bt = t - nextBlink;
        if (bt > 0) {
          if (bt < 0.18) es = 1 - 0.92 * Math.sin(Math.PI * bt / 0.18);
          else nextBlink = t + 2.2 + Math.random() * 3.5;
        }
        eye.setAttribute('transform', `translate(0 ${(170 * (1 - es)).toFixed(2)}) scale(1 ${es.toFixed(3)})`);
    
        scarfB.setAttribute('d', ribbon(536, 262, 62, 9, 18, 6, 6, 1.8, t));
        scarfA.setAttribute('d', ribbon(536, 257, 80, 11, 10, 7, 7, 0, t));
    
        fish1.setAttribute('transform', `translate(652 360) rotate(${(-12 + 8 * Math.sin(t * 7)).toFixed(2)})`);
        fish2.setAttribute('transform', `translate(678 359) rotate(${(15 + 8 * Math.sin(t * 7 + 2.1)).toFixed(2)})`);
    
        for (const L of layers) {
          L.g.setAttribute('transform', `translate(${(-((t * L.speed) % L.tile)).toFixed(2)} 0)`);
        }
    
        const la = t * 1.3, c = Math.cos(la);
        for (const B of beams) {
          B.beam.setAttribute('transform', `translate(${B.x} ${B.y}) scale(${c.toFixed(3)} 1) translate(${-B.x} ${-B.y})`);
          B.beam.setAttribute('opacity', (0.1 + 0.22 * Math.abs(c)).toFixed(3));
          B.flash.setAttribute('opacity', (Math.sin(la) > 0 ? Math.pow(1 - Math.abs(c), 3) : 0).toFixed(3));
        }
    
        for (const R of refLines) R.e.setAttribute('opacity', (0.35 + 0.35 * Math.sin(t * R.sp + R.ph)).toFixed(3));
    
        for (const G of gulls) {
          const span = 2000;
          const x = ((G.x - t * G.sp + 400) % span + span) % span - 400;
          const y = G.y + 7 * Math.sin(t * 0.7 + G.ph);
          const f = Math.sin(t * G.fl + G.ph);
          const ty = -2 + 7 * f, cy = -7 + 3 * f;
          G.p.setAttribute('d', `M-17 ${ty.toFixed(2)} Q-8 ${cy.toFixed(2)} 0 2 Q8 ${cy.toFixed(2)} 17 ${ty.toFixed(2)}`);
          G.g.setAttribute('transform', `translate(${x.toFixed(1)} ${y.toFixed(1)}) scale(${G.k})`);
        }
    
        requestAnimationFrame(frame);
      }
      frame(performance.now());
    })();
    </script>
    </body>
    </html>
    ```分别是网页版和code模式,效果差异挺大,但共同点是细节满满,截图看不出来的细节。
    
    ---
    
    车铃铛能点击这件事,真的想不到它会做,我的提示词很简单。
    
    ---```
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>鹈鹕骑车记</title>
    <style>
    *{box-sizing:border-box;margin:0;padding:0}
    html,body{height:100%;overflow:hidden;background:#a7dcf5;font-family:"PingFang SC","Hiragino Sans GB","Microsoft YaHei",system-ui,sans-serif;color:#243044;-webkit-user-select:none;user-select:none}
    #scene{position:fixed;inset:0;width:100%;height:100%;display:block;cursor:pointer}
    .hud{position:fixed;left:28px;top:22px;pointer-events:none}
    .hud h1{font-size:clamp(28px,4.2vw,46px);font-weight:900;letter-spacing:3px;color:#fff;text-shadow:0 3px 0 #2b6f9e,0 8px 20px rgba(0,40,80,.25)}
    .hud p{margin-top:6px;font-size:14px;color:#1d4b6e;opacity:.85}
    .odo{position:fixed;right:28px;top:26px;background:rgba(255,255,255,.78);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);padding:8px 16px;border-radius:14px;font-weight:700;font-size:14px;box-shadow:0 4px 14px rgba(0,50,90,.15)}
    .odo b{font-variant-numeric:tabular-nums;font-size:22px;color:#e8483f;margin:0 2px}
    .ctrl{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);display:flex;align-items:center;gap:12px;background:rgba(255,255,255,.82);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);padding:10px 18px;border-radius:999px;box-shadow:0 6px 20px rgba(0,40,70,.18);white-space:nowrap}
    .ctrl input[type=range]{width:min(38vw,220px);accent-color:#e8483f;cursor:pointer}
    .ctrl .lbl{font-size:13px;font-weight:700;color:#40546b}
    .ctrl .val{font-size:13px;font-weight:800;color:#e8483f;min-width:40px;font-variant-numeric:tabular-nums}
    .ctrl button{border:0;background:#ffcf3f;color:#5a3b00;font-weight:800;padding:8px 16px;border-radius:999px;cursor:pointer;box-shadow:0 3px 0 #d69b00;font-size:14px;font-family:inherit}
    .ctrl button:active{transform:translateY(2px);box-shadow:0 1px 0 #d69b00}
    .spark{animation:twinkle 2.6s ease-in-out infinite;opacity:0}
    @keyframes twinkle{0%,100%{opacity:0}50%{opacity:.95}}
    .beam{animation:beam 4s linear infinite;opacity:0}
    @keyframes beam{0%,100%{opacity:0}12%,30%{opacity:.5}46%{opacity:0}}
    .bob{animation:bob 3.4s ease-in-out infinite;transform-box:fill-box;transform-origin:50% 100%}
    @keyframes bob{50%{transform:translateY(3px) rotate(2deg)}}
    .ol{fill:#263041;stroke:#263041;stroke-width:7;stroke-linejoin:round}
    .fl{fill:url(#gBody);stroke:none}
    .ink{stroke:#263041;stroke-width:3;stroke-linejoin:round;stroke-linecap:round}
    </style>
    </head>
    <body>
    <div class="hud">
      <h1>鹈鹕骑车记</h1>
      <p>一只鹈鹕的海边兜风 · 点击画面按铃铛</p>
    </div>
    <div class="odo">已骑行 <b id="odo">0</b> 米</div>
    <div class="ctrl">
      <span class="lbl">🐢</span>
      <input id="spd" type="range" min="0" max="250" value="100" aria-label="骑行速度">
      <span class="lbl">🚀</span>
      <span class="val" id="spdVal">1.0×</span>
      <button id="bellBtn" type="button">🔔 按铃</button>
    </div>
    
    <svg id="scene" viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg">
    <defs>
      <linearGradient id="gSky" gradientUnits="userSpaceOnUse" x1="0" y1="-500" x2="0" y2="400">
        <stop offset="0" stop-color="#3f9ee0"/><stop offset=".6" stop-color="#9dd7f3"/><stop offset="1" stop-color="#ffe8c6"/>
      </linearGradient>
      <radialGradient id="gGlow"><stop offset="0" stop-color="#fff7cf" stop-opacity=".95"/><stop offset=".45" stop-color="#fff2b8" stop-opacity=".35"/><stop offset="1" stop-color="#fff2b8" stop-opacity="0"/></radialGradient>
      <radialGradient id="gSun" cx=".4" cy=".35"><stop offset="0" stop-color="#fffbe0"/><stop offset="1" stop-color="#ffd35c"/></radialGradient>
      <linearGradient id="gSea" gradientUnits="userSpaceOnUse" x1="0" y1="395" x2="0" y2="482">
        <stop offset="0" stop-color="#93d3e8"/><stop offset="1" stop-color="#3b90c2"/>
      </linearGradient>
      <linearGradient id="gSand" gradientUnits="userSpaceOnUse" x1="0" y1="478" x2="0" y2="552">
        <stop offset="0" stop-color="#f8e4b6"/><stop offset="1" stop-color="#ecc98a"/>
      </linearGradient>
      <linearGradient id="gBoard" gradientUnits="userSpaceOnUse" x1="0" y1="550" x2="0" y2="636">
        <stop offset="0" stop-color="#d9a26b"/><stop offset="1" stop-color="#b87a45"/>
      </linearGradient>
      <linearGradient id="gFg" gradientUnits="userSpaceOnUse" x1="0" y1="650" x2="0" y2="760">
        <stop offset="0" stop-color="#f1d49b"/><stop offset="1" stop-color="#e2b872"/>
      </linearGradient>
      <radialGradient id="gBody" gradientUnits="userSpaceOnUse" cx="600" cy="225" r="190">
        <stop offset="0" stop-color="#ffffff"/><stop offset=".55" stop-color="#f6f9fc"/><stop offset="1" stop-color="#d5dfea"/>
      </radialGradient>
      <linearGradient id="gBeak" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#ffd45e"/><stop offset="1" stop-color="#f3a42f"/></linearGradient>
      <linearGradient id="gPouch" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#ffbe72"/><stop offset="1" stop-color="#ee8636"/></linearGradient>
      <linearGradient id="gHelmet" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#5aa4ff"/><stop offset="1" stop-color="#2463c9"/></linearGradient>
    
      <!-- 鹈鹕白色部分:描边与填充共用同一形状,拼接处无缝 -->
      <g id="dBody">
        <path d="M504,306 L452,294 Q444,304 456,311 L448,318 Q448,330 462,329 L506,330 Z"/>
        <path d="M488,316 C520,280 560,248 610,246 C650,244 674,272 662,305 C652,338 614,358 570,358 C535,358 505,340 488,316 Z"/>
      </g>
      <g id="dNeck">
        <path d="M598,272 C594,236 630,222 648,182 L684,182 C686,214 660,232 664,284 Z"/>
        <ellipse cx="668" cy="170" rx="27" ry="23"/>
      </g>
    </defs>
    
    <!-- ===== 远景 ===== -->
    <rect x="-1200" y="-800" width="3600" height="1210" fill="url(#gSky)"/>
    <g id="sun">
      <circle cx="985" cy="135" r="150" fill="url(#gGlow)"/>
      <circle cx="985" cy="135" r="50" fill="url(#gSun)"/>
    </g>
    <g id="lyClouds"></g>
    <g id="lyGulls"></g>
    <rect x="-1200" y="395" width="3600" height="90" fill="url(#gSea)"/>
    <rect x="-1200" y="394" width="3600" height="2.5" fill="#eef9ff" opacity=".85"/>
    <g id="lyIsland"></g>
    <g id="lyBoat"></g>
    <g id="lySpark"></g>
    <rect x="-1200" y="478" width="3600" height="80" fill="url(#gSand)"/>
    <g id="lyFoam"></g>
    <g id="lyBeach"></g>
    
    <!-- ===== 栈道 ===== -->
    <g id="lyRail"></g>
    <rect x="-1200" y="505" width="3600" height="5" rx="2" fill="#7a5234"/>
    <rect x="-1200" y="526" width="3600" height="4" rx="2" fill="#7a5234"/>
    <rect x="-1200" y="550" width="3600" height="88" fill="url(#gBoard)"/>
    <rect x="-1200" y="550" width="3600" height="5" fill="#e8b884"/>
    <g id="lyPlanks"></g>
    <rect x="-1200" y="636" width="3600" height="16" fill="#8e5b34"/>
    <rect x="-1200" y="652" width="3600" height="700" fill="url(#gFg)"/>
    <g id="lyPiles"></g>
    <g id="lyFg"></g>
    
    <!-- ===== 自行车 + 鹈鹕 ===== -->
    <g id="rig">
    <ellipse id="shadow" cx="600" cy="585" rx="220" ry="9" fill="#5b3a1e" opacity=".22"/>
    <!-- 远侧:曲柄、脚蹬、腿 -->
    <line id="farCrank" x1="585" y1="510" x2="585" y2="542" stroke="#6f7680" stroke-width="7" stroke-linecap="round"/>
    <rect id="farPedal" x="573" y="539" width="24" height="7" rx="2" fill="#3a3e45"/>
    <path id="farLegO" fill="none" stroke="#263041" stroke-width="15" stroke-linecap="round" stroke-linejoin="round"/>
    <path id="farLegF" fill="none" stroke="#c9691c" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/>
    <g id="farFoot"><path d="M-3,-5 C6,-4 16,-1 25,2 Q30,5 25,8 Q20,8 21,11 Q24,14 19,16 Q15,15 14,16 Q14,20 9,19 L-2,9 Z" fill="#c9691c" class="ink"/></g>
    <!-- 车轮(辐条由脚本生成) -->
    <g id="wheelR"></g>
    <g id="wheelF"></g>
    <!-- 挡泥板 -->
    <path d="M365.3,492.2 A90,90 0 0 1 523.9,442.1" fill="none" stroke="#263041" stroke-width="11" stroke-linecap="round"/>
    <path d="M365.3,492.2 A90,90 0 0 1 523.9,442.1" fill="none" stroke="#f4ecd8" stroke-width="6" stroke-linecap="round"/>
    <path d="M658.1,476.7 A90,90 0 0 1 831.9,476.7" fill="none" stroke="#263041" stroke-width="11" stroke-linecap="round"/>
    <path d="M658.1,476.7 A90,90 0 0 1 831.9,476.7" fill="none" stroke="#f4ecd8" stroke-width="6" stroke-linecap="round"/>
    <!-- 车架 -->
    <path d="M730,425 Q744,468 745,500" fill="none" stroke="#263041" stroke-width="13" stroke-linecap="round"/>
    <path d="M730,425 Q744,468 745,500" fill="none" stroke="#e8483f" stroke-width="8" stroke-linecap="round"/>
    <path d="M455,500 L585,510 L555,395 Z M555,395 L720,390 M585,510 L730,425 M720,390 L730,425" fill="none" stroke="#263041" stroke-width="14" stroke-linejoin="round" stroke-linecap="round"/>
    <path d="M455,500 L585,510 L555,395 Z M555,395 L720,390 M585,510 L730,425 M720,390 L730,425" fill="none" stroke="#e8483f" stroke-width="9" stroke-linejoin="round" stroke-linecap="round"/>
    <path d="M562,392 L712,388 M592,504 L722,429" stroke="#ff8f86" stroke-width="2" stroke-linecap="round" opacity=".8"/>
    <circle cx="745" cy="500" r="5" fill="#9aa3ad"/>
    <!-- 链条与牙盘 -->
    <path id="chain" d="M455,489 L585,488 A22,22 0 0 1 585,532 L455,511 A11,11 0 0 1 455,489 Z" fill="none" stroke="#3a3b42" stroke-width="3.2" stroke-dasharray="3 2"/>
    <circle cx="455" cy="500" r="10" fill="#c3cad2" class="ink" style="stroke-width:2.5"/>
    <g id="ring">
      <circle cx="585" cy="510" r="24" fill="none" stroke="#6d7580" stroke-width="3" stroke-dasharray="2.5 2.2"/>
      <circle cx="585" cy="510" r="21" fill="#d3d9e0" class="ink"/>
      <path d="M585,510 L585,493 M585,510 L601.2,504.7 M585,510 L595,523.8 M585,510 L575,523.8 M585,510 L568.8,504.7" stroke="#8c95a1" stroke-width="4" stroke-linecap="round"/>
    </g>
    <!-- 座杆、坐垫 -->
    <path d="M555,395 L548,364" stroke="#263041" stroke-width="10" stroke-linecap="round"/>
    <path d="M555,395 L548,364" stroke="#b4bcc5" stroke-width="5.5" stroke-linecap="round"/>
    <path d="M516,361 Q546,350 578,357 Q581,364 571,367 Q546,371 520,368 Q510,366 516,361 Z" fill="#6b3e26" class="ink"/>
    <path d="M526,358 Q546,354 566,357" fill="none" stroke="#9a6444" stroke-width="2" stroke-linecap="round"/>
    <!-- 车把 -->
    <path d="M720,390 L716,364 C712,352 694,351 672,356" fill="none" stroke="#263041" stroke-width="11" stroke-linecap="round" stroke-linejoin="round"/>
    <path d="M720,390 L716,364 C712,352 694,351 672,356" fill="none" stroke="#c3cad3" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
    <path d="M677,356 L658,360" stroke="#263041" stroke-width="14" stroke-linecap="round"/>
    <path d="M677,356 L658,360" stroke="#5a3a28" stroke-width="9" stroke-linecap="round"/>
    <g id="bell">
      <path d="M692,352 A9.5,9.5 0 0 1 711,352 Z" fill="#e6ebf0" class="ink" style="stroke-width:2.2"/>
      <circle cx="701.5" cy="340" r="2.4" fill="#e6ebf0" class="ink" style="stroke-width:2"/>
      <path d="M697,347 Q700,344 704,345" fill="none" stroke="#fff" stroke-width="1.6" stroke-linecap="round"/>
    </g>
    <!-- 车筐 + 鱼 -->
    <g id="basket">
      <path d="M728,380 L718,377 M738,408 L732,428" stroke="#263041" stroke-width="6" stroke-linecap="round"/>
      <path d="M728,380 L718,377 M738,408 L732,428" stroke="#b4bcc5" stroke-width="3" stroke-linecap="round"/>
      <g id="fishTail"><path d="M748,372 C744,352 750,338 758,331 L750,321 Q760,322 768,325 L763,334 C767,348 764,362 762,372 Z" fill="#7fa7c4" class="ink"/></g>
      <path d="M770,372 C768,352 776,338 790,333 C799,338 800,354 793,372 Z" fill="#9cbad1" class="ink"/>
      <circle cx="789" cy="342" r="2.4" fill="#263041"/>
      <path d="M782,351 Q788,355 795,351" fill="none" stroke="#6b8ca6" stroke-width="2" stroke-linecap="round"/>
      <path d="M728,364 L798,364 L790,408 L736,408 Z" fill="#cf9552" class="ink"/>
      <path d="M731,379 L795,379 M733,394 L793,394 M747,366 L749,407 M763,366 L763,407 M779,366 L777,407" stroke="#9a6432" stroke-width="2" stroke-linecap="round"/>
      <rect x="724" y="360" width="78" height="8" rx="4" fill="#b77a3c" class="ink" style="stroke-width:2.5"/>
    </g>
    <!-- 近侧:曲柄、脚蹬、腿 -->
    <line id="nearCrankO" x1="585" y1="510" x2="585" y2="478" stroke="#263041" stroke-width="11" stroke-linecap="round"/>
    <line id="nearCrank" x1="585" y1="510" x2="585" y2="478" stroke="#c3cad3" stroke-width="6.5" stroke-linecap="round"/>
    <circle cx="585" cy="510" r="5" fill="#8c95a1" class="ink" style="stroke-width:2"/>
    <rect id="nearPedal" x="573" y="475" width="24" height="7" rx="2" fill="#3e434b" class="ink" style="stroke-width:2"/>
    <path id="nearLegO" fill="none" stroke="#263041" stroke-width="16" stroke-linecap="round" stroke-linejoin="round"/>
    <path id="nearLegF" fill="none" stroke="#f28c28" stroke-width="11" stroke-linecap="round" stroke-linejoin="round"/>
    <g id="nearFoot"><path d="M-3,-5 C6,-4 16,-1 25,2 Q30,5 25,8 Q20,8 21,11 Q24,14 19,16 Q15,15 14,16 Q14,20 9,19 L-2,9 Z" fill="#f28c28" class="ink"/></g>
    <g id="pel">
      <!-- 围巾飘带(路径由脚本实时计算) -->
      <path id="scarfB" fill="#0f8a7f" class="ink" style="stroke-width:2.5"/>
      <path id="scarfA" fill="#19b3a3" class="ink" style="stroke-width:2.5"/>
      <!-- 白色身体:先统一描边,再统一填充 -->
      <g class="ol"><use href="#dBody"/><use href="#dNeck"/></g>
      <g class="fl"><use href="#dBody"/><use href="#dNeck"/></g>
      <path d="M470,306 L500,313 M466,320 L500,321" stroke="#c9d4df" stroke-width="2" stroke-linecap="round"/>
      <!-- 围巾 -->
      <path d="M600,244 C620,254 648,254 670,240 L668,260 C646,272 620,270 601,262 Z" fill="#19b3a3" class="ink" style="stroke-width:2.5"/>
      <path d="M616,250 L614,266 M636,253 L635,269 M655,250 L655,265" stroke="#fff" stroke-width="3" stroke-linecap="round" opacity=".85"/>
      <circle cx="603" cy="254" r="7" fill="#19b3a3" class="ink" style="stroke-width:2.5"/>
      <!-- 头部零件:嘴、喉囊、眼、头盔 -->
      <g id="headParts">
        <path d="M690,186 L826,202 L812,213 L700,201 Z" fill="#a8423c"/>
        <g id="pouch">
          <path d="M688,186 C735,188 790,195 826,202 C812,214 790,228 760,236 C725,244 690,236 668,210 Z" fill="url(#gPouch)" class="ink"/>
          <path d="M702,215 C727,227 754,227 782,216" fill="none" stroke="#d9722a" stroke-width="2" stroke-linecap="round" opacity=".7"/>
        </g>
        <path d="M684,158 C730,160 790,172 836,190 Q847,196 840,204 Q834,207 828,200 C790,192 735,187 688,187 Z" fill="url(#gBeak)" class="ink"/>
        <path d="M693,167 C740,171 792,181 829,194" fill="none" stroke="#e39a22" stroke-width="2" stroke-linecap="round"/>
        <path d="M834,191 Q846,196 840,203 Q835,206 830,200 Z" fill="#e8643c"/>
        <g id="eye">
          <ellipse cx="675" cy="168" rx="10" ry="8.5" fill="#ffd9a0"/>
          <circle cx="675" cy="168" r="5.8" fill="#fff4bf" class="ink" style="stroke-width:2"/>
          <circle cx="676.5" cy="168" r="3" fill="#1d1d1f"/>
          <circle cx="677.6" cy="166.8" r="1.1" fill="#fff"/>
        </g>
        <path d="M646,166 Q656,184 668,196" fill="none" stroke="#263041" stroke-width="2"/>
        <path d="M638,168 C633,140 660,127 685,133 C699,137 705,149 701,160 L688,157 C670,154 652,158 638,168 Z" fill="url(#gHelmet)" class="ink"/>
        <path d="M652,145 C662,138 676,136 688,140 M646,156 C658,148 676,146 694,151" fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round" opacity=".85"/>
      </g>
      <!-- 近侧翅膀:握住车把 -->
      <g id="wing">
        <path d="M652,346 Q646,357 634,350 Q626,361 614,350 Q604,359 594,346 Q582,353 574,338 Q561,341 556,322 L560,300 L650,320 Z" fill="#2e2f38" class="ink"/>
        <path d="M560,275 C590,258 630,256 650,280 C662,296 668,318 670,340 L652,346 C640,330 622,320 600,322 C585,324 570,316 556,306 C548,298 548,285 560,275 Z" fill="#fbfcfe" class="ink"/>
        <path d="M580,290 Q606,280 630,292 M592,306 Q616,298 640,312 M606,318 Q626,314 648,328" fill="none" stroke="#c8d3de" stroke-width="2" stroke-linecap="round"/>
        <path d="M648,338 C662,333 681,341 685,354 C687,364 677,369 668,365 C661,362 655,357 649,352 Z" fill="#2e2f38" class="ink"/>
        <path d="M662,342 Q672,349 674,360 M654,346 Q662,352 663,360" fill="none" stroke="#5b5d6b" stroke-width="1.6" stroke-linecap="round"/>
      </g>
    </g>
    </g>
    <g id="fx"></g>
    <g id="bubble" opacity="0">
      <path d="M14,0 H120 A14,14 0 0 1 134,14 V42 A14,14 0 0 1 120,56 H42 L26,72 L28,56 H14 A14,14 0 0 1 0,42 V14 A14,14 0 0 1 14,0 Z" fill="#fff" class="ink"/>
      <text x="67" y="36" text-anchor="middle" font-size="22" font-weight="800" fill="#e8483f" font-family="PingFang SC,Microsoft YaHei,sans-serif">叮铃铃~</text>
    </g>
    </svg>
    <script>
    (() => {
    const NS = 'http://www.w3.org/2000/svg';
    const $ = id => document.getElementById(id);
    function el(tag, attrs, parent) {
      const n = document.createElementNS(NS, tag);
      for (const k in attrs) n.setAttribute(k, attrs[k]);
      if (parent) parent.appendChild(n);
      return n;
    }
    // 固定种子的随机数:每个平铺块内容完全一致,循环滚动无缝
    function rng(seed) {
      return () => (seed = (seed * 16807) % 2147483647) / 2147483647;
    }
    const INK = '#263041';
    
    // ---------- 几何常量 ----------
    const WHEEL_R = 80;
    const RH = { x: 455, y: 500 }, FH = { x: 745, y: 500 }, BB = { x: 585, y: 510 };
    const CRANK = 32;
    
    // ---------- 车轮 ----------
    function buildWheel(g, c) {
      el('circle', { cx: c.x, cy: c.y, r: 80, fill: 'none', stroke: INK, 'stroke-width': 15 }, g);
      el('circle', { cx: c.x, cy: c.y, r: 80, fill: 'none', stroke: '#34343a', 'stroke-width': 10 }, g);
      el('circle', { cx: c.x, cy: c.y, r: 74.5, fill: 'none', stroke: '#56565f', 'stroke-width': 1.5 }, g);
      el('circle', { cx: c.x, cy: c.y, r: 71, fill: 'none', stroke: '#c9d0d8', 'stroke-width': 3.5 }, g);
      const spin = el('g', {}, g);
      for (let i = 0; i < 20; i++) {
        const a = i / 20 * Math.PI * 2, b = a + (i % 2 ? 0.32 : -0.32);
        el('line', {
          x1: c.x + Math.cos(a) * 8, y1: c.y + Math.sin(a) * 8,
          x2: c.x + Math.cos(b) * 70, y2: c.y + Math.sin(b) * 70,
          stroke: '#aeb7c1', 'stroke-width': 1.4
        }, spin);
      }
      el('rect', { x: c.x + 38, y: c.y - 4, width: 13, height: 8, rx: 2, fill: '#ff9f1c', stroke: INK, 'stroke-width': 1.5 }, spin);
      el('rect', { x: c.x - 3, y: c.y - 79, width: 6, height: 8, rx: 1.5, fill: '#9aa3ad' }, spin);
      el('circle', { cx: c.x, cy: c.y, r: 9, fill: '#dfe4ea', stroke: INK, 'stroke-width': 3 }, g);
      return spin;
    }
    const spinR = buildWheel($('wheelR'), RH);
    const spinF = buildWheel($('wheelF'), FH);
    
    // ---------- 视差平铺层 ----------
    const layers = [];
    function layer(id, period, factor, build, drift = 0) {
      const g = $(id);
      const n = Math.ceil(3600 / period) + 1;
      for (let k = 0; k < n; k++) {
        const tile = el('g', { transform: `translate(${-1200 + k * period},0)` }, g);
        build(tile, rng(7919 + period));
      }
      layers.push({ g, period, factor, drift });
    }
    
    function cloud(p, x, y, s) {
      const g = el('g', { transform: `translate(${x},${y}) scale(${s})` }, p);
      el('ellipse', { cx: -4, cy: 20, rx: 72, ry: 11, fill: '#d3e9f7' }, g);
      [[-44, 2, 26], [-12, -16, 36], [24, -6, 30], [52, 6, 20], [-64, 10, 16]]
        .forEach(([cx, cy, r]) => el('circle', { cx, cy, r, fill: '#fff' }, g));
      el('rect', { x: -72, y: 0, width: 132, height: 20, rx: 10, fill: '#fff' }, g);
    }
    layer('lyClouds', 1800, 0.05, (p, r) => {
      for (let i = 0; i < 5; i++) cloud(p, r() * 1800, 40 + r() * 230, 0.55 + r() * 0.7);
    }, 9);
    
    layer('lyIsland', 3000, 0.02, p => {
      const x = 380, lx = 392, b = 371;
      el('path', { d: `M${x - 160},397 Q${x - 95},364 ${x - 20},370 Q${x + 55},358 ${x + 140},397 Z`, fill: '#8fbab3' }, p);
      el('path', { d: `M${x - 110},397 Q${x - 60},381 ${x},385 Q${x + 42},379 ${x + 95},397 Z`, fill: '#77a69f' }, p);
      el('path', { d: `M${lx},${b - 57} L${lx + 190},${b - 86} L${lx + 190},${b - 30} Z`, fill: '#fff7c2', class: 'beam' }, p);
      el('path', { d: `M${lx - 9},${b} L${lx - 6},${b - 50} L${lx + 6},${b - 50} L${lx + 9},${b} Z`, fill: '#fff', stroke: '#5d6b7a', 'stroke-width': 1.2 }, p);
      el('path', { d: `M${lx - 8.2},${b - 12} L${lx - 7.5},${b - 23} L${lx + 7.5},${b - 23} L${lx + 8.2},${b - 12} Z M${lx - 6.9},${b - 34} L${lx - 6.4},${b - 44} L${lx + 6.4},${b - 44} L${lx + 6.9},${b - 34} Z`, fill: '#e8483f' }, p);
      el('rect', { x: lx - 6.5, y: b - 60, width: 13, height: 10, fill: '#ffe89a', stroke: '#5d6b7a', 'stroke-width': 1.2 }, p);
      el('path', { d: `M${lx - 9},${b - 60} L${lx},${b - 68} L${lx + 9},${b - 60} Z`, fill: '#e8483f' }, p);
    });
    
    layer('lyBoat', 2200, 0.035, p => {
      const x = 1350, y = 432;
      const g = el('g', { class: 'bob' }, p);
      el('path', { d: `M${x - 24},${y} L${x + 24},${y} L${x + 17},${y + 9} L${x - 17},${y + 9} Z`, fill: '#e8483f', stroke: INK, 'stroke-width': 1.8 }, g);
      el('path', { d: `M${x - 2},${y - 4} L${x - 2},${y - 44} L${x - 22},${y - 5} Z`, fill: '#fff', stroke: INK, 'stroke-width': 1.8 }, g);
      el('path', { d: `M${x + 2},${y - 4} L${x + 2},${y - 36} L${x + 19},${y - 5} Z`, fill: '#ffd35c', stroke: INK, 'stroke-width': 1.8 }, g);
    }, -4);
    
    layer('lySpark', 700, 0.08, (p, r) => {
      for (let i = 0; i < 16; i++) {
        el('rect', {
          x: r() * 700, y: 402 + r() * 70, width: 5 + r() * 12, height: 2, rx: 1, fill: '#fff',
          class: 'spark', style: `animation-delay:${(-r() * 2.6).toFixed(2)}s`
        }, p);
      }
      for (let i = 0; i < 8; i++) {
        const x = r() * 680, y = 415 + r() * 58;
        el('path', { d: `M${x},${y} q5,-3 10,0 q5,-3 10,0`, fill: 'none', stroke: '#e6f6ff', 'stroke-width': 1.6, opacity: 0.55 }, p);
      }
    });
    
    layer('lyFoam', 120, 0.3, p => {
      el('path', { d: 'M0,481 q15,-7 30,0 t30,0 t30,0 t30,0 V489 H0 Z', fill: '#fff', opacity: 0.85 }, p);
    });
    
    function umbrella(p, x, y, c1, c2, tilt) {
      const g = el('g', { transform: `translate(${x},${y}) rotate(${tilt})` }, p);
      el('line', { x1: 0, y1: 0, x2: 0, y2: -84, stroke: '#6b4a2f', 'stroke-width': 3 }, g);
      el('path', { d: 'M-46,-60 Q-42,-94 0,-96 Q42,-94 46,-60 Z', fill: c1, stroke: INK, 'stroke-width': 2 }, g);
      el('path', { d: 'M-31,-60 L0,-96 L-11,-60 Z M11,-60 L0,-96 L31,-60 Z', fill: c2 }, g);
      el('path', { d: 'M-46,-60 Q-42,-94 0,-96 Q42,-94 46,-60 Z', fill: 'none', stroke: INK, 'stroke-width': 2 }, g);
    }
    layer('lyBeach', 1400, 0.4, p => {
      el('path', { d: 'M150,512 L232,512 L246,526 L164,526 Z', fill: '#ff7eb6' }, p);
      el('path', { d: 'M170,512 L184,526 M196,512 L210,526', stroke: '#fff', 'stroke-width': 4 }, p);
      umbrella(p, 200, 512, '#ff5a5f', '#fff', -6);
      el('path', { d: 'M520,515 L520,494 L528,494 L528,500 L536,500 L536,490 L544,490 L544,500 L552,500 L552,494 L560,494 L560,515 Z', fill: '#e2bb78', stroke: '#b98d49', 'stroke-width': 1.5 }, p);
      el('path', { d: 'M540,490 L540,476 L550,480 L540,484', fill: '#ff5a5f', stroke: '#b98d49', 'stroke-width': 1 }, p);
      umbrella(p, 860, 510, '#2f7de1', '#ffd35c', 5);
      const bx = 1080, by = 504;
      el('circle', { cx: bx, cy: by, r: 11, fill: '#fff', stroke: INK, 'stroke-width': 1.8 }, p);
      el('path', { d: `M${bx - 11},${by} A11,11 0 0 1 ${bx},${by - 11} L${bx},${by} Z M${bx + 11},${by} A11,11 0 0 1 ${bx},${by + 11} L${bx},${by} Z`, fill: '#ff5a5f' }, p);
    });
    
    layer('lyRail', 110, 0.85, p => {
      el('rect', { x: 0, y: 497, width: 10, height: 56, rx: 2, fill: '#8a5c38', stroke: INK, 'stroke-width': 1.2 }, p);
      el('rect', { x: -2, y: 493, width: 14, height: 6, rx: 2, fill: '#a8764b', stroke: INK, 'stroke-width': 1.2 }, p);
    });
    
    layer('lyPlanks', 36, 1, p => {
      el('line', { x1: 0, y1: 556, x2: -12, y2: 636, stroke: '#9f6d42', 'stroke-width': 2 }, p);
      el('line', { x1: 2.5, y1: 556, x2: -9.5, y2: 636, stroke: '#e6b37e', 'stroke-width': 1, opacity: 0.6 }, p);
    });
    
    layer('lyPiles', 150, 1.02, p => {
      el('rect', { x: 0, y: 650, width: 16, height: 24, fill: '#6e4526' }, p);
      el('ellipse', { cx: 8, cy: 675, rx: 14, ry: 3, fill: '#5b3a1e', opacity: 0.2 }, p);
    });
    
    layer('lyFg', 900, 1.35, (p, r) => {
      for (let i = 0; i < 7; i++) {
        const x = r() * 900, y = 668 + r() * 36, s = 0.7 + r() * 0.6;
        el('path', {
          d: `M${x},${y} q${-6 * s},${-16 * s} ${-15 * s},${-24 * s} M${x},${y} q0,${-20 * s} ${3 * s},${-34 * s} M${x},${y} q${7 * s},${-14 * s} ${16 * s},${-20 * s}`,
          fill: 'none', stroke: i % 2 ? '#7fae5a' : '#6c9a49', 'stroke-width': 3.2, 'stroke-linecap': 'round'
        }, p);
      }
      for (let i = 0; i < 4; i++) {
        const x = r() * 900, y = 690 + r() * 30;
        el('path', { d: `M${x},${y} l-9,-8 q9,-9 18,0 Z`, fill: '#ffd6c4', stroke: '#d59a86', 'stroke-width': 1.3 }, p);
      }
    });
    
    // ---------- 海鸥 ----------
    const gulls = [0, 1, 2].map(i => ({
      el: el('path', { fill: 'none', stroke: '#3b4f63', 'stroke-width': 2.4, 'stroke-linecap': 'round', 'stroke-linejoin': 'round' }, $('lyGulls')),
      x: 150 + i * 480, y0: 90 + i * 55, s: 1 - i * 0.18, ph: i * 2.1
    }));
    
    // ---------- 特效:风线、扬沙 ----------
    const fxG = $('fx');
    const streaks = Array.from({ length: 9 }, () => ({
      el: el('line', { stroke: '#fff', 'stroke-width': 2.2, 'stroke-linecap': 'round', opacity: 0 }, fxG),
      x: -999, y: 0, len: 0
    }));
    const dust = Array.from({ length: 16 }, () => ({
      el: el('circle', { r: 0, fill: '#e9c890', opacity: 0 }, fxG),
      x: 0, y: 0, vx: 0, vy: 0, life: 0, max: 1
    }));
    let dustTimer = 0;
    
    // ---------- 状态 ----------
    const BASE_V = 250, RATIO = 0.62, LEG_A = 95, LEG_B = 102;
    let speed = 1, targetSpeed = 1, dist = 0, wheelAng = 0, crankAng = -Math.PI / 2;
    let chainOff = 0, time = 0, last = performance.now();
    let nextBlink = 2, blinkT = -1, ringT = -1, audioCtx = null, shownOdo = -1;
    
    const pel = $('pel'), dNeck = $('dNeck'), headParts = $('headParts');
    const pouch = $('pouch'), eye = $('eye'), fishTail = $('fishTail');
    const ringEl = $('ring'), chain = $('chain'), bell = $('bell'), bubble = $('bubble');
    const scarfA = $('scarfA'), scarfB = $('scarfB');
    const f1 = n => n.toFixed(1);
    
    // 两段腿的反向运动学:已知髋和踝,求膝盖(膝盖朝前)
    function knee(h, p) {
      const dx = p.x - h.x, dy = p.y - h.y;
      const d = Math.min(Math.max(Math.hypot(dx, dy), Math.abs(LEG_A - LEG_B) + 1), LEG_A + LEG_B - 0.5);
      const k = Math.atan2(dy, dx) - Math.acos((LEG_A * LEG_A + d * d - LEG_B * LEG_B) / (2 * LEG_A * d));
      return { x: h.x + LEG_A * Math.cos(k), y: h.y + LEG_A * Math.sin(k) };
    }
    function pose(side, hip, ang) {
      const px = BB.x + CRANK * Math.cos(ang), py = BB.y + CRANK * Math.sin(ang);
      const ank = { x: px - 5, y: py - 9 }, kn = knee(hip, ank);
      const d = `M${f1(hip.x)},${f1(hip.y)} L${f1(kn.x)},${f1(kn.y)} L${f1(ank.x)},${f1(ank.y)}`;
      $(side + 'LegO').setAttribute('d', d);
      $(side + 'LegF').setAttribute('d', d);
      $(side + 'Foot').setAttribute('transform', `translate(${f1(ank.x)},${f1(ank.y)}) rotate(${f1(6 + 14 * Math.sin(ang))})`);
      const cranks = side === 'near' ? ['nearCrank', 'nearCrankO'] : ['farCrank'];
      cranks.forEach(id => { $(id).setAttribute('x2', f1(px)); $(id).setAttribute('y2', f1(py)); });
      const ped = $(side + 'Pedal');
      ped.setAttribute('x', f1(px - 12));
      ped.setAttribute('y', f1(py - 3.5));
    }
    
    // 围巾飘带:沿中心线叠加行进波
    function ribbon(x0, y0, len, w, ph) {
      const N = 14, top = [], bot = [];
      const droop = 34 / (0.6 + speed), amp = 3 + 4 * Math.min(speed, 2.5), fr = 5 + 7 * speed;
      for (let i = 0; i <= N; i++) {
        const u = i / N, x = x0 - u * len * (0.75 + 0.1 * Math.min(speed, 2.5));
        const y = y0 + droop * u * u + amp * u * Math.sin(u * 5.5 - time * fr + ph);
        const hw = w * (1 - 0.3 * u);
        top.push(`${f1(x)},${f1(y - hw)}`);
        bot.unshift(`${f1(x)},${f1(y + hw)}`);
      }
      const [ex, ey] = top[N].split(',').map(Number);
      return `M${top.join(' L')} L${f1(ex + 7)},${f1(ey + w * 0.7)} L${bot.join(' L')} Z`;
    }
    
    // ---------- 铃铛 ----------
    function ding() {
      try {
        audioCtx = audioCtx || new (window.AudioContext || window.webkitAudioContext)();
        const t0 = audioCtx.currentTime;
        [0, 0.17].forEach((delay, i) => {
          [[2093, 0.2], [2637, 0.08], [4186, 0.04]].forEach(([f, vol]) => {
            const o = audioCtx.createOscillator(), g = audioCtx.createGain();
            o.type = 'sine';
            o.frequency.value = f * (i ? 1.03 : 1);
            g.gain.setValueAtTime(0.0001, t0 + delay);
            g.gain.exponentialRampToValueAtTime(vol, t0 + delay + 0.005);
            g.gain.exponentialRampToValueAtTime(0.0001, t0 + delay + 0.9);
            o.connect(g).connect(audioCtx.destination);
            o.start(t0 + delay);
            o.stop(t0 + delay + 0.95);
          });
        });
      } catch (e) { /* 无音频也照样动画 */ }
    }
    function ringBell() { ringT = 0; ding(); }
    
    // ---------- 交互 ----------
    const spd = $('spd'), spdVal = $('spdVal');
    function setSpeed(v) {
      targetSpeed = Math.max(0, Math.min(2.5, v));
      spd.value = Math.round(targetSpeed * 100);
      spdVal.textContent = targetSpeed.toFixed(1) + '×';
    }
    spd.addEventListener('input', () => setSpeed(spd.value / 100));
    $('bellBtn').addEventListener('click', ringBell);
    $('scene').addEventListener('click', ringBell);
    addEventListener('keydown', e => {
      if (e.code === 'Space') { e.preventDefault(); ringBell(); }
      else if (e.key === 'ArrowRight' || e.key === 'ArrowUp') setSpeed(targetSpeed + 0.2);
      else if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') setSpeed(targetSpeed - 0.2);
    });
    if (matchMedia('(prefers-reduced-motion: reduce)').matches) { speed = 0.6; setSpeed(0.6); }
    
    // 风线放在鹈鹕后面,避免划过角色
    const backFx = el('g', {});
    $('rig').before(backFx);
    streaks.forEach(s => backFx.appendChild(s.el));
    const odo = $('odo');
    const easeBack = x => 1 + 2.70158 * Math.pow(x - 1, 3) + 1.70158 * Math.pow(x - 1, 2);
    
    // ---------- 主循环 ----------
    function frame(now) {
      const dt = Math.min(0.05, (now - last) / 1000);
      last = now;
      time += dt;
      speed += (targetSpeed - speed) * Math.min(1, dt * 3);
      const v = BASE_V * speed, dW = v / WHEEL_R * dt, dC = dW * RATIO;
      dist += v * dt;
      wheelAng += dW;
      crankAng += dC;
      chainOff -= dC * 22;
    
      // 视差背景
      for (const L of layers) {
        const off = ((dist * L.factor + time * L.drift) % L.period + L.period) % L.period;
        L.g.setAttribute('transform', `translate(${(-off).toFixed(2)},0)`);
      }
    
      // 车轮、牙盘、链条
      const wd = f1(wheelAng * 180 / Math.PI % 360);
      spinR.setAttribute('transform', `rotate(${wd} ${RH.x} ${RH.y})`);
      spinF.setAttribute('transform', `rotate(${wd} ${FH.x} ${FH.y})`);
      ringEl.setAttribute('transform', `rotate(${f1(crankAng * 180 / Math.PI % 360)} ${BB.x} ${BB.y})`);
      chain.setAttribute('stroke-dashoffset', (chainOff % 5).toFixed(2));
    
      // 身体随踩踏起伏,两条腿跟着脚蹬走
      const act = Math.min(1, speed);
      const bob = 1.4 * Math.sin(2 * crankAng) * act;
      pel.setAttribute('transform', `translate(0,${bob.toFixed(2)})`);
      pose('far', { x: 556, y: 341 + bob }, crankAng + Math.PI);
      pose('near', { x: 565, y: 345 + bob }, crankAng);
    
      // 头颈:踩一下点一下头,骑得越快越往前探
      const lean = Math.max(-3, Math.min(8, (speed - 1) * 5));
      const nod = 2 * Math.sin(2 * crankAng + 0.9) * act + lean + 1.2 * Math.sin(time * 0.7);
      const ht = `rotate(${nod.toFixed(2)} 630 275)`;
      dNeck.setAttribute('transform', ht);
      headParts.setAttribute('transform', ht);
    
      // 按铃:铃铛摇晃、张嘴、冒气泡
      let open = 0;
      if (ringT >= 0) {
        ringT += dt;
        const w = Math.sin(ringT * 55) * 16 * Math.exp(-ringT * 4);
        bell.setAttribute('transform', `rotate(${w.toFixed(2)} 701.5 352)`);
        open = ringT < 0.8 ? Math.sin(Math.PI * ringT / 0.8) : 0;
        const s = ringT < 0.22 ? easeBack(ringT / 0.22) : 1;
        const op = ringT < 1.3 ? 1 : Math.max(0, 1 - (ringT - 1.3) / 0.3);
        bubble.setAttribute('opacity', op.toFixed(2));
        bubble.setAttribute('transform', `translate(716,${f1(342 - 8 * ringT)}) scale(${Math.max(0.01, s).toFixed(3)}) translate(-26,-72)`);
        if (ringT > 1.6) {
          ringT = -1;
          bubble.setAttribute('opacity', 0);
          bell.removeAttribute('transform');
        }
      }
      const jig = 1 + 0.07 * Math.sin(2 * crankAng + 1.8) * act + 0.03 * Math.sin(time * 3.1);
      pouch.setAttribute('transform', `rotate(${f1(open * 10)} 690 188) translate(750 196) scale(1 ${jig.toFixed(3)}) translate(-750 -196)`);
    
      // 眨眼
      nextBlink -= dt;
      if (nextBlink <= 0) { blinkT = 0; nextBlink = 2.5 + Math.random() * 3; }
      let ey = 1;
      if (blinkT >= 0) {
        blinkT += dt;
        ey = Math.max(0.08, Math.abs(1 - blinkT / 0.07));
        if (blinkT > 0.14) { blinkT = -1; ey = 1; }
      }
      eye.setAttribute('transform', `translate(675 168) scale(1 ${ey.toFixed(2)}) translate(-675 -168)`);
    
      // 车筐里的鱼甩尾巴、围巾飘动
      fishTail.setAttribute('transform', `rotate(${f1(Math.sin(time * 7) * 7)} 755 368)`);
      scarfA.setAttribute('d', ribbon(601, 252, 96, 7, 0));
      scarfB.setAttribute('d', ribbon(601, 257, 76, 6, 1.9));
    
      // 海鸥
      for (const g of gulls) {
        g.x -= (v * 0.1 - 22) * dt;
        if (g.x < -420) g.x += 2000; else if (g.x > 1580) g.x -= 2000;
        const fl = Math.sin(time * 7 + g.ph), s = g.s, y = g.y0 + 8 * Math.sin(time * 0.9 + g.ph);
        const wy = (-5 - 7 * fl) * s;
        g.el.setAttribute('d', `M${f1(g.x - 14 * s)},${f1(y + 2 * s)} Q${f1(g.x - 7 * s)},${f1(y + wy)} ${f1(g.x)},${f1(y)} Q${f1(g.x + 7 * s)},${f1(y + wy)} ${f1(g.x + 14 * s)},${f1(y + 2 * s)}`);
      }
    
      // 风线:速度越快越明显
      const sOp = Math.max(0, Math.min(0.75, (speed - 0.9) / 1.2)).toFixed(2);
      for (const s of streaks) {
        s.x -= v * 2.4 * dt;
        if (s.x + s.len < -300) {
          s.x = 900 + Math.random() * 700;
          s.y = 110 + Math.random() * 380;
          s.len = 40 + Math.random() * 90;
        }
        s.el.setAttribute('x1', f1(s.x));
        s.el.setAttribute('x2', f1(s.x + s.len));
        s.el.setAttribute('y1', f1(s.y));
        s.el.setAttribute('y2', f1(s.y));
        s.el.setAttribute('opacity', sOp);
      }
    
      // 后轮扬起的沙粒
      dustTimer -= dt * speed;
      if (speed > 0.15 && dustTimer <= 0) {
        dustTimer = 0.09;
        const p = dust.find(d => d.life <= 0);
        if (p) {
          p.x = RH.x - 32 + Math.random() * 12;
          p.y = 578;
          p.vx = -(30 + Math.random() * 50) - v * 0.3;
          p.vy = -(12 + Math.random() * 22);
          p.max = p.life = 0.6 + Math.random() * 0.4;
        }
      }
      for (const p of dust) {
        if (p.life <= 0) continue;
        p.life -= dt;
        p.x += p.vx * dt;
        p.y += p.vy * dt;
        p.vy += 25 * dt;
        const k = Math.min(1, 1 - p.life / p.max);
        p.el.setAttribute('cx', f1(p.x));
        p.el.setAttribute('cy', f1(p.y));
        p.el.setAttribute('r', f1(3 + 7 * k));
        p.el.setAttribute('opacity', p.life > 0 ? (0.5 * (1 - k)).toFixed(2) : 0);
      }
    
      // 里程
      const m = Math.floor(dist / 100);
      if (m !== shownOdo) { shownOdo = m; odo.textContent = m; }
    
      requestAnimationFrame(frame);
    }
    requestAnimationFrame(frame);
    })();
    </script>
    </body>
    </html>
    主楼
  • coolb

    铁汁,能不能直接上图。。。。

    #1
  • 隔壁老陈

    emmmm不是说了吗,效果真的不是截图能感受到的,懒得做动图了。

    #2
  • Myth

    neo啥时候支持个html站内渲染

    #3
  • 隔壁老陈

    别说了,我本来想直接把html文件发上来,结果,还不让上传。

    #4
  • 隔壁老陈

    也可以直接去看哈雷佬打包的html附件,震惊围坐 Opus 5.5

    #5
  • 哈雷彗星

    发到 CodePen 就可以嵌 iframe

    不做不可信站点的渲染是因为 安全

    #6
  • Myth

    有没有成功案例的参考啊

    试着嵌入了2.0和classic都不生效

    #7

发表回复

登录后回复