难道gemini真的王朝了
🖼️预览

💻代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>鹈鹕翘头骑自行车牵引波音747 - SVG 2D动画</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@500;700;900&display=swap');
* {
box-sizing: border-box;
user-select: none;
-webkit-user-select: none;
}
body {
margin: 0;
padding: 0;
font-family: 'Noto Sans SC', system-ui, -apple-system, sans-serif;
background: #0f172a;
color: #f8fafc;
overflow-x: hidden;
}
.scene-container {
box-shadow: 0 25px 60px -15px rgba(0, 0, 0, 0.7), 0 0 0 1px rgba(255, 255, 255, 0.12);
}
/* Engine turbine spin animation */
@keyframes spinFast {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.turbine-spin {
transform-box: fill-box;
transform-origin: center;
animation: spinFast 0.18s linear infinite;
}
/* Interactive cursor for draggable 747 */
#boeing-747-group {
cursor: grab;
transition: filter 0.2s ease;
}
#boeing-747-group:hover {
filter: drop-shadow(0 0 12px rgba(255, 255, 255, 0.55));
}
#boeing-747-group:active {
cursor: grabbing;
}
/* Custom range slider styling */
input[type="range"] {
accent-color: #f59e0b;
}
</style>
</head>
<body class="min-h-screen flex flex-col items-center justify-between p-3 sm:p-5 bg-gradient-to-b from-slate-950 via-slate-900 to-slate-950">
<!-- Top Header Bar -->
<header class="w-full max-w-6xl flex flex-wrap items-center justify-between gap-3 mb-3 px-2">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-amber-400 to-orange-600 flex items-center justify-center shadow-lg shadow-orange-500/30 text-xl font-black text-slate-950">
🦩
</div>
<div>
<h1 class="text-lg sm:text-2xl font-black tracking-tight bg-gradient-to-r from-amber-200 via-orange-300 to-sky-300 bg-clip-text text-transparent">
鹈鹕单轮翘头骑行 × 牵引波音747
</h1>
<p class="text-xs sm:text-sm text-slate-400">
2D 物理联动骨骼与悬链线动画 · 支持鼠标/触控直接拖拽空中的波音747
</p>
</div>
</div>
<!-- Real-time Telemetry Pill -->
<div class="flex items-center gap-2 sm:gap-3 bg-slate-900/90 border border-slate-700/80 px-3.5 py-2 rounded-2xl text-xs sm:text-sm shadow-inner">
<span class="flex items-center gap-1.5 text-amber-400 font-bold">
<span class="w-2 h-2 rounded-full bg-amber-400 animate-ping"></span>
翘头倾角: <strong id="pitch-readout" class="font-mono">27.5°</strong>
</span>
<span class="text-slate-600">|</span>
<span class="text-sky-300 font-bold">
牵引拉力: <strong id="tension-readout" class="font-mono">42.8 kN</strong>
</span>
<span class="text-slate-600 hidden sm:inline">|</span>
<span class="text-emerald-400 font-bold hidden sm:inline">
地速: <strong id="speed-readout" class="font-mono">38 km/h</strong>
</span>
</div>
</header>
<!-- Main SVG Animation Viewport -->
<main class="w-full max-w-6xl flex-1 flex items-center justify-center">
<div class="scene-container relative w-full aspect-[16/10] max-h-[74vh] rounded-3xl overflow-hidden bg-sky-900 border border-slate-700/70">
<svg
id="main-svg"
viewBox="0 0 1280 800"
preserveAspectRatio="xMidYMid slice"
class="w-full h-full block"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<!-- Sky Gradients for Day / Sunset / Night -->
<linearGradient id="sky-grad-day" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#0284c7" />
<stop offset="48%" stop-color="#38bdf8" />
<stop offset="85%" stop-color="#bae6fd" />
<stop offset="100%" stop-color="#fef3c7" />
</linearGradient>
<linearGradient id="sky-grad-sunset" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#1e1b4b" />
<stop offset="35%" stop-color="#7c2d12" />
<stop offset="70%" stop-color="#ea580c" />
<stop offset="92%" stop-color="#facc15" />
<stop offset="100%" stop-color="#fef08a" />
</linearGradient>
<linearGradient id="sky-grad-night" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#020617" />
<stop offset="50%" stop-color="#0f172a" />
<stop offset="88%" stop-color="#1e293b" />
<stop offset="100%" stop-color="#334155" />
</linearGradient>
<!-- Sun / Moon Glow -->
<radialGradient id="sun-glow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#fffbeb" stop-opacity="1" />
<stop offset="35%" stop-color="#fde047" stop-opacity="0.9" />
<stop offset="70%" stop-color="#f97316" stop-opacity="0.3" />
<stop offset="100%" stop-color="#f97316" stop-opacity="0" />
</radialGradient>
<!-- Distant Mountains Gradient -->
<linearGradient id="mountain-far" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#64748b" stop-opacity="0.55" />
<stop offset="100%" stop-color="#94a3b8" stop-opacity="0.2" />
</linearGradient>
<linearGradient id="mountain-near" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#334155" stop-opacity="0.75" />
<stop offset="100%" stop-color="#475569" stop-opacity="0.9" />
</linearGradient>
<!-- Road Asphalt Gradient -->
<linearGradient id="road-grad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#334155" />
<stop offset="15%" stop-color="#1e293b" />
<stop offset="85%" stop-color="#0f172a" />
<stop offset="100%" stop-color="#020617" />
</linearGradient>
<!-- Pelican Pouch (Gular Pouch) Gradient -->
<linearGradient id="pouch-grad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#fbbf24" />
<stop offset="55%" stop-color="#f97316" />
<stop offset="100%" stop-color="#ea580c" />
</linearGradient>
<!-- Pelican Upper Beak Gradient -->
<linearGradient id="beak-grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#fde047" />
<stop offset="75%" stop-color="#f59e0b" />
<stop offset="100%" stop-color="#dc2626" />
</linearGradient>
<!-- Pelican Feather Body Gradient -->
<linearGradient id="pelican-body-grad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#ffffff" />
<stop offset="70%" stop-color="#f1f5f9" />
<stop offset="100%" stop-color="#cbd5e1" />
</linearGradient>
<!-- Bike Frame Metallic Red/Orange Gradient -->
<linearGradient id="bike-frame-grad" 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>
<!-- Chrome / Alloy Rim Gradient -->
<linearGradient id="chrome-grad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#f8fafc" />
<stop offset="50%" stop-color="#94a3b8" />
<stop offset="100%" stop-color="#475569" />
</linearGradient>
<!-- Boeing 747 Fuselage Metallic White/Silver Gradient -->
<linearGradient id="b747-fuselage-grad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#ffffff" />
<stop offset="58%" stop-color="#f8fafc" />
<stop offset="82%" stop-color="#cbd5e1" />
<stop offset="100%" stop-color="#64748b" />
</linearGradient>
<!-- Boeing 747 Tail Fin Blue/Gold Livery Gradient -->
<linearGradient id="b747-tail-grad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#0284c7" />
<stop offset="55%" stop-color="#1d4ed8" />
<stop offset="100%" stop-color="#1e3a8a" />
</linearGradient>
<!-- Jet Contrail / Exhaust Gradient -->
<linearGradient id="jet-exhaust-grad" x1="100%" y1="0%" x2="0%" y2="0%">
<stop offset="0%" stop-color="#38bdf8" stop-opacity="0.85" />
<stop offset="25%" stop-color="#e0f2fe" stop-opacity="0.65" />
<stop offset="70%" stop-color="#ffffff" stop-opacity="0.25" />
<stop offset="100%" stop-color="#ffffff" stop-opacity="0" />
</linearGradient>
<!-- Rope Pattern for Braided Look -->
<linearGradient id="rope-grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#d97706" />
<stop offset="50%" stop-color="#fde68a" />
<stop offset="100%" stop-color="#b45309" />
</linearGradient>
<!-- Soft Shadow Filter -->
<filter id="drop-shadow" x="-15%" y="-15%" width="135%" height="135%">
<feDropShadow dx="0" dy="8" stdDeviation="6" flood-color="#0f172a" flood-opacity="0.35" />
</filter>
</defs>
<!-- 1. SKY BACKGROUND -->
<rect id="sky-rect" x="0" y="0" width="1280" height="650" fill="url(#sky-grad-day)" />
<!-- Night Stars Layer (visible in night/sunset modes) -->
<g id="stars-layer" opacity="0" style="transition: opacity 0.6s ease;">
<circle cx="120" cy="80" r="1.8" fill="#fff" />
<circle cx="290" cy="45" r="2.4" fill="#fde047" />
<circle cx="460" cy="110" r="1.5" fill="#fff" />
<circle cx="650" cy="60" r="2.2" fill="#fff" />
<circle cx="820" cy="40" r="1.6" fill="#bae6fd" />
<circle cx="1040" cy="95" r="2.5" fill="#fde047" />
<circle cx="1180" cy="50" r="1.7" fill="#fff" />
<circle cx="200" cy="160" r="1.4" fill="#fff" />
<circle cx="540" cy="180" r="2.0" fill="#fff" />
<circle cx="950" cy="155" r="1.5" fill="#fff" />
</g>
<!-- Sun / Celestial Body -->
<g id="sun-group" transform="translate(210, 145)">
<circle cx="0" cy="0" r="95" fill="url(#sun-glow)" />
<circle cx="0" cy="0" r="42" fill="#fef9c3" />
</g>
<!-- 2. PARALLAX CLOUDS (Far & Mid Sky) -->
<g id="clouds-far-layer" opacity="0.72">
<g class="cloud-item" data-speed="0.35" data-x="150" data-y="110" data-scale="0.85">
<path d="M0,0 Q20,-28 52,-18 Q80,-45 120,-20 Q158,-30 175,-2 Q205,2 200,26 Q195,42 165,42 L-10,42 Q-35,42 -30,18 Q-25,0 0,0 Z" fill="#ffffff" />
</g>
<g class="cloud-item" data-speed="0.28" data-x="680" data-y="75" data-scale="0.65">
<path d="M0,0 Q20,-28 52,-18 Q80,-45 120,-20 Q158,-30 175,-2 Q205,2 200,26 Q195,42 165,42 L-10,42 Q-35,42 -30,18 Q-25,0 0,0 Z" fill="#ffffff" />
</g>
<g class="cloud-item" data-speed="0.42" data-x="1090" data-y="135" data-scale="0.95">
<path d="M0,0 Q20,-28 52,-18 Q80,-45 120,-20 Q158,-30 175,-2 Q205,2 200,26 Q195,42 165,42 L-10,42 Q-35,42 -30,18 Q-25,0 0,0 Z" fill="#ffffff" />
</g>
</g>
<!-- Distant Mountain Range (Scrolling slowly left) -->
<g id="mountains-far" transform="translate(0, 0)">
<path d="M-100,640 L110,455 L280,565 L490,410 L710,550 L920,435 L1140,560 L1320,445 L1520,570 L1700,420 L1950,640 Z" fill="url(#mountain-far)" />
</g>
<g id="mountains-near" transform="translate(0, 0)">
<path d="M-150,640 L60,520 L230,590 L420,485 L640,585 L830,500 L1050,590 L1240,490 L1460,580 L1680,495 L1900,640 Z" fill="url(#mountain-near)" />
</g>
<!-- 3. ROAD & GROUND LAYER -->
<g id="ground-layer">
<!-- Roadside Grass Verge -->
<rect x="0" y="622" width="1280" height="18" fill="#16a34a" />
<rect x="0" y="636" width="1280" height="6" fill="#15803d" />
<!-- Main Asphalt Road -->
<rect x="0" y="640" width="1280" height="160" fill="url(#road-grad)" />
<!-- Road Top Shoulder Line -->
<line x1="0" y1="646" x2="1280" y2="646" stroke="#cbd5e1" stroke-width="3" stroke-opacity="0.6" />
<!-- Scrolling Center Dashed Yellow Road Lines -->
<g id="road-dashes"></g>
<!-- Scrolling Roadside Milestones, Shrubs & Pebbles -->
<g id="road-props"></g>
<!-- Dynamic Ground Shadow directly under Rear Wheel & Bike -->
<ellipse id="bike-ground-shadow" cx="370" cy="656" rx="88" ry="11" fill="#020617" opacity="0.55" />
<!-- Shadow of the 747 far on the road -->
<ellipse id="plane-ground-shadow" cx="920" cy="662" rx="145" ry="9" fill="#020617" opacity="0.24" />
</g>
<!-- 4. BOEING 747 IN THE SKY (Towed by Rope, Facing Right) -->
<g id="boeing-747-group" filter="url(#drop-shadow)" transform="translate(910, 175) rotate(-8)">
<!-- Jet Engine Contrails / Airflow Streaks (Streaming Left behind engines) -->
<g id="jet-contrails">
<path id="contrail-1" d="M-65,42 Q-210,48 -380,38" stroke="url(#jet-exhaust-grad)" stroke-width="14" stroke-linecap="round" fill="none" />
<path id="contrail-2" d="M-15,56 Q-180,64 -340,54" stroke="url(#jet-exhaust-grad)" stroke-width="18" stroke-linecap="round" fill="none" />
<path id="contrail-3" d="M25,22 Q-120,25 -260,18" stroke="url(#jet-exhaust-grad)" stroke-width="11" stroke-linecap="round" fill="none" />
</g>
<!-- Far-side (Port/Left) Swept Wing & Far Engines (Behind Fuselage) -->
<g id="b747-far-wing">
<!-- Far Wing Surface -->
<path d="M-20,6 L-125,-34 L-152,-32 L-75,14 Z" fill="#94a3b8" stroke="#475569" stroke-width="1.5" />
<!-- Winglet Far -->
<path d="M-125,-34 L-138,-48 L-152,-32 Z" fill="#1d4ed8" />
<!-- Far Engine #1 -->
<g transform="translate(-95, -18)">
<rect x="-16" y="-7" width="28" height="14" rx="5" fill="#64748b" stroke="#334155" stroke-width="1.2" />
<ellipse cx="12" cy="0" rx="3.5" ry="6.5" fill="#1e293b" />
</g>
</g>
<!-- Tail Horizontal Stabilizer (Far side) -->
<path d="M-215,2 L-272,-12 L-285,-8 L-240,8 Z" fill="#94a3b8" />
<!-- Iconic Tall Swept Vertical Stabilizer (Tail Fin) -->
<g id="b747-tail-fin">
<path d="M-175,-6 L-248,-108 L-282,-108 L-246,4 Z" fill="url(#b747-tail-grad)" stroke="#1e3a8a" stroke-width="2" />
<!-- Decorative Airline Golden Speedbird / Globe Emblem on Tail -->
<path d="M-208,-48 Q-235,-52 -260,-78 Q-238,-42 -222,-32 Z" fill="#fbbf24" />
<circle cx="-238" cy="-65" r="8" fill="#fde047" opacity="0.9" />
<!-- Rudder Divider Line -->
<line x1="-256" y1="-108" x2="-232" y2="-2" stroke="#93c5fd" stroke-width="1.2" stroke-opacity="0.6" />
</g>
<!-- MAIN FUSELAGE with Iconic Boeing 747 Upper Deck Hump ("Queen of the Skies" 鹅头双层隆起) -->
<!-- Facing Right: Tail is on Left (-265), Nose Cone is on Right (+215) -->
<path
d="M -265,2
L -235,-8
L -70,-12
C -30,-12, 5,-28, 42,-32
C 85,-35, 128,-33, 152,-20
C 168,-11, 182,-4, 198,3
C 218,11, 224,20, 212,27
C 198,35, 165,38, 120,38
L -145,36
C -195,35, -242,20, -265,8
Z"
fill="url(#b747-fuselage-grad)"
stroke="#334155"
stroke-width="2.2"
/>
<!-- Classic Airline Red & Blue Cheatline Stripe along Fuselage -->
<path
d="M -252,4 L 198,12 C 208,15 212,19 208,22 L -246,12 Z"
fill="#dc2626"
/>
<path
d="M -246,12 L 208,22 C 204,25 196,27 185,27 L -238,18 Z"
fill="#1d4ed8"
/>
<!-- Nose Radome Cone Shading (Right Tip) -->
<path
d="M 196,2 C 216,10, 224,20, 212,27 C 204,31, 196,33, 190,34 C 194,22, 195,12, 196,2 Z"
fill="#1e293b"
/>
<!-- Cockpit Windshield Windows (Nestled on the front slope of the 747 Upper Deck Hump!) -->
<path
d="M 135,-24 L 154,-16 L 150,-8 L 130,-12 Z"
fill="#0f172a"
stroke="#38bdf8"
stroke-width="1.2"
/>
<line x1="142" y1="-21" x2="140" y2="-10" stroke="#94a3b8" stroke-width="1.2" />
<!-- Iconic Upper Deck Passenger Windows (2nd Floor Hump Windows!) -->
<g fill="#0f172a" stroke="#64748b" stroke-width="0.8">
<rect x="42" y="-23" width="6" height="8" rx="2.5" />
<rect x="54" y="-23" width="6" height="8" rx="2.5" />
<rect x="66" y="-23" width="6" height="8" rx="2.5" />
<rect x="78" y="-23" width="6" height="8" rx="2.5" />
<rect x="90" y="-23" width="6" height="8" rx="2.5" />
<rect x="102" y="-22" width="6" height="8" rx="2.5" />
<rect x="114" y="-21" width="6" height="8" rx="2.5" />
</g>
<!-- Main Deck Passenger Windows & Cabin Doors -->
<g fill="#0f172a">
<!-- Forward Section Windows -->
<rect x="155" y="0" width="5.5" height="7.5" rx="2" />
<rect x="143" y="0" width="5.5" height="7.5" rx="2" />
<rect x="131" y="0" width="5.5" height="7.5" rx="2" />
<rect x="119" y="0" width="5.5" height="7.5" rx="2" />
<!-- Door 1 -->
<rect x="102" y="-3" width="10" height="16" rx="2" fill="none" stroke="#475569" stroke-width="1.3" />
<!-- Mid Section Windows -->
<rect x="85" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="73" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="61" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="49" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="37" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="25" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="13" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="1" y="-1" width="5.5" height="7.5" rx="2" />
<!-- Door 2 -->
<rect x="-15" y="-3" width="10" height="16" rx="2" fill="none" stroke="#475569" stroke-width="1.3" />
<!-- Aft Section Windows -->
<rect x="-32" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="-44" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="-56" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="-68" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="-80" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="-92" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="-104" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="-116" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="-128" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="-140" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="-152" y="-1" width="5.5" height="7.5" rx="2" />
<rect x="-164" y="-1" width="5.5" height="7.5" rx="2" />
<!-- Aft Door -->
<rect x="-184" y="-3" width="10" height="16" rx="2" fill="none" stroke="#475569" stroke-width="1.3" />
</g>
<!-- "747 PELICAN AIR" Fuselage Title -->
<text x="-35" y="-13" font-size="9.5" font-weight="900" fill="#1e3a8a" letter-spacing="1.5">BOEING 747-400</text>
<!-- Near-Side (Starboard/Right) Swept Main Wing & Two Big Underwing Turbofan Engines -->
<g id="b747-near-wing">
<!-- Wing Root Fairing -->
<ellipse cx="15" cy="28" rx="58" ry="9" fill="#e2e8f0" stroke="#94a3b8" stroke-width="1" />
<!-- Swept Main Wing Surface (extending back and down-left in perspective) -->
<path
d="M 55,22 L -95,62 L -132,60 L -25,20 Z"
fill="#f1f5f9"
stroke="#475569"
stroke-width="2"
/>
<!-- Wing Flap Corrugations -->
<line x1="-10" y1="28" x2="-102" y2="57" stroke="#94a3b8" stroke-width="1.4" />
<!-- Iconic 747-400 Angled Winglet (Near Wingtip) -->
<path d="M -95,62 L -114,44 L -132,60 Z" fill="#dc2626" stroke="#991b1b" stroke-width="1.2" />
<!-- Near-Side Engine #3 (Outboard Underwing Pylon & Turbofan Pod) -->
<g transform="translate(-52, 56)">
<!-- Pylon Strut -->
<path d="M -5,-8 L 8,-12 L 14,-2 L -2,-2 Z" fill="#94a3b8" />
<!-- Exhaust Nozzle Cone -->
<path d="M -24,-6 L -12,-9 L -12,9 L -24,6 Z" fill="#334155" />
<!-- Engine Cowling Nacelle -->
<rect x="-14" y="-11" width="36" height="22" rx="7" fill="#ffffff" stroke="#334155" stroke-width="1.8" />
<line x1="-6" y1="-10" x2="-6" y2="10" stroke="#dc2626" stroke-width="2.5" />
<!-- Front Air Intake & Spinning Fan Blades -->
<ellipse cx="22" cy="0" rx="5" ry="10" fill="#0f172a" stroke="#cbd5e1" stroke-width="1.5" />
<g transform="translate(22, 0)">
<g class="turbine-spin">
<line x1="-3" y1="-7" x2="3" y2="7" stroke="#94a3b8" stroke-width="1.6" />
<line x1="-3" y1="7" x2="3" y2="-7" stroke="#94a3b8" stroke-width="1.6" />
<circle cx="0" cy="0" r="2.2" fill="#f8fafc" />
</g>
</g>
</g>
<!-- Near-Side Engine #2 (Inboard Underwing Pylon & Larger Turbofan Pod) -->
<g transform="translate(8, 42)">
<!-- Pylon Strut -->
<path d="M -6,-10 L 10,-15 L 16,-3 L -2,-3 Z" fill="#94a3b8" />
<!-- Exhaust Nozzle Cone -->
<path d="M -28,-7 L -14,-11 L -14,11 L -28,7 Z" fill="#334155" />
<!-- Engine Cowling Nacelle -->
<rect x="-16" y="-13" width="42" height="26" rx="8" fill="#ffffff" stroke="#334155" stroke-width="2" />
<line x1="-6" y1="-12" x2="-6" y2="12" stroke="#dc2626" stroke-width="3" />
<!-- Front Air Intake & Spinning Fan Blades -->
<ellipse cx="26" cy="0" rx="5.5" ry="12" fill="#0f172a" stroke="#cbd5e1" stroke-width="1.8" />
<g transform="translate(26, 0)">
<g class="turbine-spin">
<line x1="-3.5" y1="-8.5" x2="3.5" y2="8.5" stroke="#cbd5e1" stroke-width="1.8" />
<line x1="-3.5" y1="8.5" x2="3.5" y2="-8.5" stroke="#cbd5e1" stroke-width="1.8" />
<circle cx="0" cy="0" r="2.5" fill="#f8fafc" />
</g>
</g>
</g>
</g>
<!-- Tail Horizontal Stabilizer (Near side) -->
<path d="M -205,12 L -268,26 L -284,22 L -235,8 Z" fill="#e2e8f0" stroke="#475569" stroke-width="1.6" />
<!-- Tow Hook / Bridle Ring under Forward Fuselage (at local coordinates (125, 39)) -->
<g id="b747-tow-ring" transform="translate(125, 39)">
<path d="M -10,-2 L 10,-2 L 6,7 L -6,7 Z" fill="#f59e0b" stroke="#78350f" stroke-width="1.5" />
<circle cx="0" cy="9" r="5.5" fill="none" stroke="#fbbf24" stroke-width="3" />
</g>
<!-- Blinking Navigation Strobes -->
<circle cx="145" cy="-26" r="3.5" fill="#ef4444">
<animate attributeName="opacity" values="1;0.1;1" dur="0.9s" repeatCount="indefinite" />
</circle>
<circle cx="-114" cy="44" r="3" fill="#22c55e">
<animate attributeName="opacity" values="0.2;1;0.2" dur="1.1s" repeatCount="indefinite" />
</circle>
<circle cx="-265" cy="-108" r="3.5" fill="#ffffff">
<animate attributeName="opacity" values="1;0;1" dur="0.7s" repeatCount="indefinite" />
</circle>
</g>
<!-- 5. TOW ROPE CONNECTING BIKE HANDLEBAR TO BOEING 747 -->
<g id="tow-rope-layer">
<!-- Rope Shadow / Outer Casing -->
<path id="tow-rope-outline" d="" fill="none" stroke="#451a03" stroke-width="8" stroke-linecap="round" />
<!-- Main Braided Rope Core -->
<path id="tow-rope-main" d="" fill="none" stroke="url(#rope-grad)" stroke-width="5" stroke-linecap="round" />
<!-- Dashed Twist Texture along Rope -->
<path id="tow-rope-twist" d="" fill="none" stroke="#78350f" stroke-width="4" stroke-dasharray="7 9" stroke-linecap="round" />
<!-- Red Aviation Tow Pennant Flag fluttering in the middle of the rope -->
<g id="rope-pennant" transform="translate(700, 260)">
<path id="pennant-shape" d="M 0,0 L -38,12 L -28,0 L -42,-10 Z" fill="#ef4444" stroke="#991b1b" stroke-width="1.5" />
<circle cx="0" cy="0" r="3.5" fill="#fde047" stroke="#78350f" stroke-width="1.2" />
</g>
</g>
<!-- 6. PELICAN & WHEELIE BICYCLE ASSEMBLY (Pivoting around Rear Wheel Axle (350, 576)) -->
<!-- Rear Wheel radius = 66, so bottom of tire touches road at y = 576 + 66 = 642 -->
<g id="wheelie-assembly" transform="translate(0, 0)">
<!-- 6A. PELICAN FAR LEG (Left Leg behind bike frame, dynamically driven by IK) -->
<g id="pelican-left-leg">
<!-- Left Thigh -->
<line id="leg-l-thigh" x1="370" y1="430" x2="430" y2="480" stroke="#e2e8f0" stroke-width="14" stroke-linecap="round" />
<!-- Left Knee Joint -->
<circle id="leg-l-knee" cx="430" cy="480" r="6" fill="#ea580c" />
<!-- Left Orange Scaly Shin -->
<line id="leg-l-shin" x1="430" y1="480" x2="445" y2="540" stroke="#ea580c" stroke-width="8.5" stroke-linecap="round" />
<!-- Left Webbed Foot on Left Pedal -->
<path id="leg-l-foot" d="M 435,540 L 465,536 L 460,546 L 435,545 Z" fill="#c2410c" stroke="#7c2d12" stroke-width="1.5" />
</g>
<!-- Far Pedal Crank Arm (Left Crank behind frame) -->
<g id="crank-left-group">
<line id="crank-l-arm" x1="452" y1="538" x2="452" y2="568" stroke="#64748b" stroke-width="7" stroke-linecap="round" />
<rect id="pedal-l-body" x="438" y="564" width="28" height="8" rx="3" fill="#1e293b" stroke="#475569" stroke-width="1.5" />
</g>
<!-- Pelican Far Wing (Left Wing reaching toward handlebar behind neck) -->
<path
id="pelican-left-wing"
d="M 412,385 Q 475,365 538,352 Q 492,390 422,405 Z"
fill="#cbd5e1"
stroke="#64748b"
stroke-width="2"
/>
<!-- 6B. REAR WHEEL (Supporting the entire wheelie on the road at (350, 576)) -->
<g id="rear-wheel-center" transform="translate(350, 576)">
<g id="rear-wheel-spin">
<!-- Outer Rubber Tire -->
<circle cx="0" cy="0" r="66" fill="none" stroke="#0f172a" stroke-width="13" />
<!-- Tire Tread Notches -->
<circle cx="0" cy="0" r="66" fill="none" stroke="#334155" stroke-width="13" stroke-dasharray="8 12" />
<!-- Alloy Rim -->
<circle cx="0" cy="0" r="56" fill="none" stroke="url(#chrome-grad)" stroke-width="4.5" />
<!-- High-visibility Orange Rim Stripe -->
<circle cx="0" cy="0" r="52" fill="none" stroke="#f97316" stroke-width="2" stroke-dasharray="28 18" />
<!-- Wheel Spokes (12 spokes) -->
<g stroke="#cbd5e1" stroke-width="1.8">
<line x1="-54" y1="0" x2="54" y2="0" />
<line x1="0" y1="-54" x2="0" y2="54" />
<line x1="-38" y1="-38" x2="38" y2="38" />
<line x1="-38" y1="38" x2="38" y2="-38" />
<line x1="-47" y1="-27" x2="47" y2="27" />
<line x1="-27" y1="-47" x2="27" y2="47" />
<line x1="-47" y1="27" x2="47" y2="-27" />
<line x1="-27" y1="47" x2="27" y2="-47" />
</g>
<!-- Gold Tire Valve & Reflector -->
<rect x="28" y="-8" width="12" height="6" rx="2" fill="#facc15" stroke="#ca8a04" stroke-width="1" />
<!-- Rear Sprocket Cassette -->
<circle cx="0" cy="0" r="15" fill="#475569" stroke="#1e293b" stroke-width="2" stroke-dasharray="4 3" />
</g>
<!-- Rear Hub Center -->
<circle cx="0" cy="0" r="8" fill="#e2e8f0" stroke="#1e293b" stroke-width="2.5" />
</g>
<!-- 6C. FRONT WHEEL (Lifted high in the air doing a wheelie at (622, 456)!) -->
<g id="front-wheel-center" transform="translate(622, 456)">
<!-- Motion Blur Arc around elevated spinning front wheel -->
<path d="M -76,-15 A 78,78 0 0,1 65,-42" fill="none" stroke="#ffffff" stroke-width="2.5" stroke-opacity="0.45" stroke-dasharray="18 12" />
<g id="front-wheel-spin">
<!-- Outer Rubber Tire -->
<circle cx="0" cy="0" r="66" fill="none" stroke="#0f172a" stroke-width="13" />
<!-- Tire Tread Notches -->
<circle cx="0" cy="0" r="66" fill="none" stroke="#334155" stroke-width="13" stroke-dasharray="8 12" />
<!-- Alloy Rim -->
<circle cx="0" cy="0" r="56" fill="none" stroke="url(#chrome-grad)" stroke-width="4.5" />
<!-- High-visibility Orange Rim Stripe -->
<circle cx="0" cy="0" r="52" fill="none" stroke="#f97316" stroke-width="2" stroke-dasharray="28 18" />
<!-- Wheel Spokes -->
<g stroke="#cbd5e1" stroke-width="1.8">
<line x1="-54" y1="0" x2="54" y2="0" />
<line x1="0" y1="-54" x2="0" y2="54" />
<line x1="-38" y1="-38" x2="38" y2="38" />
<line x1="-38" y1="38" x2="38" y2="-38" />
<line x1="-47" y1="-27" x2="47" y2="27" />
<line x1="-27" y1="-47" x2="27" y2="47" />
<line x1="-47" y1="27" x2="47" y2="-27" />
<line x1="-27" y1="47" x2="27" y2="-47" />
</g>
<!-- Reflector -->
<rect x="-40" y="-8" width="12" height="6" rx="2" fill="#facc15" stroke="#ca8a04" stroke-width="1" />
<!-- Front Brake Disc -->
<circle cx="0" cy="0" r="16" fill="none" stroke="#94a3b8" stroke-width="3" stroke-dasharray="5 3" />
</g>
<!-- Front Hub Center -->
<circle cx="0" cy="0" r="8" fill="#e2e8f0" stroke="#1e293b" stroke-width="2.5" />
</g>
<!-- 6D. BICYCLE DRIVETRAIN & FRAME (Pitched up in Wheelie Posture, Facing Right) -->
<g id="bicycle-frame">
<!-- Drive Chain Loop connecting Rear Hub (350, 576) and Bottom Bracket (452, 538) -->
<path
id="bike-chain"
d="M 346,562 L 448,516 A 22,22 0 1,1 456,560 L 354,590 A 14,14 0 1,1 346,562 Z"
fill="none"
stroke="#1e293b"
stroke-width="4"
stroke-dasharray="6 4"
/>
<!-- Rear Fender / Mudguard -->
<path d="M 278,568 A 74,74 0 0,1 368,504" fill="none" stroke="#f59e0b" stroke-width="6" stroke-linecap="round" />
<!-- Frame Tubes (Red Metallic Tubing) -->
<!-- Chainstay: Rear Axle (350,576) -> Bottom Bracket (452,538) -->
<line x1="350" y1="576" x2="452" y2="538" stroke="url(#bike-frame-grad)" stroke-width="10" stroke-linecap="round" />
<!-- Seatstay: Rear Axle (350,576) -> Seat Cluster (384,442) -->
<line x1="350" y1="576" x2="384" y2="442" stroke="url(#bike-frame-grad)" stroke-width="9" stroke-linecap="round" />
<!-- Seat Tube: Bottom Bracket (452,538) -> Seat Cluster (384,442) -->
<line x1="452" y1="538" x2="384" y2="442" stroke="url(#bike-frame-grad)" stroke-width="11" stroke-linecap="round" />
<!-- Down Tube: Bottom Bracket (452,538) -> Head Tube Lower (562,406) -->
<line x1="452" y1="538" x2="562" y2="406" stroke="url(#bike-frame-grad)" stroke-width="13" stroke-linecap="round" />
<!-- Top Tube: Seat Cluster (384,442) -> Head Tube Upper (546,374) -->
<line x1="384" y1="442" x2="546" y2="374" stroke="url(#bike-frame-grad)" stroke-width="11" stroke-linecap="round" />
<!-- Frame Brand Decal -->
<text x="458" y="476" transform="rotate(-48, 458, 476)" font-size="8.5" font-weight="900" fill="#fef08a" letter-spacing="1.2">PELICAN-PRO</text>
<!-- Seatpost & Saddle -->
<line x1="384" y1="442" x2="372" y2="420" stroke="#94a3b8" stroke-width="8" stroke-linecap="round" />
<path d="M 342,418 Q 372,412 402,420 Q 396,430 370,428 Q 348,428 342,418 Z" fill="#1e293b" stroke="#0f172a" stroke-width="2" />
<!-- Front Fork: Head Tube Lower (562,406) -> Front Wheel Axle (622,456) -->
<path d="M 558,402 L 622,456" stroke="url(#chrome-grad)" stroke-width="10" stroke-linecap="round" />
<path d="M 558,402 L 622,456" stroke="#f59e0b" stroke-width="4" stroke-linecap="round" />
<!-- Head Tube & Handlebar Stem (Facing Right) -->
<line x1="542" y1="368" x2="566" y2="412" stroke="url(#bike-frame-grad)" stroke-width="14" stroke-linecap="round" />
<!-- Chrome Riser Stem & Curved Cruiser/BMX Handlebars -->
<path d="M 544,370 L 558,342 L 535,346" fill="none" stroke="#e2e8f0" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" />
<!-- Classic Vintage Headlight on Front of Stem (Facing Right!) -->
<path d="M 562,350 L 578,344 L 578,360 L 562,356 Z" fill="#facc15" stroke="#ca8a04" stroke-width="1.5" />
<!-- Headlight Beam Cone -->
<path id="headlight-beam" d="M 578,346 L 675,315 L 685,375 L 578,358 Z" fill="#fef08a" opacity="0.22" />
<!-- TOW ROPE KNOT ON HANDLEBAR STEM (Exact anchor point at local (562, 342)) -->
<g id="handlebar-rope-knot" transform="translate(562, 342)">
<circle cx="0" cy="0" r="8" fill="#d97706" stroke="#78350f" stroke-width="2.2" />
<path d="M -5,-4 Q 0,-9 6,-3 Q 4,5 -4,4 Z" fill="#fbbf24" stroke="#78350f" stroke-width="1.5" />
</g>
<!-- Handlebar Rubber Grip (at (532, 347)) -->
<rect x="518" y="341" width="22" height="10" rx="5" transform="rotate(-12, 529, 346)" fill="#0f172a" stroke="#f59e0b" stroke-width="1.5" />
<!-- Bottom Bracket Chainring (at (452, 538)) -->
<g transform="translate(452, 538)">
<g id="chainring-spin">
<circle cx="0" cy="0" r="23" fill="#334155" stroke="#94a3b8" stroke-width="3" stroke-dasharray="5 3" />
<circle cx="0" cy="0" r="16" fill="none" stroke="#cbd5e1" stroke-width="2" />
<line x1="-20" y1="0" x2="20" y2="0" stroke="#94a3b8" stroke-width="3" />
<line x1="0" y1="-20" x2="0" y2="20" stroke="#94a3b8" stroke-width="3" />
</g>
<circle cx="0" cy="0" r="6" fill="#e2e8f0" />
</g>
</g>
<!-- 6E. PELICAN BODY, NECK, HEAD & ICONIC GULAR POUCH (Facing Right) -->
<g id="pelican-character">
<!-- Plump White Feathered Torso sitting on Saddle -->
<path
d="M 332,402
L 310,392
L 325,384
L 308,374
L 335,372
C 355,342, 412,338, 434,368
C 448,388, 436,422, 405,426
C 374,430, 346,420, 332,402 Z"
fill="url(#pelican-body-grad)"
stroke="#475569"
stroke-width="2.5"
stroke-linejoin="round"
/>
<!-- Subtle Feather Wing Scallops on Back -->
<path d="M 358,366 Q 382,376 404,364" fill="none" stroke="#94a3b8" stroke-width="2" stroke-linecap="round" />
<path d="M 350,382 Q 376,392 400,378" fill="none" stroke="#94a3b8" stroke-width="2" stroke-linecap="round" />
<!-- Pelican Long Curved S-Neck & Head (bobbing slightly with pedaling effort) -->
<g id="pelican-head-group" transform="translate(0, 0)">
<!-- S-Curved White Neck rising from Chest (422, 368) to Head (458, 268) -->
<path
d="M 412,374
C 402,340, 422,300, 436,274
C 442,256, 464,250, 476,262
C 484,272, 476,292, 464,306
C 448,326, 438,354, 434,378 Z"
fill="url(#pelican-body-grad)"
stroke="#475569"
stroke-width="2.5"
/>
<!-- Tuft of Feathers on Crown of Head -->
<path d="M 442,256 L 426,244 L 446,250 L 434,236 L 456,248 Z" fill="#f8fafc" stroke="#475569" stroke-width="2" stroke-linejoin="round" />
<!-- ICONIC GIANT PELICAN THROAT POUCH (Gular Pouch - 弹性大喉囊, Facing Right) -->
<path
id="pelican-pouch"
d="M 472,278
L 598,282
C 592,324, 548,344, 504,334
C 476,328, 464,304, 472,278 Z"
fill="url(#pouch-grad)"
stroke="#9a3412"
stroke-width="2.5"
stroke-linejoin="round"
/>
<!-- Pouch Stretch Ribs / Striations -->
<path d="M 488,284 Q 505,318 532,326" fill="none" stroke="#c2410c" stroke-width="1.6" stroke-opacity="0.65" />
<path d="M 518,284 Q 532,314 558,318" fill="none" stroke="#c2410c" stroke-width="1.6" stroke-opacity="0.65" />
<!-- Cute Little Fish Tail Poking Out of the Pelican's Beak Pouch! -->
<g id="pouch-fish" transform="translate(556, 280)">
<path d="M 0,0 L 16,-12 L 13,-1 L 20,6 L 2,3 Z" fill="#38bdf8" stroke="#0369a1" stroke-width="1.5" />
</g>
<!-- LONG UPPER BEAK with Hooked Tip (Pointing Right!) -->
<path
d="M 472,262
L 596,274
C 606,275, 610,283, 603,290
L 597,293
L 594,284
L 470,280 Z"
fill="url(#beak-grad)"
stroke="#9a3412"
stroke-width="2.2"
stroke-linejoin="round"
/>
<!-- Pelican Eye & Cool Aviator Sunglasses -->
<circle cx="464" cy="266" r="7.5" fill="#fef08a" stroke="#475569" stroke-width="1.8" />
<circle cx="466" cy="266" r="3.5" fill="#0f172a" />
<circle cx="467.5" cy="264.5" r="1.3" fill="#ffffff" />
<!-- Aviator Captain Cap on Pelican's Head -->
<g transform="translate(454, 246) rotate(-10)">
<path d="M -18,4 C -16,-10, 12,-12, 18,2 Z" fill="#1e3a8a" stroke="#0f172a" stroke-width="1.8" />
<path d="M -16,3 L 26,3 L 22,8 L -16,6 Z" fill="#0f172a" />
<circle cx="2" cy="-2" r="3.5" fill="#fbbf24" />
</g>
<!-- Fluttering Red Aviator Scarf Streaming Left in the Wind -->
<g id="pelican-scarf" transform="translate(432, 332)">
<path
id="scarf-tail"
d="M 0,0 Q -38,-12 -72,4 Q -98,16 -122,2 Q -90,26 -56,16 Q -25,8 4,12 Z"
fill="#ef4444"
stroke="#991b1b"
stroke-width="1.8"
/>
<ellipse cx="4" cy="6" rx="14" ry="7" transform="rotate(18)" fill="#dc2626" stroke="#991b1b" stroke-width="1.8" />
</g>
</g>
<!-- Near Pedal Crank Arm (Right Crank in foreground) -->
<g id="crank-right-group">
<line id="crank-r-arm" x1="452" y1="538" x2="452" y2="510" stroke="#cbd5e1" stroke-width="7.5" stroke-linecap="round" />
<rect id="pedal-r-body" x="438" y="506" width="28" height="8" rx="3" fill="#0f172a" stroke="#f59e0b" stroke-width="1.8" />
</g>
<!-- 6F. PELICAN NEAR LEG (Right Leg in foreground, dynamically driven by IK) -->
<g id="pelican-right-leg">
<!-- Fluffy White Upper Thigh -->
<line id="leg-r-thigh" x1="382" y1="422" x2="442" y2="468" stroke="#ffffff" stroke-width="16" stroke-linecap="round" />
<line id="leg-r-thigh-inner" x1="382" y1="422" x2="442" y2="468" stroke="#f1f5f9" stroke-width="12" stroke-linecap="round" />
<!-- Orange Knee Joint -->
<circle id="leg-r-knee" cx="442" cy="468" r="6.5" fill="#f97316" stroke="#9a3412" stroke-width="1.5" />
<!-- Orange Lower Shin -->
<line id="leg-r-shin" x1="442" y1="468" x2="452" y2="510" stroke="#f97316" stroke-width="9" stroke-linecap="round" />
<!-- Webbed Orange Foot firmly pressing the Right Pedal -->
<path id="leg-r-foot" d="M 442,510 L 474,506 L 468,516 L 442,515 Z" fill="#fb923c" stroke="#9a3412" stroke-width="1.8" />
</g>
<!-- 6G. PELICAN NEAR WING (Right Wing stretching forward to grip the Handlebar!) -->
<g id="pelican-right-wing-group">
<path
id="pelican-right-wing"
d="M 402,378
C 438,358, 488,342, 532,346
C 538,347, 540,355, 530,358
C 492,368, 448,398, 412,402
C 396,402, 392,386, 402,378 Z"
fill="#ffffff"
stroke="#475569"
stroke-width="2.5"
stroke-linejoin="round"
/>
<!-- Wing Primary Feather Details gripping the handlebar -->
<path d="M 442,384 Q 482,368 522,354" fill="none" stroke="#94a3b8" stroke-width="2" stroke-linecap="round" />
<path d="M 426,392 Q 468,378 510,360" fill="none" stroke="#94a3b8" stroke-width="2" stroke-linecap="round" />
</g>
</g>
<!-- Wheelie Balance Sparks / Tire Dust Particles behind Rear Wheel Contact Point -->
<g id="wheel-dust-layer"></g>
</g>
<!-- 7. FOREGROUND WIND SPEED STREAKS (Moving Left to emphasize rightward motion) -->
<g id="wind-lines-layer"></g>
<!-- 8. ON-CANVAS VECTOR LABELS (Subtle callouts) -->
<g id="hud-annotations" opacity="0.88" class="pointer-events-none">
<!-- Callout badge near 747 Hump -->
<g id="callout-747" transform="translate(880, 68)">
<rect x="-95" y="-18" width="190" height="26" rx="13" fill="#0f172a" fill-opacity="0.72" stroke="#38bdf8" stroke-width="1.2" />
<text x="0" y="-1" text-anchor="middle" fill="#e0f2fe" font-size="11" font-weight="700">✈️ 波音747 (可按住拖动位置)</text>
</g>
</g>
</svg>
<!-- Floating Boost Toast Notification -->
<div
id="boost-toast"
class="pointer-events-none absolute top-4 left-4 bg-amber-500/95 text-slate-950 font-black text-xs sm:text-sm px-3.5 py-1.5 rounded-full shadow-lg opacity-0 transition-opacity duration-300"
>
🔥 涡轮蹬车加速!波音747拉升中!
</div>
</div>
</main>
<!-- Interactive Control Panel -->
<footer class="w-full max-w-6xl mt-3 bg-slate-900/90 border border-slate-800 rounded-2xl p-3 sm:px-6 sm:py-3.5 flex flex-wrap items-center justify-between gap-4 shadow-xl">
<!-- Sliders Group -->
<div class="flex flex-wrap items-center gap-4 sm:gap-6">
<!-- Speed Control -->
<label class="flex items-center gap-2.5 text-xs sm:text-sm font-medium text-slate-300">
<span>🚴 骑行速度</span>
<input
id="speed-slider"
type="range"
min="0.3"
max="2.6"
step="0.1"
value="1.2"
class="w-24 sm:w-32 cursor-pointer"
/>
</label>
<!-- Wheelie Pitch Bias Control -->
<label class="flex items-center gap-2.5 text-xs sm:text-sm font-medium text-slate-300">
<span>📐 翘头高度</span>
<input
id="wheelie-slider"
type="range"
min="-14"
max="14"
step="1"
value="2"
class="w-24 sm:w-32 cursor-pointer"
/>
</label>
<!-- Rope Length / Plane Altitude Control -->
<label class="flex items-center gap-2.5 text-xs sm:text-sm font-medium text-slate-300">
<span>🪢 放飞高度</span>
<input
id="altitude-slider"
type="range"
min="95"
max="310"
step="5"
value="175"
class="w-24 sm:w-32 cursor-pointer"
/>
</label>
</div>
<!-- Action Buttons & Theme Switcher -->
<div class="flex items-center gap-2.5">
<!-- Sky Theme Buttons -->
<div class="flex bg-slate-950 p-1 rounded-xl border border-slate-800">
<button
data-theme="day"
class="theme-btn px-2.5 py-1 rounded-lg text-xs font-bold bg-sky-500 text-slate-950 transition"
>
☀️ 晴空
</button>
<button
data-theme="sunset"
class="theme-btn px-2.5 py-1 rounded-lg text-xs font-bold text-slate-400 hover:text-white transition"
>
🌇 晚霞
</button>
<button
data-theme="night"
class="theme-btn px-2.5 py-1 rounded-lg text-xs font-bold text-slate-400 hover:text-white transition"
>
🌙 星夜
</button>
</div>
<!-- Boost Stunt Action Button -->
<button
id="boost-btn"
class="px-4 py-2 rounded-xl font-black text-xs sm:text-sm bg-gradient-to-r from-amber-400 via-orange-500 to-red-500 text-slate-950 shadow-lg shadow-orange-500/25 hover:brightness-110 active:scale-95 transition flex items-center gap-1.5"
>
<span>⚡ 猛踩特技拉升</span>
</button>
</div>
</footer>
<script>
window.onload = function () {
const svgNS = 'http://www.w3.org/2000/svg';
// DOM Element References
const mainSvg = document.getElementById('main-svg');
const wheelieAssembly = document.getElementById('wheelie-assembly');
const rearWheelSpin = document.getElementById('rear-wheel-spin');
const frontWheelSpin = document.getElementById('front-wheel-spin');
const chainringSpin = document.getElementById('chainring-spin');
const bikeChain = document.getElementById('bike-chain');
// Pelican & IK Leg elements
const pelicanHeadGroup = document.getElementById('pelican-head-group');
const pelicanPouch = document.getElementById('pelican-pouch');
const pouchFish = document.getElementById('pouch-fish');
const scarfTail = document.getElementById('scarf-tail');
const pelicanRightWing = document.getElementById('pelican-right-wing');
const crankRArm = document.getElementById('crank-r-arm');
const pedalRBody = document.getElementById('pedal-r-body');
const crankLArm = document.getElementById('crank-l-arm');
const pedalLBody = document.getElementById('pedal-l-body');
const legRThigh = document.getElementById('leg-r-thigh');
const legRThighInner = document.getElementById('leg-r-thigh-inner');
const legRKnee = document.getElementById('leg-r-knee');
const legRShin = document.getElementById('leg-r-shin');
const legRFoot = document.getElementById('leg-r-foot');
const legLThigh = document.getElementById('leg-l-thigh');
const legLKnee = document.getElementById('leg-l-knee');
const legLShin = document.getElementById('leg-l-shin');
const legLFoot = document.getElementById('leg-l-foot');
// Boeing 747 & Tow Rope elements
const boeingGroup = document.getElementById('boeing-747-group');
const callout747 = document.getElementById('callout-747');
const towRopeOutline = document.getElementById('tow-rope-outline');
const towRopeMain = document.getElementById('tow-rope-main');
const towRopeTwist = document.getElementById('tow-rope-twist');
const ropePennant = document.getElementById('rope-pennant');
const pennantShape = document.getElementById('pennant-shape');
// Environment & Shadows
const roadDashesGroup = document.getElementById('road-dashes');
const roadPropsGroup = document.getElementById('road-props');
const windLinesLayer = document.getElementById('wind-lines-layer');
const wheelDustLayer = document.getElementById('wheel-dust-layer');
const mountainsFar = document.getElementById('mountains-far');
const mountainsNear = document.getElementById('mountains-near');
const bikeGroundShadow = document.getElementById('bike-ground-shadow');
const planeGroundShadow = document.getElementById('plane-ground-shadow');
// Telemetry & Controls
const pitchReadout = document.getElementById('pitch-readout');
const tensionReadout = document.getElementById('tension-readout');
const speedReadout = document.getElementById('speed-readout');
const speedSlider = document.getElementById('speed-slider');
const wheelieSlider = document.getElementById('wheelie-slider');
const altitudeSlider = document.getElementById('altitude-slider');
const boostBtn = document.getElementById('boost-btn');
const boostToast = document.getElementById('boost-toast');
// ============================================================================
// 1. PROCEDURAL SCENE GENERATION (Road Dashes, Pebbles/Bushes, Wind Streaks)
// ============================================================================
const roadDashes = [];
for (let i = 0; i < 12; i++) {
const rect = document.createElementNS(svgNS, 'rect');
const x = i * 120;
rect.setAttribute('x', x);
rect.setAttribute('y', 698);
rect.setAttribute('width', 64);
rect.setAttribute('height', 8);
rect.setAttribute('rx', 4);
rect.setAttribute('fill', '#facc15');
rect.setAttribute('opacity', '0.88');
roadDashesGroup.appendChild(rect);
roadDashes.push({ el: rect, x: x });
}
const roadProps = [];
for (let i = 0; i < 10; i++) {
const g = document.createElementNS(svgNS, 'g');
const x = i * 145 + (i % 2) * 30;
const isBush = i % 3 === 0;
if (isBush) {
const bush = document.createElementNS(svgNS, 'path');
bush.setAttribute('d', 'M-18,0 Q-14,-16 0,-12 Q10,-22 22,-10 Q32,-12 30,0 Z');
bush.setAttribute('fill', '#15803d');
g.appendChild(bush);
} else {
const pebble = document.createElementNS(svgNS, 'ellipse');
pebble.setAttribute('cx', 0);
pebble.setAttribute('cy', 0);
pebble.setAttribute('rx', 7 + (i % 4) * 2);
pebble.setAttribute('ry', 2.5);
pebble.setAttribute('fill', '#64748b');
g.appendChild(pebble);
}
const y = isBush ? 626 : 665 + (i % 5) * 22;
g.setAttribute('transform', `translate(${x}, ${y})`);
roadPropsGroup.appendChild(g);
roadProps.push({ el: g, x: x, y: y, speedMult: isBush ? 0.92 : 1.05 });
}
const windLines = [];
for (let i = 0; i < 9; i++) {
const line = document.createElementNS(svgNS, 'line');
const x = Math.random() * 1280;
const y = 90 + i * 62;
const len = 55 + (i % 3) * 35;
line.setAttribute('x1', x);
line.setAttribute('y1', y);
line.setAttribute('x2', x + len);
line.setAttribute('y2', y);
line.setAttribute('stroke', '#ffffff');
line.setAttribute('stroke-width', i % 2 === 0 ? '2' : '1.2');
line.setAttribute('stroke-linecap', 'round');
line.setAttribute('stroke-opacity', '0.28');
windLinesLayer.appendChild(line);
windLines.push({ el: line, x: x, y: y, len: len, speed: 1.3 + (i % 4) * 0.25 });
}
// Dust particles at rear wheel contact point (350, 642)
const dustParticles = [];
for (let i = 0; i < 8; i++) {
const c = document.createElementNS(svgNS, 'circle');
c.setAttribute('r', 4 + (i % 3) * 2.5);
c.setAttribute('fill', i % 2 === 0 ? '#fde68a' : '#cbd5e1');
wheelDustLayer.appendChild(c);
dustParticles.push({
el: c,
phase: i / 8
});
}
// ============================================================================
// 2. ANALYTICAL 2-BONE INVERSE KINEMATICS (Pelican Hip -> Knee -> Pedal)
// ============================================================================
function solveTwoBoneIK(hipX, hipY, footX, footY, L1, L2) {
const dx = footX - hipX;
const dy = footY - hipY;
let dist = Math.hypot(dx, dy);
// Clamp reach so legs never detach
const maxReach = (L1 + L2) * 0.992;
const minReach = Math.abs(L1 - L2) * 1.05;
if (dist > maxReach) dist = maxReach;
if (dist < minReach) dist = minReach;
// Law of cosines for knee bend (bending forward-right like a bird/cyclist knee)
const cosAngle = (L1 * L1 + dist * dist - L2 * L2) / (2 * L1 * dist);
const clampedCos = Math.max(-1, Math.min(1, cosAngle));
const baseAngle = Math.atan2(dy, dx);
const bendAngle = Math.acos(clampedCos);
// Knee bends upward/rightward (subtract bendAngle)
const thighAngle = baseAngle - bendAngle;
const kneeX = hipX + L1 * Math.cos(thighAngle);
const kneeY = hipY + L1 * Math.sin(thighAngle);
return { kneeX, kneeY };
}
// Helper to transform a point (x, y) rotated around pivot (px, py) by angleDeg
function rotatePointAround(x, y, px, py, angleDeg) {
const rad = (angleDeg * Math.PI) / 180;
const cos = Math.cos(rad);
const sin = Math.sin(rad);
const dx = x - px;
const dy = y - py;
return {
x: px + dx * cos - dy * sin,
y: py + dx * sin + dy * cos
};
}
// ============================================================================
// 3. ANIMATION STATE & INTERACTIVE DRAGGING FOR BOEING 747
// ============================================================================
const state = {
time: 0,
wheelAngle: 0,
crankAngle: 0,
roadOffset: 0,
mountainFarOffset: 0,
mountainNearOffset: 0,
speedMultiplier: 1.2,
wheelieBias: 2,
boostRemaining: 0,
// Boeing 747 base position in SVG coordinates
planeBaseX: 925,
planeBaseY: 175,
isDraggingPlane: false
};
// Convert client mouse/touch coordinates into SVG viewBox coordinates
function getSvgPoint(clientX, clientY) {
const pt = mainSvg.createSVGPoint();
pt.x = clientX;
pt.y = clientY;
const ctm = mainSvg.getScreenCTM();
if (!ctm) return { x: 925, y: 175 };
return pt.matrixTransform(ctm.inverse());
}
boeingGroup.addEventListener('pointerdown', (e) => {
state.isDraggingPlane = true;
boeingGroup.setPointerCapture(e.pointerId);
});
window.addEventListener('pointermove', (e) => {
if (!state.isDraggingPlane) return;
const svgPt = getSvgPoint(e.clientX, e.clientY);
state.planeBaseX = Math.max(580, Math.min(1150, svgPt.x));
state.planeBaseY = Math.max(75, Math.min(390, svgPt.y));
altitudeSlider.value = Math.round(state.planeBaseY);
});
window.addEventListener('pointerup', () => {
state.isDraggingPlane = false;
});
// UI Controls Event Listeners
speedSlider.addEventListener('input', (e) => {
state.speedMultiplier = parseFloat(e.target.value);
});
wheelieSlider.addEventListener('input', (e) => {
state.wheelieBias = parseFloat(e.target.value);
});
altitudeSlider.addEventListener('input', (e) => {
state.planeBaseY = parseFloat(e.target.value);
});
boostBtn.addEventListener('click', () => {
state.boostRemaining = 2.6; // 2.6 seconds of turbo boost
boostToast.style.opacity = '1';
setTimeout(() => {
boostToast.style.opacity = '0';
}, 2200);
});
// Theme Switcher
const skyRect = document.getElementById('sky-rect');
const starsLayer = document.getElementById('stars-layer');
const themeBtns = document.querySelectorAll('.theme-btn');
themeBtns.forEach((btn) => {
btn.addEventListener('click', () => {
const theme = btn.getAttribute('data-theme');
themeBtns.forEach((b) => {
b.className = 'theme-btn px-2.5 py-1 rounded-lg text-xs font-bold text-slate-400 hover:text-white transition';
});
btn.className = 'theme-btn px-2.5 py-1 rounded-lg text-xs font-bold bg-sky-500 text-slate-950 transition';
if (theme === 'day') {
skyRect.setAttribute('fill', 'url(#sky-grad-day)');
starsLayer.setAttribute('opacity', '0');
} else if (theme === 'sunset') {
skyRect.setAttribute('fill', 'url(#sky-grad-sunset)');
starsLayer.setAttribute('opacity', '0.45');
} else if (theme === 'night') {
skyRect.setAttribute('fill', 'url(#sky-grad-night)');
starsLayer.setAttribute('opacity', '0.95');
}
});
});
// Clouds array initialization
const cloudItems = Array.from(document.querySelectorAll('.cloud-item')).map((el) => ({
el,
speed: parseFloat(el.getAttribute('data-speed')),
x: parseFloat(el.getAttribute('data-x')),
y: parseFloat(el.getAttribute('data-y')),
scale: parseFloat(el.getAttribute('data-scale'))
}));
// ============================================================================
// 4. MAIN ANIMATION LOOP (60fps Physics & SVG Synchronization)
// ============================================================================
let lastTimestamp = performance.now();
function animate(now) {
const dt = Math.min(0.05, (now - lastTimestamp) / 1000);
lastTimestamp = now;
// Handle Turbo Boost decay
if (state.boostRemaining > 0) {
state.boostRemaining = Math.max(0, state.boostRemaining - dt);
}
const boostFactor = state.boostRemaining > 0 ? 1.85 : 1.0;
const effectiveSpeed = state.speedMultiplier * boostFactor;
state.time += dt * effectiveSpeed;
const t = state.time;
// ------------------------------------------------------------------------
// A. WHEEL ROTATION & CRANK PEDALING (Moving Forward to the Right -> Clockwise)
// ------------------------------------------------------------------------
state.wheelAngle = (state.wheelAngle + dt * effectiveSpeed * 420) % 360;
state.crankAngle = (state.crankAngle + dt * effectiveSpeed * 5.2) % (Math.PI * 2);
rearWheelSpin.setAttribute('transform', `rotate(${state.wheelAngle})`);
frontWheelSpin.setAttribute('transform', `rotate(${state.wheelAngle * 0.92})`);
chainringSpin.setAttribute('transform', `rotate(${(state.crankAngle * 180) / Math.PI})`);
bikeChain.setAttribute('stroke-dashoffset', -(t * 45) % 20);
// ------------------------------------------------------------------------
// B. WHEELIE BALANCING PITCH OSCILLATION (Pivoting at Rear Axle (350, 576))
// ------------------------------------------------------------------------
// Positive angle tilts front wheel down, negative angle lifts front wheel higher!
const altitudePull = (175 - state.planeBaseY) * 0.035;
const boostWheelieLift = state.boostRemaining > 0 ? -4.5 : 0;
const wheelieRockingDeg =
-state.wheelieBias -
altitudePull +
boostWheelieLift +
Math.sin(t * 3.2) * 2.8 +
Math.cos(t * 1.7) * 1.2;
wheelieAssembly.setAttribute(
'transform',
`rotate(${wheelieRockingDeg.toFixed(2)}, 350, 576)`
);
// Update ground shadow width based on wheelie pitch
bikeGroundShadow.setAttribute('rx', (86 + wheelieRockingDeg * 0.9).toFixed(1));
// ------------------------------------------------------------------------
// C. PELICAN PEDALING INVERSE KINEMATICS (2 Legs + Cranks + Webbed Feet)
// ------------------------------------------------------------------------
const crankCenterX = 452;
const crankCenterY = 538;
const crankRadius = 26;
// Right Pedal (Near side)
const rPedalX = crankCenterX + crankRadius * Math.cos(state.crankAngle);
const rPedalY = crankCenterY + crankRadius * Math.sin(state.crankAngle);
crankRArm.setAttribute('x2', rPedalX.toFixed(1));
crankRArm.setAttribute('y2', rPedalY.toFixed(1));
pedalRBody.setAttribute('x', (rPedalX - 14).toFixed(1));
pedalRBody.setAttribute('y', (rPedalY - 4).toFixed(1));
// Left Pedal (Far side, 180 degrees opposite)
const lPedalX = crankCenterX + crankRadius * Math.cos(state.crankAngle + Math.PI);
const lPedalY = crankCenterY + crankRadius * Math.sin(state.crankAngle + Math.PI);
crankLArm.setAttribute('x2', lPedalX.toFixed(1));
crankLArm.setAttribute('y2', lPedalY.toFixed(1));
pedalLBody.setAttribute('x', (lPedalX - 14).toFixed(1));
pedalLBody.setAttribute('y', (lPedalY - 4).toFixed(1));
// Solve IK for Right Leg (Hip at (382, 422))
const rHipX = 382, rHipY = 422;
const rIK = solveTwoBoneIK(rHipX, rHipY, rPedalX, rPedalY, 68, 66);
legRThigh.setAttribute('x2', rIK.kneeX.toFixed(1));
legRThigh.setAttribute('y2', rIK.kneeY.toFixed(1));
legRThighInner.setAttribute('x2', rIK.kneeX.toFixed(1));
legRThighInner.setAttribute('y2', rIK.kneeY.toFixed(1));
legRKnee.setAttribute('cx', rIK.kneeX.toFixed(1));
legRKnee.setAttribute('cy', rIK.kneeY.toFixed(1));
legRShin.setAttribute('x1', rIK.kneeX.toFixed(1));
legRShin.setAttribute('y1', rIK.kneeY.toFixed(1));
legRShin.setAttribute('x2', rPedalX.toFixed(1));
legRShin.setAttribute('y2', rPedalY.toFixed(1));
legRFoot.setAttribute(
'd',
`M ${(rPedalX - 10).toFixed(1)},${rPedalY.toFixed(1)} L ${(rPedalX + 22).toFixed(1)},${(rPedalY - 5).toFixed(1)} L ${(rPedalX + 16).toFixed(1)},${(rPedalY + 5).toFixed(1)} L ${(rPedalX - 10).toFixed(1)},${(rPedalY + 4).toFixed(1)} Z`
);
// Solve IK for Left Leg (Hip at (374, 426))
const lHipX = 374, lHipY = 426;
const lIK = solveTwoBoneIK(lHipX, lHipY, lPedalX, lPedalY, 68, 66);
legLThigh.setAttribute('x2', lIK.kneeX.toFixed(1));
legLThigh.setAttribute('y2', lIK.kneeY.toFixed(1));
legLKnee.setAttribute('cx', lIK.kneeX.toFixed(1));
legLKnee.setAttribute('cy', lIK.kneeY.toFixed(1));
legLShin.setAttribute('x1', lIK.kneeX.toFixed(1));
legLShin.setAttribute('y1', lIK.kneeY.toFixed(1));
legLShin.setAttribute('x2', lPedalX.toFixed(1));
legLShin.setAttribute('y2', lPedalY.toFixed(1));
legLFoot.setAttribute(
'd',
`M ${(lPedalX - 10).toFixed(1)},${lPedalY.toFixed(1)} L ${(lPedalX + 20).toFixed(1)},${(lPedalY - 5).toFixed(1)} L ${(lPedalX + 15).toFixed(1)},${(lPedalY + 5).toFixed(1)} L ${(lPedalX - 10).toFixed(1)},${(lPedalY + 4).toFixed(1)} Z`
);
// ------------------------------------------------------------------------
// D. PELICAN SECONDARY ANIMATIONS (Head Bob, Throat Pouch Jiggle, Scarf)
// ------------------------------------------------------------------------
const headBobY = Math.sin(state.crankAngle * 2) * 3.2;
const headTilt = Math.cos(state.crankAngle) * 2.2;
pelicanHeadGroup.setAttribute(
'transform',
`translate(0, ${headBobY.toFixed(2)}) rotate(${headTilt.toFixed(2)}, 445, 310)`
);
// Throat pouch elastic jiggle
const pouchSag = 342 + Math.sin(t * 7.5) * 6.5;
pelicanPouch.setAttribute(
'd',
`M 472,278 L 598,282 C 592,${(pouchSag - 16).toFixed(1)}, 548,${pouchSag.toFixed(1)}, 504,${(pouchSag - 8).toFixed(1)} C 476,328, 464,304, 472,278 Z`
);
// Little fish tail wiggling in the pouch
pouchFish.setAttribute(
'transform',
`translate(556, 280) rotate(${(Math.sin(t * 11) * 12).toFixed(1)})`
);
// Fluttering aviator scarf wave
const scarfWave1 = -12 + Math.sin(t * 12) * 9;
const scarfWave2 = 2 + Math.cos(t * 10) * 12;
scarfTail.setAttribute(
'd',
`M 0,0 Q -38,${scarfWave1.toFixed(1)} -72,4 Q -98,16 -122,${scarfWave2.toFixed(1)} Q -90,26 -56,16 Q -25,8 4,12 Z`
);
// ------------------------------------------------------------------------
// E. BOEING 747 SKY FLOATING MOTION & EXACT TOW ROPE PHYSICS
// ------------------------------------------------------------------------
const planeFloatX = state.planeBaseX + Math.sin(t * 1.4) * 16;
const planeFloatY =
state.planeBaseY +
Math.sin(t * 2.3) * 14 -
(state.boostRemaining > 0 ? 18 : 0);
const planePitchDeg = -8.5 + Math.cos(t * 2.3) * 3.2 - (state.boostRemaining > 0 ? 4 : 0);
boeingGroup.setAttribute(
'transform',
`translate(${planeFloatX.toFixed(2)}, ${planeFloatY.toFixed(2)}) rotate(${planePitchDeg.toFixed(2)})`
);
// Update callout badge above 747
callout747.setAttribute(
'transform',
`translate(${(planeFloatX - 25).toFixed(1)}, ${(planeFloatY - 108).toFixed(1)})`
);
planeGroundShadow.setAttribute('cx', planeFloatX.toFixed(1));
planeGroundShadow.setAttribute('rx', (155 - planeFloatY * 0.15).toFixed(1));
// Compute Exact World Position of Bike Handlebar Knot (local (562, 342) rotated around (350, 576))
const handlebarWorld = rotatePointAround(562, 342, 350, 576, wheelieRockingDeg);
// Compute Exact World Position of Boeing 747 Tow Ring (local (125, 45) inside boeingGroup)
const planeTowLocalRotated = rotatePointAround(125, 45, 0, 0, planePitchDeg);
const planeTowWorld = {
x: planeFloatX + planeTowLocalRotated.x,
y: planeFloatY + planeTowLocalRotated.y
};
// Construct Quadratic/Cubic Bezier Rope Curve with realistic aerodynamic sag & vibration
const midX = (handlebarWorld.x + planeTowWorld.x) * 0.5;
const midY = (handlebarWorld.y + planeTowWorld.y) * 0.5;
const ropeVibration = Math.sin(t * 14) * 4.5;
const ropeSag = 26 - effectiveSpeed * 6 + ropeVibration;
const ctrlX = midX + 18;
const ctrlY = midY + ropeSag;
const ropePathData = `M ${handlebarWorld.x.toFixed(1)},${handlebarWorld.y.toFixed(1)} Q ${ctrlX.toFixed(1)},${ctrlY.toFixed(1)} ${planeTowWorld.x.toFixed(1)},${planeTowWorld.y.toFixed(1)}`;
towRopeOutline.setAttribute('d', ropePathData);
towRopeMain.setAttribute('d', ropePathData);
towRopeTwist.setAttribute('d', ropePathData);
// Position the Red Warning Pennant at t = 0.5 on the quadratic Bezier curve
const flagX = 0.25 * handlebarWorld.x + 0.5 * ctrlX + 0.25 * planeTowWorld.x;
const flagY = 0.25 * handlebarWorld.y + 0.5 * ctrlY + 0.25 * planeTowWorld.y;
const flagFlutter = Math.sin(t * 16) * 8;
ropePennant.setAttribute(
'transform',
`translate(${flagX.toFixed(1)}, ${flagY.toFixed(1)}) rotate(${flagFlutter.toFixed(1)})`
);
// ------------------------------------------------------------------------
// F. SCROLLING ROAD, PARALLAX MOUNTAINS, CLOUDS & DUST PARTICLES
// ------------------------------------------------------------------------
const scrollPixels = dt * effectiveSpeed * 340;
// Road center dashes
roadDashes.forEach((d) => {
d.x -= scrollPixels;
if (d.x < -80) d.x += 12 * 120;
d.el.setAttribute('x', d.x.toFixed(1));
});
// Roadside bushes & pebbles
roadProps.forEach((p) => {
p.x -= scrollPixels * p.speedMult;
if (p.x < -60) p.x += 10 * 145;
p.el.setAttribute('transform', `translate(${p.x.toFixed(1)}, ${p.y})`);
});
// Foreground wind speed lines
windLines.forEach((w) => {
w.x -= scrollPixels * w.speed * 1.35;
if (w.x < -150) w.x = 1320 + Math.random() * 120;
w.el.setAttribute('x1', w.x.toFixed(1));
w.el.setAttribute('x2', (w.x + w.len).toFixed(1));
});
// Parallax clouds
cloudItems.forEach((c) => {
c.x -= scrollPixels * c.speed * 0.22;
if (c.x < -240) c.x = 1360;
c.el.setAttribute(
'transform',
`translate(${c.x.toFixed(1)}, ${c.y}) scale(${c.scale})`
);
});
// Parallax mountains
state.mountainFarOffset = (state.mountainFarOffset - scrollPixels * 0.08) % 600;
state.mountainNearOffset = (state.mountainNearOffset - scrollPixels * 0.18) % 600;
mountainsFar.setAttribute('transform', `translate(${state.mountainFarOffset.toFixed(1)}, 0)`);
mountainsNear.setAttribute('transform', `translate(${state.mountainNearOffset.toFixed(1)}, 0)`);
// Rear tire dust puffs (kicked backward to the left from (336, 638))
dustParticles.forEach((p) => {
const progress = (t * 1.9 + p.phase) % 1;
const px = 340 - progress * 115;
const py = 638 - Math.sin(progress * Math.PI) * 24 - progress * 12;
const scale = (1 - progress) * 1.3;
p.el.setAttribute('cx', px.toFixed(1));
p.el.setAttribute('cy', py.toFixed(1));
p.el.setAttribute('opacity', ((1 - progress) * 0.65).toFixed(2));
p.el.setAttribute('r', (3 + scale * 5).toFixed(1));
});
// ------------------------------------------------------------------------
// G. UPDATE LIVE TELEMETRY BADGES
// ------------------------------------------------------------------------
const displayPitch = Math.abs(26 - wheelieRockingDeg);
const ropeDist = Math.hypot(
planeTowWorld.x - handlebarWorld.x,
planeTowWorld.y - handlebarWorld.y
);
const displayTension = 35 + ropeDist * 0.045 + effectiveSpeed * 4.2;
const displaySpeed = Math.round(effectiveSpeed * 32);
pitchReadout.textContent = `${displayPitch.toFixed(1)}°`;
tensionReadout.textContent = `${displayTension.toFixed(1)} kN`;
speedReadout.textContent = `${displaySpeed} km/h`;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
};
</script>
</body>
</html>
这个厉害呀。
这尼玛有点离谱了吧
别用鹈鹕了,换成三条腿的癞蛤蟆试试
哈哈哈,离谱
牛逼啊这个