logo NodeSeekbeta

gemini-4-argon 鹈鹕测试

感觉还行吧
image

svg源码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>鹈鹕骑单车 · Pelican on a Bicycle (SVG 2D Animation)</title>
  <style>
    :root {
      --bg-page: #0f172a;
      --panel-bg: rgba(15, 23, 42, 0.78);
      --panel-border: rgba(255, 255, 255, 0.14);
      --text-main: #f8fafc;
      --text-sub: #94a3b8;
      --accent: #f97316;
      --accent-hover: #fb923c;
    }

    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      user-select: none;
    }

    body {
      min-height: 100vh;
      background: radial-gradient(circle at 50% 20%, #1e293b 0%, #090d16 100%);
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Microsoft YaHei", sans-serif;
      color: var(--text-main);
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      padding: 20px;
      overflow: hidden;
    }

    .stage-wrapper {
      position: relative;
      width: min(1060px, 96vw);
      aspect-ratio: 16 / 10;
      border-radius: 24px;
      overflow: hidden;
      box-shadow:
        0 30px 70px -15px rgba(0, 0, 0, 0.7),
        0 0 0 1px rgba(255, 255, 255, 0.12);
      background: #85d7ff;
    }

    svg#scene {
      width: 100%;
      height: 100%;
      display: block;
    }

    /* 顶部标题角标 */
    .hud-header {
      position: absolute;
      top: 18px;
      left: 22px;
      display: flex;
      align-items: center;
      gap: 12px;
      padding: 8px 16px;
      border-radius: 999px;
      background: var(--panel-bg);
      backdrop-filter: blur(12px);
      border: 1px solid var(--panel-border);
      pointer-events: none;
    }

    .hud-dot {
      width: 10px;
      height: 10px;
      border-radius: 50%;
      background: #22c55e;
      box-shadow: 0 0 10px #22c55e;
      animation: pulse 2s infinite;
    }

    @keyframes pulse {
      0%, 100% { transform: scale(1); opacity: 1; }
      50% { transform: scale(1.25); opacity: 0.7; }
    }

    .hud-title {
      font-size: 14px;
      font-weight: 600;
      letter-spacing: 0.4px;
    }

    .hud-stats {
      font-size: 12px;
      color: var(--text-sub);
      font-variant-numeric: tabular-nums;
      border-left: 1px solid rgba(255, 255, 255, 0.15);
      padding-left: 12px;
    }

    /* 底部交互控制栏 */
    .controls-bar {
      position: absolute;
      bottom: 18px;
      left: 50%;
      transform: translateX(-50%);
      display: flex;
      align-items: center;
      gap: 14px;
      padding: 10px 18px;
      border-radius: 999px;
      background: var(--panel-bg);
      backdrop-filter: blur(14px);
      border: 1px solid var(--panel-border);
      box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
      flex-wrap: wrap;
      justify-content: center;
      max-width: 94%;
    }

    .ctrl-group {
      display: flex;
      align-items: center;
      gap: 8px;
      font-size: 13px;
      color: var(--text-main);
    }

    .ctrl-label {
      color: var(--text-sub);
      font-size: 12px;
      font-weight: 500;
    }

    input[type="range"] {
      -webkit-appearance: none;
      appearance: none;
      width: 110px;
      height: 6px;
      background: rgba(255, 255, 255, 0.18);
      border-radius: 999px;
      outline: none;
      cursor: pointer;
    }

    input[type="range"]::-webkit-slider-thumb {
      -webkit-appearance: none;
      appearance: none;
      width: 16px;
      height: 16px;
      border-radius: 50%;
      background: var(--accent);
      border: 2px solid #fff;
      box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
      transition: transform 0.15s ease;
    }

    input[type="range"]::-webkit-slider-thumb:hover {
      transform: scale(1.18);
    }

    .btn {
      appearance: none;
      border: 1px solid rgba(255, 255, 255, 0.14);
      background: rgba(255, 255, 255, 0.08);
      color: var(--text-main);
      font-size: 13px;
      font-weight: 500;
      padding: 6px 13px;
      border-radius: 999px;
      cursor: pointer;
      display: inline-flex;
      align-items: center;
      gap: 6px;
      transition: all 0.18s ease;
    }

    .btn:hover {
      background: rgba(255, 255, 255, 0.18);
      transform: translateY(-1px);
    }

    .btn:active {
      transform: translateY(0);
    }

    .btn-primary {
      background: linear-gradient(135deg, #f97316, #ea580c);
      border-color: rgba(255, 255, 255, 0.25);
    }

    .btn-primary:hover {
      background: linear-gradient(135deg, #fb923c, #f97316);
    }

    .divider {
      width: 1px;
      height: 20px;
      background: rgba(255, 255, 255, 0.14);
    }

    .speed-val {
      min-width: 34px;
      text-align: right;
      font-variant-numeric: tabular-nums;
      font-weight: 600;
      color: #fdba74;
    }
  </style>
</head>
<body>

  <div class="stage-wrapper">
    <!-- 顶部状态条 -->
    <div class="hud-header">
      <span class="hud-dot"></span>
      <span class="hud-title">海滨骑行鹈鹕 · SVG 2D 实时骨骼动画</span>
      <span class="hud-stats" id="hudTelemetry">时速 22 km/h · 踏频 75 RPM</span>
    </div>

    <!-- 主 SVG 画布 -->
    <svg id="scene" viewBox="0 0 1000 625" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <!-- 天空渐变(白天/黄昏/夜晚可动态切换) -->
        <linearGradient id="skyGrad" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop id="skyStop1" offset="0%" stop-color="#38bdf8" />
          <stop id="skyStop2" offset="58%" stop-color="#bae6fd" />
          <stop id="skyStop3" offset="100%" stop-color="#fef3c7" />
        </linearGradient>

        <!-- 海洋渐变 -->
        <linearGradient id="seaGrad" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop id="seaStop1" offset="0%" stop-color="#0284c7" />
          <stop id="seaStop2" offset="100%" stop-color="#0369a1" />
        </linearGradient>

        <!-- 公路渐变 -->
        <linearGradient id="roadGrad" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" stop-color="#334155" />
          <stop offset="100%" stop-color="#1e293b" />
        </linearGradient>

        <!-- 鹈鹕大喉囊渐变 -->
        <linearGradient id="pouchGrad" x1="0%" y1="0%" x2="100%" y2="100%">
          <stop offset="0%" stop-color="#fbbf24" />
          <stop offset="55%" stop-color="#f59e0b" />
          <stop offset="100%" stop-color="#ea580c" />
        </linearGradient>

        <!-- 鹈鹕上喙渐变 -->
        <linearGradient id="upperBeakGrad" x1="0%" y1="0%" x2="100%" y2="0%">
          <stop offset="0%" stop-color="#fde047" />
          <stop offset="70%" stop-color="#fb923c" />
          <stop offset="100%" stop-color="#ef4444" />
        </linearGradient>

        <!-- 羽毛身体立体渐变 -->
        <linearGradient id="bodyFeatherGrad" x1="15%" y1="5%" x2="85%" y2="95%">
          <stop offset="0%" stop-color="#ffffff" />
          <stop offset="70%" stop-color="#f1f5f9" />
          <stop offset="100%" stop-color="#cbd5e1" />
        </linearGradient>

        <!-- 翅膀阴影渐变 -->
        <linearGradient id="wingGrad" x1="0%" y1="0%" x2="100%" y2="100%">
          <stop offset="0%" stop-color="#ffffff" />
          <stop offset="65%" stop-color="#e2e8f0" />
          <stop offset="100%" stop-color="#94a3b8" />
        </linearGradient>

        <!-- 车架烤漆渐变 -->
        <linearGradient id="frameGrad" x1="0%" y1="0%" x2="100%" y2="100%">
          <stop offset="0%" stop-color="#ef4444" />
          <stop offset="50%" stop-color="#dc2626" />
          <stop offset="100%" stop-color="#991b1b" />
        </linearGradient>

        <!-- 金属光泽渐变 -->
        <linearGradient id="chromeGrad" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" stop-color="#f8fafc" />
          <stop offset="50%" stop-color="#94a3b8" />
          <stop offset="100%" stop-color="#475569" />
        </linearGradient>

        <!-- 太阳发光滤镜 -->
        <filter id="sunGlow" x="-40%" y="-40%" width="180%" height="180%">
          <feGaussianBlur stdDeviation="14" result="blur" />
          <feMerge>
            <feMergeNode in="blur" />
            <feMergeNode in="SourceGraphic" />
          </feMerge>
        </filter>

        <!-- 柔和投影 -->
        <filter id="softShadow" x="-15%" y="-15%" width="130%" height="130%">
          <feDropShadow dx="0" dy="4" stdDeviation="4" flood-color="#0f172a" flood-opacity="0.22" />
        </filter>

        <!-- 单个车轮复用组件(圆心在 0,0) -->
        <g id="bikeWheel">
          <!-- 外胎阴影底 -->
          <circle r="92" fill="none" stroke="#0f172a" stroke-width="15" />
          <!-- 复古米白胎壁 -->
          <circle r="83" fill="none" stroke="#fef3c7" stroke-width="5" />
          <!-- 铝合金轮圈 -->
          <circle r="79" fill="none" stroke="url(#chromeGrad)" stroke-width="4" />
          <!-- 辐条组 (16根交织) -->
          <g stroke="#cbd5e1" stroke-width="1.6" stroke- opacity="0.85">
            <line x1="-78" y1="0" x2="78" y2="0" />
            <line x1="0" y1="-78" x2="0" y2="78" />
            <line x1="-55.1" y1="-55.1" x2="55.1" y2="55.1" />
            <line x1="-55.1" y1="55.1" x2="55.1" y2="-55.1" />
            <line x1="-72" y1="-29.8" x2="72" y2="29.8" />
            <line x1="-29.8" y1="-72" x2="29.8" y2="72" />
            <line x1="-72" y1="29.8" x2="72" y2="-29.8" />
            <line x1="-29.8" y1="72" x2="29.8" y2="-72" />
          </g>
          <!-- 气嘴与反光片(让旋转清晰可见) -->
          <rect x="-4" y="-76" width="8" height="11" rx="2" fill="#f59e0b" />
          <circle cx="52" cy="30" r="5.5" fill="#fb923c" stroke="#fff" stroke-width="1.5" />
          <circle cx="-52" cy="-30" r="5.5" fill="#38bdf8" stroke="#fff" stroke-width="1.5" />
        </g>
      </defs>

      <!-- ================= 1. 背景天空与天体 ================= -->
      <rect width="1000" height="470" fill="url(#skyGrad)" />

      <!-- 星星层(夜晚模式显现) -->
      <g id="starsLayer" opacity="0" fill="#fff">
        <circle cx="110" cy="65" r="1.8" />
        <circle cx="240" cy="40" r="2.2" />
        <circle cx="380" cy="90" r="1.5" />
        <circle cx="520" cy="50" r="2.4" />
        <circle cx="670" cy="78" r="1.6" />
        <circle cx="880" cy="48" r="2.1" />
        <circle cx="930" cy="120" r="1.5" />
        <circle cx="160" cy="145" r="1.4" />
        <circle cx="760" cy="135" r="2.0" />
      </g>

      <!-- 太阳 / 月亮 -->
      <g id="celestialBody" transform="translate(820, 110)" filter="url(#sunGlow)">
        <circle id="sunHalo" r="56" fill="#fef08a" opacity="0.35" />
        <circle id="sunCore" r="40" fill="#fde047" />
      </g>

      <!-- 远景流云层 -->
      <g id="cloudsLayer" opacity="0.88" fill="#ffffff">
        <g class="cloud" data-x="120" data-y="85" data-speed="0.18" data-scale="0.9">
          <path d="M0,30 Q10,5 38,12 Q55,-10 85,8 Q115,2 125,26 Q142,30 138,45 L-10,45 Q-15,32 0,30 Z" />
        </g>
        <g class="cloud" data-x="480" data-y="55" data-speed="0.12" data-scale="1.15">
          <path d="M0,30 Q10,5 38,12 Q55,-10 85,8 Q115,2 125,26 Q142,30 138,45 L-10,45 Q-15,32 0,30 Z" />
        </g>
        <g class="cloud" data-x="860" data-y="105" data-speed="0.22" data-scale="0.75">
          <path d="M0,30 Q10,5 38,12 Q55,-10 85,8 Q115,2 125,26 Q142,30 138,45 L-10,45 Q-15,32 0,30 Z" />
        </g>
      </g>

      <!-- 远方海鸥剪影 -->
      <g id="seagullsLayer" stroke="#475569" stroke-width="2.2" fill="none" stroke-linecap="round" opacity="0.55">
        <path id="gull1" d="M180,150 Q192,138 202,148 Q212,138 224,150" />
        <path id="gull2" d="M235,168 Q244,159 252,166 Q260,159 269,168" />
      </g>

      <!-- ================= 2. 远山岛屿与海洋层 ================= -->
      <!-- 远景海岛与灯塔 -->
      <g id="islandLayer">
        <path d="M-40,385 Q90,310 240,360 Q360,335 470,385 Z" fill="#64748b" opacity="0.38" />
        <path d="M620,385 Q770,295 920,350 Q1010,330 1060,385 Z" fill="#475569" opacity="0.35" />
      </g>

      <!-- 蔚蓝大海 -->
      <rect y="380" width="1000" height="90" fill="url(#seaGrad)" />

      <!-- 滚动海浪纹与远景帆船 -->
      <g id="seaWavesLayer">
        <!-- 远景小帆船 -->
        <g id="sailboat" transform="translate(260, 376)">
          <polygon points="0,0 18,-36 22,0" fill="#f8fafc" />
          <polygon points="25,0 25,-30 40,0" fill="#fed7aa" />
          <path d="M-4,2 L44,2 L38,10 L2,10 Z" fill="#9a3412" />
        </g>
        <!-- 波浪线条 -->
        <path id="waveLine1" d="M-100,402 Q-50,395 0,402 T100,402 T200,402 T300,402 T400,402 T500,402 T600,402 T700,402 T800,402 T900,402 T1000,402 T1100,402"
              fill="none" stroke="#7dd3fc" stroke-width="2.5" opacity="0.55" />
        <path id="waveLine2" d="M-100,425 Q-50,418 0,425 T100,425 T200,425 T300,425 T400,425 T500,425 T600,425 T700,425 T800,425 T900,425 T1000,425 T1100,425"
              fill="none" stroke="#bae6fd" stroke-width="2" opacity="0.4" />
      </g>

      <!-- ================= 3. 海滨步道与棕榈树视差层 ================= -->
      <!-- 沙滩缓冲带 -->
      <path d="M0,445 Q250,438 500,445 T1000,443 L1000,480 L0,480 Z" fill="#fde68a" />

      <!-- 视差移动景物(棕榈树 & 海滨里程桩) -->
      <g id="sceneryLayer"></g>

      <!-- 木质海滨护栏柱 -->
      <g id="railingLayer"></g>

      <!-- ================= 4. 柏油单车道 ================= -->
      <rect y="476" width="1000" height="149" fill="url(#roadGrad)" />
      <!-- 路肩石缘 -->
      <rect y="472" width="1000" height="8" fill="#cbd5e1" />
      <rect y="472" width="1000" height="3" fill="#f8fafc" />
      <!-- 车道中线(动态滚动) -->
      <g id="roadDashes" fill="#facc15" opacity="0.9"></g>
      <!-- 路面细颗粒掠过线 -->
      <g id="roadPebbles" stroke="#64748b" stroke-width="2" stroke-linecap="round" opacity="0.55"></g>

      <!-- ================= 5. 主角:鹈鹕与自行车总成 ================= -->
      <!-- 车下地面动态阴影 -->
      <ellipse id="groundShadow" cx="500" cy="542" rx="235" ry="15" fill="#090d16" opacity="0.42" />

      <!-- 整车与角色主容器(带动避震微颠簸与冲刺前倾) -->
      <g id="rigRoot" transform="translate(0, 0)">

        <!-- ===== 5.1 后景层:鹈鹕左腿(远端腿,在车架后方) ===== -->
        <g id="leftLegGroup">
          <!-- 左大腿 -->
          <path id="leftThigh" d="" stroke="#cbd5e1" stroke-width="20" stroke-linecap="round" fill="none" />
          <!-- 左小腿 -->
          <path id="leftShin" d="" stroke="#d97706" stroke-width="10" stroke-linecap="round" fill="none" />
          <!-- 左脚掌蹼(贴合左踏板) -->
          <path id="leftFoot" d="" fill="#d97706" stroke="#b45309" stroke-width="2" stroke-linejoin="round" />
        </g>

        <!-- 左曲柄与左踏板(在五通后侧) -->
        <g id="leftCrankGroup">
          <line id="leftCrankArm" x1="455" y1="448" x2="455" y2="490" stroke="#64748b" stroke-width="8" stroke-linecap="round" />
          <rect id="leftPedal" x="439" y="485" width="32" height="9" rx="4" fill="#1e293b" stroke="#475569" stroke-width="2" />
        </g>

        <!-- ===== 5.2 自行车车轮组 ===== -->
        <!-- 后轮 (中心 305, 448) -->
        <g transform="translate(305, 448)">
          <g id="rearWheelSpin">
            <use href="#bikeWheel" />
          </g>
        </g>

        <!-- 前轮 (中心 675, 448) -->
        <g transform="translate(675, 448)">
          <g id="frontWheelSpin">
            <use href="#bikeWheel" />
          </g>
        </g>

        <!-- ===== 5.3 传动链条与飞轮 ===== -->
        <g id="drivetrain">
          <!-- 后飞轮 -->
          <circle cx="305" cy="448" r="18" fill="#475569" stroke="#cbd5e1" stroke-width="3" />
          <circle cx="305" cy="448" r="8" fill="#1e293b" />
          <!-- 链条上下边(带滚动虚线) -->
          <path id="chainLoop" d="M 305,430 L 455,416 A 32,32 0 1,1 455,480 L 305,466 A 18,18 0 1,1 305,430 Z"
                fill="none" stroke="#1e293b" stroke-width="5.5" />
          <path id="chainLinks" d="M 305,430 L 455,416 A 32,32 0 1,1 455,480 L 305,466 A 18,18 0 1,1 305,430 Z"
                fill="none" stroke="#94a3b8" stroke-width="3" stroke-dasharray="6 6" />
        </g>

        <!-- ===== 5.4 自行车车架、挡泥板与车把 ===== -->
        <g id="bikeFrame" filter="url(#softShadow)">
          <!-- 后轮复古挡泥板 -->
          <path d="M 206,442 A 99,99 0 0,1 388,392" fill="none" stroke="#fef3c7" stroke-width="8" stroke-linecap="round" />
          <!-- 前轮复古挡泥板 -->
          <path d="M 592,395 A 99,99 0 0,1 756,390" fill="none" stroke="#fef3c7" stroke-width="8" stroke-linecap="round" />

          <!-- 后货架与装鱼竹篓 -->
          <line x1="305" y1="448" x2="275" y2="338" stroke="#94a3b8" stroke-width="4.5" stroke-linecap="round" />
          <line x1="392" y1="338" x2="255" y2="338" stroke="#cbd5e1" stroke-width="6" stroke-linecap="round" />

          <!-- 车尾小竹篓与旗帜 -->
          <g id="rearBasket" transform="translate(258, 294)">
            <!-- 小三角破风旗 -->
            <line x1="8" y1="4" x2="-12" y2="-45" stroke="#cbd5e1" stroke-width="3" />
            <path id="pennantFlag" d="M-12,-45 L-58,-34 L-10,-22 Z" fill="#38bdf8" />
            <!-- 藤编篮框 -->
            <rect x="0" y="0" width="68" height="42" rx="7" fill="#d97706" stroke="#92400e" stroke-width="3" />
            <line x1="4" y1="14" x2="64" y2="14" stroke="#92400e" stroke-width="2" />
            <line x1="4" y1="28" x2="64" y2="28" stroke="#92400e" stroke-width="2" />
            <!-- 探出头的小鱼尾 -->
            <g id="basketFish" transform="translate(28, -4) rotate(-18)">
              <path d="M0,12 Q10,-6 24,8 L36,-2 L32,12 L40,20 L24,16 Q10,24 0,12 Z" fill="#38bdf8" stroke="#0284c7" stroke-width="2" />
              <circle cx="8" cy="10" r="2" fill="#0f172a" />
            </g>
          </g>

          <!-- 座管与车座弹簧 -->
          <line x1="455" y1="448" x2="380" y2="276" stroke="#cbd5e1" stroke-width="9" stroke-linecap="round" />
          <!-- 头管延伸立管 -->
          <path d="M 620,308 L 598,228 Q 593,212 572,212 L 542,212" fill="none" stroke="url(#chromeGrad)" stroke-width="9" stroke-linecap="round" />

          <!-- 主车架经典钻石管型 -->
          <g stroke="url(#frameGrad)" stroke-width="13" stroke-linejoin="round" stroke-linecap="round" fill="none">
            <!-- 后下叉 -->
            <line x1="305" y1="448" x2="455" y2="448" />
            <!-- 后上叉 -->
            <line x1="305" y1="448" x2="395" y2="310" />
            <!-- 立管 -->
            <line x1="455" y1="448" x2="395" y2="310" />
            <!-- 下管 -->
            <line x1="455" y1="448" x2="612" y2="326" />
            <!-- 复古弧形上管 -->
            <path d="M 395,310 Q 505,302 606,304" />
            <!-- 头管 -->
            <line x1="602" y1="292" x2="620" y2="338" />
            <!-- 前叉 -->
            <path d="M 615,332 Q 642,395 675,448" />
          </g>

          <!-- 车架高光装饰贴花 -->
          <line x1="485" y1="422" x2="578" y2="352" stroke="#fca5a5" stroke-width="3" stroke-linecap="round" opacity="0.7" />
          <!-- 前后花鼓盖 -->
          <circle cx="305" cy="448" r="9" fill="#f8fafc" stroke="#334155" stroke-width="3" />
          <circle cx="675" cy="448" r="9" fill="#f8fafc" stroke="#334155" stroke-width="3" />

          <!-- 复古真皮车座 -->
          <g id="saddleGroup" transform="translate(380, 274)">
            <path d="M -38,6 Q -42,-12 -14,-10 Q 18,-6 36,-2 Q 44,4 28,12 Q -10,16 -38,6 Z"
                  fill="#78350f" stroke="#451a03" stroke-width="3" />
            <circle cx="-26" cy="3" r="2.2" fill="#fbbf24" />
            <circle cx="-16" cy="5" r="2.2" fill="#fbbf24" />
          </g>

          <!-- 车头复古大灯 -->
          <g id="headlamp" transform="translate(626, 286)">
            <path d="M 0,-12 L 18,-16 A 14,14 0 0,1 18,16 L 0,12 Z" fill="#cbd5e1" stroke="#475569" stroke-width="2.5" />
            <ellipse cx="20" cy="0" rx="5" ry="14" fill="#fef08a" />
            <!-- 夜间车灯光束 -->
            <polygon id="headlightBeam" points="24,-10 360,-85 360,115 24,10" fill="url(#skyGrad)" opacity="0" />
          </g>

          <!-- 车铃铛 -->
          <g id="bikeBell" transform="translate(566, 200)" style="cursor: pointer;">
            <path d="M -12,6 A 12,12 0 0,1 12,6 Z" fill="#fbbf24" stroke="#b45309" stroke-width="2" />
            <rect x="-3" y="6" width="6" height="6" fill="#94a3b8" />
          </g>
          <!-- 铃铛音波环(触发时闪烁) -->
          <g id="bellWaves" transform="translate(566, 192)" opacity="0" fill="none" stroke="#fde047" stroke-width="3" stroke-linecap="round">
            <path d="M -16,-8 Q 0,-22 16,-8" />
            <path d="M -24,-16 Q 0,-34 24,-16" />
          </g>
        </g>

        <!-- ===== 5.5 大齿盘与右曲柄踏板(前景侧) ===== -->
        <g id="crankWheelGroup" transform="translate(455, 448)">
          <g id="chainringSpin">
            <circle r="32" fill="#e2e8f0" stroke="#475569" stroke-width="4" />
            <circle r="24" fill="none" stroke="#94a3b8" stroke-width="2" stroke-dasharray="8 6" />
            <!-- 五爪镂空 -->
            <line x1="0" y1="-28" x2="0" y2="28" stroke="#475569" stroke-width="4" />
            <line x1="-26" y1="-9" x2="26" y2="9" stroke="#475569" stroke-width="4" />
            <line x1="-17" y1="22" x2="17" y2="-22" stroke="#475569" stroke-width="4" />
            <circle r="8" fill="#334155" />
          </g>
        </g>

        <!-- ===== 5.6 鹈鹕角色主体(随踩踏重心起伏) ===== -->
        <g id="pelicanBodyRoot">

          <!-- 左翼(远端翅膀,扶住远端车把) -->
          <path id="leftWing" d="M 430,220 Q 495,188 552,208"
                fill="none" stroke="#cbd5e1" stroke-width="18" stroke-linecap="round" />

          <!-- 翘起的尾羽 -->
          <g id="tailFeathers" transform="translate(342, 248)">
            <path d="M 10,8 L -32,-14 Q -42,-4 -22,10 L -38,6 Q -42,18 -16,22 L -28,24 Q -24,34 8,26 Z"
                  fill="#f8fafc" stroke="#94a3b8" stroke-width="2.5" stroke-linejoin="round" />
          </g>

          <!-- 饱满圆润的鹈鹕躯干 -->
          <path id="pelicanTorso"
                d="M 345,252
                   C 332,205 375,165 426,178
                   C 468,188 490,235 468,268
                   C 446,298 362,292 345,252 Z"
                fill="url(#bodyFeatherGrad)" stroke="#94a3b8" stroke-width="2.8" />

          <!-- 肚子软毛纹理 -->
          <path d="M 382,268 Q 418,278 446,258" fill="none" stroke="#cbd5e1" stroke-width="2.2" stroke-linecap="round" />
          <path d="M 392,252 Q 422,260 448,242" fill="none" stroke="#e2e8f0" stroke-width="2" stroke-linecap="round" />

          <!-- 飘扬的红白条纹运动围巾 -->
          <g id="scarfGroup" transform="translate(445, 192)">
            <!-- 脖圈部分 -->
            <path d="M -14,-6 Q 10,8 30,-4 Q 28,14 6,16 Q -16,14 -14,-6 Z"
                  fill="#ef4444" stroke="#991b1b" stroke-width="2" />
            <!-- 随风飘动的围巾尾带(动态路径) -->
            <path id="scarfTail1" d="M -8,4 Q -65,-8 -118,8 Q -150,18 -182,4"
                  fill="none" stroke="#ef4444" stroke-width="14" stroke-linecap="round" />
            <path id="scarfTailStripe" d="M -8,4 Q -65,-8 -118,8 Q -150,18 -182,4"
                  fill="none" stroke="#fef2f2" stroke-width="14" stroke-dasharray="10 14" stroke-linecap="round" />
          </g>

          <!-- 优雅修长的 S 型天鹅般长颈 -->
          <path id="pelicanNeck"
                d="M 442,196
                   C 426,158 430,118 458,92
                   C 472,78 492,82 486,110
                   C 480,142 465,172 468,198 Z"
                fill="url(#bodyFeatherGrad)" stroke="#94a3b8" stroke-width="2.6" />

          <!-- ===== 5.7 鹈鹕头部、骑行小帽、护目镜与巨大喉囊 ===== -->
          <g id="pelicanHead" transform="translate(474, 88)">
            <!-- 后脑勺羽冠(随风微微颤动) -->
            <path id="headCrest" d="M -18,-8 L -42,-20 L -28,-2 L -46,-6 L -24,8 Z"
                  fill="#f8fafc" stroke="#94a3b8" stroke-width="2" stroke-linejoin="round" />

            <!-- 头骨圆球 -->
            <ellipse cx="0" cy="0" rx="26" ry="23" fill="#ffffff" stroke="#94a3b8" stroke-width="2.5" />

            <!-- 底部弹性大喉囊(核心特征!会随颠簸和装鱼剧烈晃动) -->
            <path id="gularPouch"
                  d="M 8,6
                     C 18,62 68,78 122,52
                     C 156,36 172,14 178,-2
                     L 12,2 Z"
                  fill="url(#pouchGrad)" stroke="#b45309" stroke-width="2.8" stroke-linejoin="round" />

            <!-- 喉囊里的鼓包鱼影(喂鱼时显现) -->
            <g id="pouchFishSilhouette" opacity="0" transform="translate(82, 30) rotate(12)">
              <ellipse cx="0" cy="0" rx="22" ry="9" fill="#9a3412" opacity="0.35" />
            </g>

            <!-- 喉囊褶皱纹理线 -->
            <path id="pouchCrease1" d="M 26,14 Q 68,52 134,26" fill="none" stroke="#d97706" stroke-width="2" stroke-linecap="round" opacity="0.75" />
            <path id="pouchCrease2" d="M 42,10 Q 82,36 148,14" fill="none" stroke="#d97706" stroke-width="1.6" stroke-linecap="round" opacity="0.6" />

            <!-- 超长上喙与粉红钩尖 -->
            <path id="upperBeak"
                  d="M 16,-11
                     C 66,-16 132,-15 178,-10
                     C 188,-9 192,2 184,8
                     C 178,12 174,4 170,1
                     L 8,5 Z"
                  fill="url(#upperBeakGrad)" stroke="#c2410c" stroke-width="2.5" stroke-linejoin="round" />

            <!-- 眼睛与眼周黄色裸皮 -->
            <circle cx="8" cy="-5" r="9.5" fill="#fde047" />
            <g id="eyeOpen">
              <circle cx="8" cy="-5" r="6.5" fill="#ffffff" stroke="#475569" stroke-width="1.5" />
              <circle cx="10" cy="-5" r="3.6" fill="#0f172a" />
              <circle cx="11.5" cy="-6.8" r="1.5" fill="#ffffff" />
            </g>
            <!-- 眨眼闭合线 -->
            <path id="eyeBlink" d="M 1,-5 Q 8,-1 15,-5" fill="none" stroke="#334155" stroke-width="2.5" stroke-linecap="round" opacity="0" />

            <!-- 复古飞行员护目镜(推在额头上) -->
            <g transform="translate(2, -18) rotate(-14)">
              <path d="M -24,2 Q -2,6 18,0" fill="none" stroke="#334155" stroke-width="4" />
              <rect x="2" y="-8" width="18" height="13" rx="5" fill="#38bdf8" stroke="#78350f" stroke-width="3" />
              <line x1="6" y1="-5" x2="12" y2="2" stroke="#e0f2fe" stroke-width="2" stroke-linecap="round" />
            </g>

            <!-- 帅气蓝色条纹骑行小歪帽 (Casquette) -->
            <g transform="translate(-4, -22) rotate(-10)">
              <path d="M -22,4 C -22,-16 18,-18 22,2 Z" fill="#0284c7" stroke="#0369a1" stroke-width="2.2" />
              <path d="M -2,-11 C 4,-11 8,-4 8,2 L -4,2 Z" fill="#facc15" />
              <!-- 翘起的帽檐 -->
              <path d="M 14,1 Q 32,-6 36,2 Q 26,7 12,4 Z" fill="#0369a1" />
              <circle cx="-1" cy="-12" r="3" fill="#facc15" />
            </g>
          </g>
        </g>

        <!-- ===== 5.8 前景层:鹈鹕右腿(近端腿,完整逆向运动学踩踏) ===== -->
        <g id="rightLegGroup">
          <!-- 大腿羽毛蓬松根部 -->
          <circle id="rightHipJoint" cx="406" cy="264" r="18" fill="#f1f5f9" />
          <!-- 右大腿(白羽包裹上段 + 橙色脚胫) -->
          <path id="rightThighFeather" d="" stroke="#f8fafc" stroke-width="24" stroke-linecap="round" fill="none" />
          <path id="rightThigh" d="" stroke="#f59e0b" stroke-width="11" stroke-linecap="round" fill="none" />
          <!-- 膝关节圆点 -->
          <circle id="rightKneeJoint" cx="460" cy="350" r="6.5" fill="#fbbf24" stroke="#d97706" stroke-width="2" />
          <!-- 右小腿 -->
          <path id="rightShin" d="" stroke="#f59e0b" stroke-width="10" stroke-linecap="round" fill="none" />
          <!-- 大扁平鸭蹼脚掌(踩在右踏板上) -->
          <path id="rightFoot" d="" fill="#fb923c" stroke="#c2410c" stroke-width="2.4" stroke-linejoin="round" />
        </g>

        <!-- 右曲柄与右踏板(最前景) -->
        <g id="rightCrankGroup">
          <line id="rightCrankArm" x1="455" y1="448" x2="455" y2="406" stroke="url(#chromeGrad)" stroke-width="8.5" stroke-linecap="round" />
          <circle cx="455" cy="448" r="6" fill="#1e293b" />
          <g id="rightPedal" transform="translate(455, 406)">
            <rect x="-18" y="-5" width="36" height="10" rx="4" fill="#0f172a" stroke="#cbd5e1" stroke-width="2" />
            <rect x="-11" y="-2" width="8" height="4" rx="1" fill="#f97316" />
            <rect x="3" y="-2" width="8" height="4" rx="1" fill="#f97316" />
          </g>
        </g>

        <!-- ===== 5.9 前景层:鹈鹕右翅膀(伸出握住近端车把) ===== -->
        <g id="rightWingGroup">
          <!-- 主翼臂 -->
          <path id="rightWingShape"
                d="M 418,215
                   C 405,248 450,264 495,245
                   C 522,233 536,222 548,214
                   C 535,204 510,214 484,218
                   C 456,222 435,202 418,215 Z"
                fill="url(#wingGrad)" stroke="#64748b" stroke-width="2.6" stroke-linejoin="round" />
          <!-- 飞羽层次分界线 -->
          <path id="rightWingDetail"
                d="M 432,238 Q 468,244 504,232 M 448,248 Q 482,250 518,234"
                fill="none" stroke="#94a3b8" stroke-width="2" stroke-linecap="round" />
          <!-- 握住车把的翅尖羽毛指 -->
          <ellipse cx="545" cy="213" rx="11" ry="8" transform="rotate(-12 545 213)"
                   fill="#ffffff" stroke="#64748b" stroke-width="2.4" />
          <!-- 近端车把胶套 -->
          <rect x="530" y="207" width="24" height="10" rx="5" fill="#78350f" stroke="#451a03" stroke-width="2" />
        </g>

      </g>

      <!-- ================= 6. 投喂飞鱼与前景速度线特效层 ================= -->
      <!-- 投喂抛物线飞鱼 -->
      <g id="flyingFish" opacity="0" transform="translate(900, 200)">
        <path d="M -22,0 C -10,-14 14,-12 24,0 L 38,-10 L 33,0 L 38,10 L 24,0 C 14,12 -10,14 -22,0 Z"
              fill="#38bdf8" stroke="#0369a1" stroke-width="2.2" stroke-linejoin="round" />
        <path d="M -6,-8 Q 4,0 -6,8" fill="none" stroke="#bae6fd" stroke-width="2" />
        <circle cx="-12" cy="-2" r="2.3" fill="#0f172a" />
      </g>

      <!-- 迎面气流速度线 -->
      <g id="windLinesLayer" stroke="#ffffff" stroke-linecap="round" opacity="0.45"></g>
    </svg>

    <!-- 底部交互控制栏 -->
    <div class="controls-bar">
      <div class="ctrl-group">
        <span class="ctrl-label">骑行速度</span>
        <input type="range" id="speedSlider" min="0.2" max="2.8" step="0.1" value="1.2" />
        <span class="speed-val" id="speedLabel">1.2x</span>
      </div>

      <div class="divider"></div>

      <button class="btn btn-primary" id="feedFishBtn" title="丢一条鲜鱼进鹈鹕的大喉囊">
        🐟 投喂海鱼
      </button>

      <button class="btn" id="ringBellBtn" title="按响清脆的单车铃铛">
        🔔 叮铃铃
      </button>

      <button class="btn" id="sprintBtn" title="短暂全力冲刺">
        ⚡ 破风冲刺
      </button>

      <button class="btn" id="themeBtn" title="切换白昼、黄昏、星夜">
        🌅 切换时段
      </button>
    </div>
  </div>

  <script>
    (() => {
      // ==================== 1. 场景初始化:生成背景棕榈树、护栏、车道线 ====================
      const sceneryLayer = document.getElementById('sceneryLayer');
      const railingLayer = document.getElementById('railingLayer');
      const roadDashes = document.getElementById('roadDashes');
      const roadPebbles = document.getElementById('roadPebbles');
      const windLinesLayer = document.getElementById('windLinesLayer');

      const SVG_NS = 'http://www.w3.org/2000/svg';

      // 创建棕榈树与海滨灌木
      const palmTrees = [
        { x: 140, scale: 0.92, lean: -8 },
        { x: 490, scale: 0.76, lean: 6 },
        { x: 850, scale: 1.02, lean: -5 }
      ];

      palmTrees.forEach((tree) => {
        const g = document.createElementNS(SVG_NS, 'g');
        g.innerHTML = `
          <g transform="scale(${tree.scale})">
            <!-- 地面树影 -->
            <ellipse cx="0" cy="0" rx="36" ry="6" fill="#d97706" opacity="0.28" />
            <!-- 弯曲树干 -->
            <path d="M -6,0 Q ${tree.lean * 2},-75 ${tree.lean * 3},-145 L ${tree.lean * 3 + 7},-145 Q ${tree.lean * 2 + 8},-75 6,0 Z"
                  fill="#92400e" />
            <!-- 椰子 -->
            <circle cx="${tree.lean * 3 - 4}" cy="-142" r="5.5" fill="#451a03" />
            <circle cx="${tree.lean * 3 + 5}" cy="-140" r="5.5" fill="#451a03" />
            <!-- 棕榈树叶冠 -->
            <g transform="translate(${tree.lean * 3 + 2}, -146)" fill="#15803d">
              <path d="M 0,0 Q -42,-24 -76,8 Q -38,-4 0,0 Z" />
              <path d="M 0,0 Q -36,-45 -58,-28 Q -28,-22 0,0 Z" fill="#16a34a" />
              <path d="M 0,0 Q 38,-42 66,-18 Q 30,-18 0,0 Z" fill="#16a34a" />
              <path d="M 0,0 Q 46,-18 78,16 Q 38,0 0,0 Z" />
              <path d="M 0,0 Q 2,-48 18,-56 Q 14,-24 0,0 Z" fill="#22c55e" />
            </g>
          </g>
        `;
        sceneryLayer.appendChild(g);
        tree.el = g;
      });

      // 创建海滨木栏杆 (12根循环)
      const railPosts = [];
      for (let i = 0; i < 12; i++) {
        const g = document.createElementNS(SVG_NS, 'g');
        const x = i * 95;
        g.innerHTML = `
          <rect x="-5" y="432" width="10" height="40" rx="2" fill="#78350f" />
          <rect x="-5" y="432" width="10" height="6" rx="1" fill="#f8fafc" />
          <line x1="0" y1="446" x2="95" y2="446" stroke="#d97706" stroke-width="3" />
        `;
        railingLayer.appendChild(g);
        railPosts.push({ x, el: g });
      }

      // 创建公路虚线 (9段循环)
      const dashes = [];
      for (let i = 0; i < 9; i++) {
        const rect = document.createElementNS(SVG_NS, 'rect');
        rect.setAttribute('y', '546');
        rect.setAttribute('width', '68');
        rect.setAttribute('height', '8');
        rect.setAttribute('rx', '4');
        roadDashes.appendChild(rect);
        dashes.push({ x: i * 130, el: rect });
      }

      // 创建路面掠过碎石线
      const pebbles = [];
      for (let i = 0; i < 14; i++) {
        const line = document.createElementNS(SVG_NS, 'line');
        const y = 492 + (i % 5) * 24 + Math.random() * 8;
        const len = 8 + (i % 3) * 7;
        line.dataset.y = y;
        line.dataset.len = len;
        roadPebbles.appendChild(line);
        pebbles.push({ x: i * 82, y, len, el: line });
      }

      // 创建空中气流线
      const windStreaks = [];
      for (let i = 0; i < 6; i++) {
        const line = document.createElementNS(SVG_NS, 'line');
        line.setAttribute('stroke-width', '2');
        windLinesLayer.appendChild(line);
        windStreaks.push({
          x: Math.random() * 1000,
          y: 120 + i * 58,
          len: 45 + (i % 3) * 28,
          el: line
        });
      }

      // ==================== 2. 逆向运动学 (2-Bone IK) 求解器 ====================
      /**
       * 根据髋关节 (hx, hy) 与脚踏点 (px, py),计算膝盖坐标 (kx, ky)
       * 自行车朝右骑行,膝盖始终朝右前方弯曲
       */
      function solveKneeIK(hx, hy, px, py, L1, L2) {
        const dx = px - hx;
        const dy = py - hy;
        let d = Math.hypot(dx, dy);
        const maxReach = L1 + L2 - 0.5;
        const minReach = Math.abs(L1 - L2) + 0.5;
        d = Math.max(minReach, Math.min(maxReach, d));

        const a = (L1 * L1 - L2 * L2 + d * d) / (2 * d);
        const h = Math.sqrt(Math.max(0, L1 * L1 - a * a));

        const ux = dx / d;
        const uy = dy / d;

        // 朝右前方突出的法向量 (uy, -ux)
        const kx = hx + a * ux + h * uy;
        const ky = hy + a * uy - h * ux;
        return { x: kx, y: ky };
      }

      // 生成鸭蹼脚掌 SVG Path(脚踝在踝点,脚尖朝右前方踩住踏板)
      function buildWebbedFootPath(ankleX, ankleY, pedalX, pedalY, angleDeg) {
        const rad = (angleDeg * Math.PI) / 180;
        const cos = Math.cos(rad);
        const sin = Math.sin(rad);
        const rot = (lx, ly) => {
          const rx = pedalX + lx * cos - ly * sin;
          const ry = pedalY + lx * sin + ly * cos;
          return `${rx.toFixed(1)},${ry.toFixed(1)}`;
        };
        return `M ${ankleX.toFixed(1)},${ankleY.toFixed(1)} ` +
               `L ${rot(-16, -2)} ` +
               `L ${rot(-12, 5)} ` +
               `L ${rot(24, 5)} ` +
               `Q ${rot(32, -2)} ${rot(22, -7)} ` +
               `L ${ankleX.toFixed(1)},${ankleY.toFixed(1)} Z`;
      }

      // ==================== 3. 获取动画 DOM 节点引用 ====================
      const rearWheelSpin = document.getElementById('rearWheelSpin');
      const frontWheelSpin = document.getElementById('frontWheelSpin');
      const chainringSpin = document.getElementById('chainringSpin');
      const chainLinks = document.getElementById('chainLinks');

      const rightCrankArm = document.getElementById('rightCrankArm');
      const rightPedal = document.getElementById('rightPedal');
      const leftCrankArm = document.getElementById('leftCrankArm');
      const leftPedal = document.getElementById('leftPedal');

      const rightHipJoint = document.getElementById('rightHipJoint');
      const rightThighFeather = document.getElementById('rightThighFeather');
      const rightThigh = document.getElementById('rightThigh');
      const rightKneeJoint = document.getElementById('rightKneeJoint');
      const rightShin = document.getElementById('rightShin');
      const rightFoot = document.getElementById('rightFoot');

      const leftThigh = document.getElementById('leftThigh');
      const leftShin = document.getElementById('leftShin');
      const leftFoot = document.getElementById('leftFoot');

      const pelicanBodyRoot = document.getElementById('pelicanBodyRoot');
      const pelicanHead = document.getElementById('pelicanHead');
      const gularPouch = document.getElementById('gularPouch');
      const pouchCrease1 = document.getElementById('pouchCrease1');
      const pouchCrease2 = document.getElementById('pouchCrease2');
      const upperBeak = document.getElementById('upperBeak');
      const eyeOpen = document.getElementById('eyeOpen');
      const eyeBlink = document.getElementById('eyeBlink');
      const pouchFishSilhouette = document.getElementById('pouchFishSilhouette');

      const scarfTail1 = document.getElementById('scarfTail1');
      const scarfTailStripe = document.getElementById('scarfTailStripe');
      const pennantFlag = document.getElementById('pennantFlag');
      const rigRoot = document.getElementById('rigRoot');
      const groundShadow = document.getElementById('groundShadow');

      const waveLine1 = document.getElementById('waveLine1');
      const waveLine2 = document.getElementById('waveLine2');
      const sailboat = document.getElementById('sailboat');
      const clouds = Array.from(document.querySelectorAll('.cloud'));

      const flyingFish = document.getElementById('flyingFish');
      const bellWaves = document.getElementById('bellWaves');
      const hudTelemetry = document.getElementById('hudTelemetry');

      // ==================== 4. 状态与交互参数 ====================
      let baseSpeed = 1.2;
      let sprintBoost = 0;
      let crankAngle = 0; // 弧度
      let distanceScroll = 0;
      let timeSec = 0;

      // 投喂鱼状态
      let fishState = {
        active: false,
        progress: 0,
        chewTimer: 0
      };

      // 车铃状态
      let bellTimer = 0;

      // 昼夜主题列表
      const themes = [
        {
          name: '晴朗白昼',
          sky: ['#38bdf8', '#bae6fd', '#fef3c7'],
          sea: ['#0284c7', '#0369a1'],
          sunColor: '#fde047',
          starsOpacity: 0,
          beamOpacity: 0
        },
        {
          name: '落日黄金海岸',
          sky: ['#4c1d95', '#f97316', '#fde047'],
          sea: ['#1e3a8a', '#7c2d12'],
          sunColor: '#fb923c',
          starsOpacity: 0.35,
          beamOpacity: 0.15
        },
        {
          name: '星夜巡航',
          sky: ['#020617', '#0f172a', '#1e293b'],
          sea: ['#090d16', '#0f172a'],
          sunColor: '#f8fafc',
          starsOpacity: 0.95,
          beamOpacity: 0.35
        }
      ];
      let currentThemeIdx = 0;

      // Web Audio 合成音效(车铃声 + 吞鱼咕噜声,无需外部音频文件)
      let audioCtx = null;
      function ensureAudio() {
        if (!audioCtx) {
          const AudioContextClass = window.AudioContext || window.webkitAudioContext;
          if (AudioContextClass) audioCtx = new AudioContextClass();
        }
      }

      function playBellSound() {
        ensureAudio();
        if (!audioCtx) return;
        const now = audioCtx.currentTime;
        [1568, 2093].forEach((freq, idx) => {
          const osc = audioCtx.createOscillator();
          const gain = audioCtx.createGain();
          osc.type = 'sine';
          osc.frequency.setValueAtTime(freq, now + idx * 0.07);
          gain.gain.setValueAtTime(0.18, now + idx * 0.07);
          gain.gain.exponentialRampToValueAtTime(0.001, now + idx * 0.07 + 0.55);
          osc.connect(gain);
          gain.connect(audioCtx.destination);
          osc.start(now + idx * 0.07);
          osc.stop(now + idx * 0.07 + 0.56);
        });
      }

      function playGulpSound() {
        ensureAudio();
        if (!audioCtx) return;
        const now = audioCtx.currentTime;
        const osc = audioCtx.createOscillator();
        const gain = audioCtx.createGain();
        osc.type = 'triangle';
        osc.frequency.setValueAtTime(240, now);
        osc.frequency.exponentialRampToValueAtTime(520, now + 0.14);
        osc.frequency.exponentialRampToValueAtTime(180, now + 0.28);
        gain.gain.setValueAtTime(0.2, now);
        gain.gain.exponentialRampToValueAtTime(0.001, now + 0.3);
        osc.connect(gain);
        gain.connect(audioCtx.destination);
        osc.start(now);
        osc.stop(now + 0.31);
      }

      // ==================== 5. 绑定 UI 控件事件 ====================
      const speedSlider = document.getElementById('speedSlider');
      const speedLabel = document.getElementById('speedLabel');

      speedSlider.addEventListener('input', (e) => {
        baseSpeed = parseFloat(e.target.value);
        speedLabel.textContent = `${baseSpeed.toFixed(1)}x`;
      });

      function triggerFeedFish() {
        if (fishState.active) return;
        fishState.active = true;
        fishState.progress = 0;
      }

      function triggerRingBell() {
        bellTimer = 0.65;
        playBellSound();
      }

      document.getElementById('feedFishBtn').addEventListener('click', triggerFeedFish);
      document.getElementById('ringBellBtn').addEventListener('click', triggerRingBell);
      document.getElementById('bikeBell').addEventListener('click', triggerRingBell);

      document.getElementById('sprintBtn').addEventListener('click', () => {
        sprintBoost = 1.65;
        triggerRingBell();
      });

      document.getElementById('themeBtn').addEventListener('click', () => {
        currentThemeIdx = (currentThemeIdx + 1) % themes.length;
        const t = themes[currentThemeIdx];
        document.getElementById('skyStop1').setAttribute('stop-color', t.sky[0]);
        document.getElementById('skyStop2').setAttribute('stop-color', t.sky[1]);
        document.getElementById('skyStop3').setAttribute('stop-color', t.sky[2]);
        document.getElementById('seaStop1').setAttribute('stop-color', t.sea[0]);
        document.getElementById('seaStop2').setAttribute('stop-color', t.sea[1]);
        document.getElementById('sunCore').setAttribute('fill', t.sunColor);
        document.getElementById('starsLayer').setAttribute('opacity', t.starsOpacity);
        document.getElementById('headlightBeam').setAttribute('opacity', t.beamOpacity);
      });

      // ==================== 6. 核心帧循环 (requestAnimationFrame) ====================
      const BB_X = 455; // 五通中心 X
      const BB_Y = 448; // 五通中心 Y
      const CRANK_R = 42; // 曲柄半径
      const L_THIGH = 98; // 大腿骨骼长度
      const L_SHIN = 102; // 小腿骨骼长度

      let lastTime = performance.now();

      function animate(now) {
        const dt = Math.min(0.05, (now - lastTime) / 1000);
        lastTime = now;

        // 冲刺衰减
        if (sprintBoost > 0) {
          sprintBoost = Math.max(0, sprintBoost - dt * 0.7);
        }

        const effectiveSpeed = baseSpeed + sprintBoost;
        timeSec += dt;
        crankAngle += dt * effectiveSpeed * 5.2;
        distanceScroll += dt * effectiveSpeed * 290;

        // 更新顶部遥测数据
        const kmh = Math.round(effectiveSpeed * 18.5);
        const rpm = Math.round(effectiveSpeed * 62);
        hudTelemetry.textContent = `时速 ${kmh} km/h · 踏频 ${rpm} RPM`;

        // ---------- 6.1 车轮、牙盘、链条转动 ----------
        const wheelDeg = (crankAngle * 145) % 360;
        const crankDeg = ((crankAngle * 180) / Math.PI) % 360;
        rearWheelSpin.setAttribute('transform', `rotate(${wheelDeg.toFixed(1)})`);
        frontWheelSpin.setAttribute('transform', `rotate(${wheelDeg.toFixed(1)})`);
        chainringSpin.setAttribute('transform', `rotate(${crankDeg.toFixed(1)})`);
        chainLinks.setAttribute('stroke-dashoffset', (-distanceScroll * 0.35).toFixed(1));

        // ---------- 6.2 左右曲柄与脚踏位置计算 ----------
        const rPedalX = BB_X + Math.cos(crankAngle) * CRANK_R;
        const rPedalY = BB_Y + Math.sin(crankAngle) * CRANK_R;
        const lPedalX = BB_X + Math.cos(crankAngle + Math.PI) * CRANK_R;
        const lPedalY = BB_Y + Math.sin(crankAngle + Math.PI) * CRANK_R;

        rightCrankArm.setAttribute('x2', rPedalX.toFixed(1));
        rightCrankArm.setAttribute('y2', rPedalY.toFixed(1));
        rightPedal.setAttribute('transform', `translate(${rPedalX.toFixed(1)}, ${rPedalY.toFixed(1)})`);

        leftCrankArm.setAttribute('x2', lPedalX.toFixed(1));
        leftCrankArm.setAttribute('y2', lPedalY.toFixed(1));
        leftPedal.setAttribute('x', (lPedalX - 16).toFixed(1));
        leftPedal.setAttribute('y', (lPedalY - 4.5).toFixed(1));

        // ---------- 6.3 身体随踩踏起伏与前倾 ----------
        const bodyBobY = Math.sin(crankAngle * 2) * 5.5;
        const bodyPitch = Math.cos(crankAngle * 2) * 1.4 + sprintBoost * 2.5;
        pelicanBodyRoot.setAttribute(
          'transform',
          `translate(0, ${bodyBobY.toFixed(2)}) rotate(${bodyPitch.toFixed(2)}, 410, 250)`
        );

        // 整车轻颠簸
        const bikeBounceY = Math.sin(crankAngle * 2 + 0.5) * 1.8;
        rigRoot.setAttribute('transform', `translate(0, ${bikeBounceY.toFixed(2)})`);
        groundShadow.setAttribute('rx', (235 + bikeBounceY * 2).toFixed(1));

        // ---------- 6.4 右腿 & 左腿 2D IK 骨骼解算 ----------
        const hipX = 406;
        const hipY = 264 + bodyBobY;
        rightHipJoint.setAttribute('cx', hipX.toFixed(1));
        rightHipJoint.setAttribute('cy', hipY.toFixed(1));

        // 右腿踝关节略高于踏板表面
        const rAnkleX = rPedalX - 4;
        const rAnkleY = rPedalY - 7;
        const rKnee = solveKneeIK(hipX, hipY, rAnkleX, rAnkleY, L_THIGH, L_SHIN);

        // 大腿上段白羽包裹(从髋部延伸到60%大腿处)
        const featherEndX = hipX + (rKnee.x - hipX) * 0.58;
        const featherEndY = hipY + (rKnee.y - hipY) * 0.58;
        rightThighFeather.setAttribute('d', `M ${hipX.toFixed(1)},${hipY.toFixed(1)} L ${featherEndX.toFixed(1)},${featherEndY.toFixed(1)}`);
        rightThigh.setAttribute('d', `M ${hipX.toFixed(1)},${hipY.toFixed(1)} L ${rKnee.x.toFixed(1)},${rKnee.y.toFixed(1)}`);
        rightKneeJoint.setAttribute('cx', rKnee.x.toFixed(1));
        rightKneeJoint.setAttribute('cy', rKnee.y.toFixed(1));
        rightShin.setAttribute('d', `M ${rKnee.x.toFixed(1)},${rKnee.y.toFixed(1)} L ${rAnkleX.toFixed(1)},${rAnkleY.toFixed(1)}`);

        const rFootTilt = Math.sin(crankAngle) * 10;
        rightFoot.setAttribute('d', buildWebbedFootPath(rAnkleX, rAnkleY, rPedalX, rPedalY - 2, rFootTilt));

        // 左腿(远端腿)IK
        const lHipX = 414;
        const lHipY = 260 + bodyBobY;
        const lAnkleX = lPedalX - 4;
        const lAnkleY = lPedalY - 7;
        const lKnee = solveKneeIK(lHipX, lHipY, lAnkleX, lAnkleY, L_THIGH, L_SHIN);

        leftThigh.setAttribute('d', `M ${lHipX.toFixed(1)},${lHipY.toFixed(1)} L ${lKnee.x.toFixed(1)},${lKnee.y.toFixed(1)}`);
        leftShin.setAttribute('d', `M ${lKnee.x.toFixed(1)},${lKnee.y.toFixed(1)} L ${lAnkleX.toFixed(1)},${lAnkleY.toFixed(1)}`);
        const lFootTilt = Math.sin(crankAngle + Math.PI) * 10;
        leftFoot.setAttribute('d', buildWebbedFootPath(lAnkleX, lAnkleY, lPedalX, lPedalY - 2, lFootTilt));

        // ---------- 6.5 鹈鹕大喉囊弹性物理摆动与咀嚼 ----------
        let chewStretch = 0;
        if (fishState.chewTimer > 0) {
          fishState.chewTimer = Math.max(0, fishState.chewTimer - dt);
          chewStretch = Math.sin(fishState.chewTimer * 28) * 18 + 14;
          pouchFishSilhouette.setAttribute('opacity', Math.min(1, fishState.chewTimer * 1.2).toFixed(2));
        } else {
          pouchFishSilhouette.setAttribute('opacity', '0');
        }

        const pouchSwing = Math.sin(crankAngle * 2 - 0.8) * 7 + chewStretch;
        const pouchWave = Math.cos(timeSec * 8) * 4;
        const pMidY = 78 + pouchSwing * 1.15;
        const pRightY = 52 + pouchSwing * 0.7 + pouchWave;

        gularPouch.setAttribute(
          'd',
          `M 8,6 C 18,${(62 + pouchSwing * 0.8).toFixed(1)} 68,${pMidY.toFixed(1)} 122,${pRightY.toFixed(1)} C 156,${(36 + pouchSwing * 0.3).toFixed(1)} 172,14 178,-2 L 12,2 Z`
        );
        pouchCrease1.setAttribute('d', `M 26,14 Q 68,${(52 + pouchSwing * 0.85).toFixed(1)} 134,${(26 + pouchSwing * 0.4).toFixed(1)}`);
        pouchCrease2.setAttribute('d', `M 42,10 Q 82,${(36 + pouchSwing * 0.6).toFixed(1)} 148,14`);

        // 头部微点动与张嘴接鱼
        const headNod = Math.sin(crankAngle * 2 - 0.4) * 3.2;
        pelicanHead.setAttribute('transform', `translate(474, 88) rotate(${headNod.toFixed(2)})`);

        // 鱼接近时张开大上喙
        let beakOpenDeg = 0;
        if (fishState.active && fishState.progress > 0.55) {
          beakOpenDeg = -18 * Math.sin(((fishState.progress - 0.55) / 0.45) * Math.PI);
        } else if (fishState.chewTimer > 0) {
          beakOpenDeg = -Math.abs(Math.sin(fishState.chewTimer * 28)) * 8;
        }
        upperBeak.setAttribute('transform', `rotate(${beakOpenDeg.toFixed(1)}, 16, -5)`);

        // 自然眨眼控制
        const blinkCycle = timeSec % 4.2;
        const isBlinking = blinkCycle > 4.02;
        eyeOpen.setAttribute('opacity', isBlinking ? '0' : '1');
        eyeBlink.setAttribute('opacity', isBlinking ? '1' : '0');

        // ---------- 6.6 飘带围巾与小旗波动 ----------
        const w1 = Math.sin(timeSec * 12) * 14;
        const w2 = Math.cos(timeSec * 12 - 1.2) * 18;
        const scarfPath = `M -8,4 Q -65,${(-8 + w1).toFixed(1)} -118,${(8 - w1 * 0.7).toFixed(1)} Q -150,${(14 + w2).toFixed(1)} -188,${(2 - w2).toFixed(1)}`;
        scarfTail1.setAttribute('d', scarfPath);
        scarfTailStripe.setAttribute('d', scarfPath);

        const flagWave = Math.sin(timeSec * 16) * 6;
        pennantFlag.setAttribute('d', `M-12,-45 Q-35,${(-42 + flagWave).toFixed(1)} -60,${(-33 - flagWave).toFixed(1)} L-10,-22 Z`);

        // ---------- 6.7 投喂海鱼抛物线动画 ----------
        if (fishState.active) {
          fishState.progress += dt * 1.45;
          const p = Math.min(1, fishState.progress);
          // 起点在画面右侧 (940, 220),终点在鹈鹕大嘴里 (595, 92)
          const fx = 940 + (595 - 940) * p;
          const fy = 220 + (92 - 220) * p - Math.sin(p * Math.PI) * 145;
          const fRot = -155 + p * 210;

          flyingFish.setAttribute('opacity', '1');
          flyingFish.setAttribute('transform', `translate(${fx.toFixed(1)}, ${fy.toFixed(1)}) rotate(${fRot.toFixed(1)})`);

          if (p >= 1) {
            fishState.active = false;
            fishState.chewTimer = 1.1;
            flyingFish.setAttribute('opacity', '0');
            playGulpSound();
          }
        }

        // ---------- 6.8 车铃音波闪烁 ----------
        if (bellTimer > 0) {
          bellTimer = Math.max(0, bellTimer - dt);
          const waveScale = 1 + (0.65 - bellTimer) * 0.6;
          bellWaves.setAttribute('opacity', (bellTimer / 0.65).toFixed(2));
          bellWaves.setAttribute('transform', `translate(566, 192) scale(${waveScale.toFixed(2)})`);
        } else {
          bellWaves.setAttribute('opacity', '0');
        }

        // ---------- 6.9 视差背景滚动(流云、海浪、棕榈树、栏杆、路面、风线) ----------
        // 云朵缓慢移动
        clouds.forEach((c) => {
          let cx = parseFloat(c.dataset.x) - dt * effectiveSpeed * 28 * parseFloat(c.dataset.speed);
          if (cx < -180) cx = 1080;
          c.dataset.x = cx;
          c.setAttribute('transform', `translate(${cx.toFixed(1)}, ${c.dataset.y}) scale(${c.dataset.scale})`);
        });

        // 海面波纹与小帆船微晃
        const waveShift = (distanceScroll * 0.12) % 100;
        waveLine1.setAttribute('transform', `translate(${-waveShift.toFixed(1)}, 0)`);
        waveLine2.setAttribute('transform', `translate(${(-waveShift * 0.7).toFixed(1)}, 0)`);
        const boatBob = Math.sin(timeSec * 2.5) * 2.5;
        sailboat.setAttribute('transform', `translate(260, ${(376 + boatBob).toFixed(1)}) rotate(${(boatBob * 1.2).toFixed(1)})`);

        // 棕榈树中景视差
        palmTrees.forEach((tree) => {
          tree.x -= dt * effectiveSpeed * 125;
          if (tree.x < -120) tree.x += 1220;
          tree.el.setAttribute('transform', `translate(${tree.x.toFixed(1)}, 452)`);
        });

        // 步道木栏杆
        railPosts.forEach((post) => {
          post.x -= dt * effectiveSpeed * 210;
          if (post.x < -95) post.x += 12 * 95;
          post.el.setAttribute('transform', `translate(${post.x.toFixed(1)}, 0)`);
        });

        // 公路虚线高速掠过
        dashes.forEach((d) => {
          d.x -= dt * effectiveSpeed * 320;
          if (d.x < -90) d.x += 9 * 130;
          d.el.setAttribute('x', d.x.toFixed(1));
        });

        // 路面碎石细线
        pebbles.forEach((p) => {
          p.x -= dt * effectiveSpeed * 320;
          if (p.x < -40) p.x += 1140;
          p.el.setAttribute('x1', p.x.toFixed(1));
          p.el.setAttribute('y1', p.y.toFixed(1));
          p.el.setAttribute('x2', (p.x + p.len).toFixed(1));
          p.el.setAttribute('y2', p.y.toFixed(1));
        });

        // 迎面风速线
        windLinesLayer.setAttribute('opacity', Math.min(0.75, effectiveSpeed * 0.28).toFixed(2));
        windStreaks.forEach((w) => {
          w.x -= dt * effectiveSpeed * 520;
          if (w.x < -120) {
            w.x = 1060 + Math.random() * 200;
            w.y = 95 + Math.random() * 280;
          }
          w.el.setAttribute('x1', w.x.toFixed(1));
          w.el.setAttribute('y1', w.y.toFixed(1));
          w.el.setAttribute('x2', (w.x + w.len * Math.min(1.8, effectiveSpeed)).toFixed(1));
          w.el.setAttribute('y2', w.y.toFixed(1));
        });

        requestAnimationFrame(animate);
      }

      requestAnimationFrame(animate);
    })();
  </script>
</body>
</html>

12
  • 感觉效果有点屌啊

  • 经典哈基米风格,不过结构和细节好了很多

  • 正价api吗还是ultra

  • 看着可以啊,比上一代好多了

  • ultra吗?好像挺不错的

  • @nikonikoni #3 正价aistudio api(用的gemini pro gcp 10刀赠金),搭配cc使用high effort

  • 物理状态看着还不错,但是缺乏艺术风格

  • 看起来还挺不错的

  • @gyo #0 提示词是简单的那一句话吗

  • 宛如核弹爆炸

12

你好啊,陌生人!

我的朋友,看起来你是新来的,如果想参与到讨论中,点击下面的按钮!

📈用户数目📈

目前论坛共有73057位seeker

🎉欢迎新用户🎉