总结
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>鹈鹕骑行 · 海风道</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;700&family=ZCOOL+KuaiLe&display=swap" rel="stylesheet">
<style>
:root{
--ink:#1E3A47; --paper:#FFF6E0; --red:#E8483F; --amber:#FFB020; --sea:#1E8F9C;
}
*{ box-sizing:border-box; }
html,body{ height:100%; }
body{
margin:0; overflow:hidden; background:#2E9CCB;
font-family:'Noto Sans SC',system-ui,sans-serif; color:var(--ink);
-webkit-user-select:none; user-select:none;
}
svg#scene{ position:fixed; inset:0; width:100%; height:100%; display:block; }
.plaque{
position:fixed; background:var(--paper); border:3px solid var(--ink);
border-radius:16px; box-shadow:7px 8px 0 rgba(30,58,71,.28);
padding:14px 18px 12px; animation:drop .8s cubic-bezier(.2,1.4,.4,1) backwards;
}
@keyframes drop{ from{ opacity:0; transform:translateY(-26px) rotate(var(--rot,0deg)); }
to { opacity:1; transform:translateY(0) rotate(var(--rot,0deg)); } }
#title{ top:20px; left:20px; --rot:-2deg; padding:14px 22px 12px; }
#title h1{
font-family:'ZCOOL KuaiLe',cursive; font-weight:400; margin:0;
font-size:clamp(34px,5.2vw,56px); line-height:1; letter-spacing:.06em;
color:var(--red); text-shadow:2.5px 2.5px 0 var(--amber);
}
#title .row{ display:flex; align-items:center; gap:10px; margin-top:6px; }
#title .zh{ font-weight:700; font-size:clamp(12px,1.4vw,15px); letter-spacing:.24em; }
#title .en{ font-size:10px; letter-spacing:.32em; font-weight:700; opacity:.55; }
#title .fish{ width:44px; flex:none; }
#hud{ right:20px; bottom:20px; --rot:1.4deg; min-width:220px; }
#hud .speed{ display:flex; align-items:baseline; gap:6px; }
#hud .speed b{ font-family:'ZCOOL KuaiLe',cursive; font-weight:400; font-size:46px; line-height:1; color:var(--sea); }
#hud .speed span{ font-size:12px; font-weight:700; letter-spacing:.2em; opacity:.6; }
#hud .hint{ margin:8px 0 10px; font-size:12px; line-height:1.6; opacity:.85; }
#hud kbd{ font-family:inherit; border:1.5px solid var(--ink); border-bottom-width:3px; border-radius:6px; padding:0 6px; background:#fff; font-size:11px; }
.chips{ display:flex; gap:10px; }
.chip{
flex:1; font-family:'ZCOOL KuaiLe',cursive; font-size:17px; letter-spacing:.12em;
padding:9px 0; border-radius:12px; cursor:pointer;
border:3px solid var(--ink); background:#EAF4EF; color:var(--ink);
transition:transform .15s, background .2s, color .2s, box-shadow .15s;
}
.chip:hover{ transform:translateY(-3px); box-shadow:0 5px 0 rgba(30,58,71,.2); }
.chip[aria-pressed="true"]{ background:var(--amber); }
.chip b{ display:block; font-family:'Noto Sans SC'; font-weight:700; font-size:9px; letter-spacing:.3em; opacity:.6; }
#credit{ position:fixed; left:20px; bottom:16px; font-size:11px; letter-spacing:.18em;
color:#fff; opacity:.75; background:rgba(30,58,71,.28); padding:5px 12px;
border-radius:999px; }
#rider{ cursor:pointer; }
@media (max-width:640px){
#credit{ display:none; }
#hud{ left:20px; right:20px; bottom:14px; }
}
</style>
</head>
<body>
<svg id="scene" viewBox="0 0 1600 900" preserveAspectRatio="xMidYMid slice"
role="img" aria-label="一只白鹈鹕在海边公路上骑自行车的动画场景">
<defs>
<linearGradient id="gSky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#2E9CCB"/>
<stop offset=".58" stop-color="#7CD6E5"/>
<stop offset=".88" stop-color="#D8F3E2"/>
<stop offset="1" stop-color="#FFE9B3"/>
</linearGradient>
<linearGradient id="gSea" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#127387"/><stop offset="1" stop-color="#37AEB5"/>
</linearGradient>
<radialGradient id="gSun">
<stop offset="0" stop-color="#FFF3B0"/>
<stop offset=".6" stop-color="#FFD447"/>
<stop offset="1" stop-color="#FFD447" stop-opacity="0"/>
</radialGradient>
<radialGradient id="gGround">
<stop offset="0" stop-color="#0B1B26" stop-opacity="0"/>
<stop offset=".72" stop-color="#0B1B26" stop-opacity="0"/>
<stop offset="1" stop-color="#0B1B26" stop-opacity=".35"/>
</radialGradient>
<g id="cloud" fill="#FFF" opacity=".92">
<ellipse cx="0" cy="0" rx="58" ry="24"/>
<ellipse cx="48" cy="6" rx="44" ry="20"/>
<ellipse cx="-46" cy="8" rx="40" ry="18"/>
<ellipse cx="10" cy="-16" rx="38" ry="20"/>
</g>
<g id="palms">
<g stroke="#2F7D4F" stroke-width="7" stroke-linecap="round" fill="none">
<path d="M820 692 C826 664 818 636 830 610"/><path d="M846 692 C850 668 844 640 852 616"/>
</g>
<g stroke="#3FA05F" stroke-width="8" stroke-linecap="round" fill="none">
<path d="M830 610 C800 596 772 598 748 612"/><path d="M830 610 C812 584 792 574 766 572"/>
<path d="M830 610 C834 580 848 562 872 556"/><path d="M830 610 C856 588 884 584 908 592"/>
<path d="M830 610 C852 606 874 614 890 630"/>
<path d="M852 616 C830 606 810 608 792 618"/><path d="M852 616 C856 594 868 580 886 574"/>
<path d="M852 616 C872 600 894 598 914 606"/>
</g>
</g>
<g id="buoys" fill="#FF6B5E">
<circle cx="250" cy="646" r="5"/><circle cx="520" cy="646" r="5"/><circle cx="1180" cy="646" r="5"/>
</g>
<g id="waveSet" stroke="#CFF3EE" stroke-width="4" stroke-linecap="round" fill="none">
<path d="M80 604 q22 -10 44 0 q22 10 44 0"/><path d="M430 588 q22 -10 44 0 q22 10 44 0"/>
<path d="M880 622 q22 -10 44 0 q22 10 44 0"/><path d="M1240 596 q22 -10 44 0 q22 10 44 0"/>
<path d="M660 656 q22 -10 44 0 q22 10 44 0"/><path d="M1480 648 q22 -10 44 0"/>
</g>
<g id="bush">
<circle cx="0" cy="-20" r="24" fill="#2F7D4F"/><circle cx="24" cy="-26" r="20" fill="#3FA05F"/>
<circle cx="-22" cy="-24" r="18" fill="#3FA05F"/><circle cx="8" cy="-34" r="16" fill="#4CBA6E"/>
</g>
<g id="lamp">
<rect x="-5" y="-205" width="10" height="205" rx="5" fill="#2A3B47"/>
<rect x="-30" y="-228" width="60" height="24" rx="8" fill="#2A3B47"/>
<circle cx="0" cy="-216" r="8" fill="#FFE08A"/>
</g>
<g id="sign">
<rect x="-6" y="-150" width="12" height="150" rx="4" fill="#8B5A2B"/>
<rect x="-68" y="-206" width="136" height="58" rx="10" fill="#1F7A5C" stroke="#FFF" stroke-width="3"/>
<text x="0" y="-166" text-anchor="middle" font-family="ZCOOL KuaiLe, Noto Sans SC" font-size="27" fill="#FFF">海风道</text>
</g>
</defs>
<!-- 天空 / 太阳 -->
<rect x="0" y="0" width="1600" height="726" fill="url(#gSky)"/>
<g id="sun">
<circle cx="1315" cy="165" r="118" fill="url(#gSun)"/>
<circle cx="1315" cy="165" r="54" fill="#FFD447"/>
<circle cx="1315" cy="165" r="54" fill="none" stroke="#FFB020" stroke-width="4" opacity=".5"/>
</g>
<g id="clouds"></g>
<!-- 海 -->
<rect x="0" y="556" width="1600" height="142" fill="url(#gSea)"/>
<rect x="0" y="556" width="1600" height="3" fill="#FFF" opacity=".5"/>
<g id="waveFar"><use href="#waveSet"/><use href="#waveSet" x="1600"/></g>
<g id="waveNear"><use href="#waveSet"/><use href="#waveSet" x="1600"/></g>
<!-- 远景视差层 -->
<g id="far">
<use href="#buoys"/><use href="#buoys" x="1600"/>
<g>
<ellipse cx="300" cy="562" rx="82" ry="20" fill="#3F7D52"/>
<path d="M300 556 C294 522 297 492 300 470 L322 470 C325 492 328 522 322 556 Z" fill="#FDF6EA" stroke="#1E3A47" stroke-width="2.5"/>
<path d="M295 540 h33 M297 512 h31" stroke="#E8483F" stroke-width="9"/>
<rect x="291" y="448" width="40" height="24" rx="5" fill="#E8483F"/>
<circle cx="311" cy="460" r="6" fill="#FFE08A"/>
<path d="M291 448 l20 -12 20 12 Z" fill="#1E3A47"/>
</g>
<path d="M690 556 q45 -16 90 0 Z" fill="#3F7D52"/>
<path d="M1330 556 q55 -20 110 0 Z" fill="#3F7D52"/>
<g id="boat" stroke="#1E3A47" stroke-width="3">
<path d="M1400 596 q52 20 104 0 l-14 20 h-76 Z" fill="#E8483F"/>
<path d="M1452 594 V540" /><path d="M1452 542 l-42 46 h42 Z" fill="#FFF6E0"/>
<path d="M1452 542 l30 40 h-30 Z" fill="#FFE08A"/>
<path d="M1452 542 q12 4 20 0" fill="none" stroke="#FFB020" stroke-width="4"/>
</g>
</g>
<!-- 中景视差层 -->
<g id="mid"><use href="#palms"/><use href="#palms" x="1600"/></g>
<!-- 沙滩 / 路面 -->
<rect x="0" y="696" width="1600" height="34" fill="#F0DBA0"/>
<path d="M0 706 h1600" stroke="#D9BC77" stroke-width="2" stroke-dasharray="4 14"/>
<rect x="0" y="728" width="1600" height="172" fill="#454B57"/>
<path d="M0 730 h1600" stroke="#E6E9EF" stroke-width="4" opacity=".6"/>
<path id="lane" d="M0 806 H1600" stroke="#F4C64D" stroke-width="10" stroke-dasharray="64 82"/>
<!-- 近景视差层 -->
<g id="road">
<use href="#bush" x="180" y="732"/><use href="#lamp" x="430" y="732"/>
<use href="#sign" x="760" y="732"/><use href="#bush" x="1090" y="732"/>
<use href="#lamp" x="1330" y="732"/><use href="#bush" x="1520" y="732"/>
<use href="#bush" x="1780" y="732"/><use href="#lamp" x="2030" y="732"/>
<use href="#sign" x="2360" y="732"/><use href="#bush" x="2690" y="732"/>
<use href="#lamp" x="2930" y="732"/><use href="#bush" x="3120" y="732"/>
</g>
<ellipse cx="810" cy="812" rx="285" ry="16" fill="#10181F" opacity=".22"/>
<rect id="vignette" x="0" y="0" width="1600" height="900" fill="url(#gGround)" pointer-events="none"/>
<!-- 风速线 -->
<g id="winds" stroke="#EAF7F2" stroke-width="6" stroke-linecap="round" fill="none" opacity="0">
<path id="w1" d="M330 610 H530"/>
<path id="w2" d="M270 672 H450"/>
<path id="w3" d="M370 730 H600"/>
</g>
<!-- 车轮 -->
<g id="wheelF" stroke-linecap="round">
<circle cx="980" cy="740" r="70" fill="none" stroke="#1E2630" stroke-width="11"/>
<circle cx="980" cy="740" r="56" fill="none" stroke="#F3F6F9" stroke-width="5"/>
<g stroke="#B9C4CE" stroke-width="4">
<line x1="980" y1="684" x2="980" y2="796"/><line x1="924" y1="740" x2="1036" y2="740"/>
<line x1="940" y1="700" x2="1020" y2="780"/><line x1="1020" y1="700" x2="940" y2="780"/>
</g>
<circle cx="980" cy="740" r="8" fill="#1E2630"/>
</g>
<g id="wheelR" stroke-linecap="round">
<circle cx="640" cy="740" r="70" fill="none" stroke="#1E2630" stroke-width="11"/>
<circle cx="640" cy="740" r="56" fill="none" stroke="#F3F6F9" stroke-width="5"/>
<g stroke="#B9C4CE" stroke-width="4">
<line x1="640" y1="684" x2="640" y2="796"/><line x1="584" y1="740" x2="696" y2="740"/>
<line x1="600" y1="700" x2="680" y2="780"/><line x1="680" y1="700" x2="600" y2="780"/>
</g>
<circle cx="640" cy="740" r="8" fill="#1E2630"/>
</g>
<!-- 车架 -->
<g stroke-linecap="round">
<path d="M640 740 L760 745" stroke="#1E2630" stroke-width="8"/>
<g id="chainA" stroke="#2A323C" stroke-width="7" stroke-dasharray="7 7" fill="none">
<path d="M760 727 L640 728"/><path d="M760 763 L640 752"/>
</g>
<circle cx="640" cy="740" r="12" fill="#1E2630"/><circle cx="760" cy="745" r="17" fill="#1E2630"/>
<circle cx="760" cy="745" r="7" fill="#FFB020"/>
<path d="M640 740 L718 560" stroke="#1E8F9C" stroke-width="11"/>
<path d="M760 745 L722 562" stroke="#1E8F9C" stroke-width="13"/>
<path d="M722 562 L952 588" stroke="#1E8F9C" stroke-width="12"/>
<path d="M760 745 L952 588" stroke="#1E8F9C" stroke-width="14"/>
<path d="M952 588 L980 740" stroke="#1E8F9C" stroke-width="10"/>
<path d="M952 588 L938 528 Q934 506 952 502 L966 500" fill="none" stroke="#1E2630" stroke-width="9"/>
<path d="M966 500 L978 498" stroke="#E8483F" stroke-width="12"/>
</g>
<g id="bottle" transform="rotate(15 820 712)">
<rect x="810" y="696" width="20" height="34" rx="7" fill="#FF8B3D"/>
<rect x="813" y="690" width="14" height="8" rx="3" fill="#1E2630"/>
</g>
<!-- 旗杆 -->
<path d="M640 606 L614 512" stroke="#1E2630" stroke-width="6" stroke-linecap="round"/>
<path id="flag" d="M614 512 l-44 11 44 11 Z" fill="#E8483F"/>
<!-- 车座 + 腿 -->
<path id="saddle" d="M688 546 q32 -12 60 -2 q-6 12 -34 12 q-26 0 -26 -10 Z" fill="#E8483F" stroke="#1E2630" stroke-width="3"/>
<path id="legB" d="M735 545 L770 700 L760 745" fill="none" stroke="#EF8E1C" stroke-width="18" stroke-linecap="round" stroke-linejoin="round"/>
<path id="legF" d="M735 545 L770 650 L760 745" fill="none" stroke="#FF9E2C" stroke-width="21" stroke-linecap="round" stroke-linejoin="round"/>
<!-- 车篮与鱼 -->
<g>
<rect id="fishBody" x="986" y="524" width="34" height="17" rx="8.5" fill="#4C6E93"/>
<path id="tail" d="M1018 532 l17 -12 v24 Z" fill="#4C6E93"/>
<g fill="none" stroke="#2A3A52" stroke-width="2.5">
<path d="M988 553 V584"/><path d="M1002 553 V584"/><path d="M1016 553 V584"/><path d="M1030 553 V584"/>
<path d="M984 562 H1032"/><path d="M984 574 H1032"/>
</g>
<path d="M982 552 h52 v32 h-52 Z" fill="none" stroke="#8B5A2B" stroke-width="5"/>
</g>
<!-- 踏板(含脚蹼,脚本定位) -->
<g id="pedalB">
<rect x="-23" y="-5" width="46" height="10" rx="4" fill="#1E2630"/>
<path d="M-20 -5 Q-22 -16 -11 -18 L17 -18 Q27 -16 20 -5 Z" fill="#EF8E1C"/>
<path d="M-6 -18 l3 -8 M6 -18 l3 -8" stroke="#EF8E1C" stroke-width="4" stroke-linecap="round"/>
</g>
<g id="pedalF">
<rect x="-23" y="-5" width="46" height="10" rx="4" fill="#1E2630"/>
<path d="M-20 -5 Q-22 -16 -11 -18 L17 -18 Q27 -16 20 -5 Z" fill="#FF9E2C"/>
<path d="M-6 -18 l3 -8 M6 -18 l3 -8" stroke="#FF9E2C" stroke-width="4" stroke-linecap="round"/>
</g>
<!-- 鹈鹕本体 -->
<g id="pelican">
<path id="tail" d="M668 440 Q624 452 600 484 L644 490 L616 514 L662 514 Z" fill="#F4EDDD" stroke="#D9CFB8" stroke-width="2"/>
<path d="M672 444 C664 392 716 358 780 358 C848 358 888 400 882 452 C876 506 824 540 762 538 C706 536 680 492 672 444 Z"
fill="#FDFBF4" stroke="#D9CFB8" stroke-width="3"/>
<path d="M690 516 C730 538 820 538 858 512 C846 530 806 542 762 540 C724 538 700 528 690 516 Z" fill="#E9E0CB"/>
<path d="M824 420 C846 356 902 330 936 336 C954 340 962 356 958 372 C954 388 938 396 920 394 C884 390 856 420 850 462"
fill="none" stroke="#FDFBF4" stroke-width="34" stroke-linecap="round"/>
<path id="crest" d="M906 300 q-24 -18 -44 -10 q22 -4 34 6 q-18 -14 -38 -12 q18 -2 30 6 Z" fill="#FDFBF4" stroke="#D9CFB8" stroke-width="2"/>
<path id="head" d="M902 340 q0 -32 34 -32 q32 0 32 30 q0 30 -32 30 q-34 0 -34 -28 Z"
fill="#FDFBF4" stroke="#D9CFB8" stroke-width="3"/>
<path id="pouch" d="M956 354 C978 402 1034 420 1086 384 L1088 366 C1044 398 986 388 956 354 Z"
fill="#F08A24" stroke="#C96A15" stroke-width="3"/>
<path d="M952 348 C952 330 984 318 1024 322 C1058 326 1084 338 1094 350 C1102 358 1098 364 1088 366 C1046 352 992 344 952 348 Z"
fill="#FF9E2C" stroke="#C96A15" stroke-width="3"/>
<path d="M1088 366 q12 6 2 14" fill="none" stroke="#C96A15" stroke-width="4" stroke-linecap="round"/>
<path d="M1046 354 q-2 12 -12 22" fill="none" stroke="#C96A15" stroke-width="3" stroke-linecap="round" opacity=".8"/>
<circle cx="944" cy="320" r="10" fill="#FFF"/>
<circle id="pupil" cx="948" cy="321" r="4.6" fill="#1E3A47"/>
<path d="M932 311 l15 -7" stroke="#D9CFB8" stroke-width="3" stroke-linecap="round"/>
<g id="wing"><path d="M764 402 C714 400 672 432 662 480 C656 510 668 542 694 550 C708 554 716 542 712 528 C734 536 752 522 756 500 C762 468 766 434 764 402 Z"
fill="#F1E8D4" stroke="#D9CFB8" stroke-width="3"/>
<path d="M690 470 q14 24 8 46 M714 452 q16 26 10 52 M738 436 q14 28 8 54"
fill="none" stroke="#D9CFB8" stroke-width="2.5" stroke-linecap="round"/></g>
</g>
<g id="wingspan" pointer-events="all"><rect x="590" y="270" width="540" height="555" fill="transparent"/></g>
<!-- 鸣叫效果 -->
<g id="honkfx" opacity="0">
<g id="honkArcs" stroke="#FFD447" stroke-width="6" stroke-linecap="round" fill="none">
<path d="M1116 330 a26 26 0 0 1 0 -40"/><path d="M1134 342 a46 46 0 0 1 0 -64"/>
<path d="M1152 354 a66 66 0 0 1 0 -88"/>
</g>
<text id="honkTxt" x="1128" y="292" font-family="ZCOOL KuaiLe, Noto Sans SC" font-size="56"
fill="#FFF6E0" stroke="#1E3A47" stroke-width="6" paint-order="stroke"
transform="rotate(-8 1128 292)">噗——!</text>
</g>
</svg>
<header class="plaque" id="title" style="--rot:-2deg">
<div class="row">
<svg class="fish" viewBox="0 0 44 24" aria-hidden="true">
<path d="M4 12 q8 -10 22 -10 q12 0 14 10 q-2 10 -14 10 q-14 0 -22 -10 Z" fill="#1E8F9C"/>
<path d="M2 12 l-2 -8 8 8 -8 8 Z" fill="#E8483F"/>
<circle cx="30" cy="9" r="2.2" fill="#FFF"/>
</svg>
<div>
<h1>鹈鹕骑行</h1>
<div class="row"><span class="zh">海风道 · 单车特快</span><span class="en">PELICAN CYCLING CO.</span></div>
</div>
</div>
</header>
<div class="plaque" id="hud" style="--rot:1.4deg">
<div class="speed"><b id="kmh">14</b><span>KM/H</span></div>
<p class="hint">点一下鹈鹕,听它鸣一声 <kbd>空格</kbd><br>悬停时它会朝你挥手</p>
<div class="chips">
<button class="chip" id="btnSlow" aria-pressed="true">慢悠悠<b>EASY</b></button>
<button class="chip" id="btnFast" aria-pressed="false">冲刺<b>SPRINT</b></button>
</div>
</div>
<div id="credit">纯 SVG + 原生 JS 动画 · 无依赖</div>
<script>
(function(){
"use strict";
var $ = function(id){ return document.getElementById(id); };
var wheelR=$('wheelR'), wheelF=$('wheelF'), legF=$('legF'), legB=$('legB'),
pedalF=$('pedalF'), pedalB=$('pedalB'), pelican=$('pelican'), wing=$('wing'),
pouch=$('pouch'), flag=$('flag'), tail=$('tail'), honkfx=$('honkfx'),
honkArcs=$('honkArcs'), honkTxt=$('honkTxt'), winds=$('winds'),
boat=$('boat'), lane=$('lane'), chainA=$('chainA'),
far=$('far'), mid=$('mid'), road=$('road'),
waveFar=$('waveFar'), waveNear=$('waveNear'), clouds=$('clouds'), sun=$('sun');
var w1=$('w1'), w2=$('w2'), w3=$('w3');
var BB={x:760,y:745}, CR=34, HIP={x:735,y:545}, L1=118, L2=118;
var D2R=Math.PI/180;
var S=1, targetS=1, dist=0, th=0, tt=0, honkT=0, wingPhase=0, wavePh=0;
var hover=false, lastK=14, lastF=-1, lastW=-1, lastC=-1, lastShade=0;
var cloudData=[
{x:180,y:110,s:1.1,v:14},{x:520,y:60,s:0.8,v:10},{x:900,y:150,s:1.3,v:17},
{x:1250,y:80,s:0.9,v:12},{x:1520,y:190,s:0.7,v:9}
];
while(clouds.firstChild) clouds.removeChild(clouds.firstChild);
cloudData.forEach(function(c){
var u=document.createElementNS('http://www.w3.org/2000/svg','use');
u.setAttribute('href','#cloud'); c.el=u; clouds.appendChild(u);
});
function ik(hx,hy,ax,ay){
var dx=ax-hx, dy=ay-hy, d=Math.hypot(dx,dy);
var mx=ax, my=ay;
if(d>L1+L2-1){ var m=(L1+L2-1)/d; mx=hx+dx*m; my=hy+dy*m; dx=mx-hx; dy=my-hy; d=Math.hypot(dx,dy); }
if(d<1){ dx=1; dy=0; d=1; }
var a=(L1*L1-L2*L2+d*d)/(2*d), h=Math.sqrt(Math.max(0,L1*L1-a*a));
var mx2=hx+dx*a/d, my2=hy+dy*a/d;
var px=-dy/d, py=dx/d;
if(px<0){ px=-px; py=-py; }
return {x:mx2+px*h, y:my2+py*h};
}
var AC=null;
function honkSound(){
try{
AC = AC || new (window.AudioContext||window.webkitAudioContext)();
if(AC.state==='suspended') AC.resume();
var t0=AC.currentTime;
var o=AC.createOscillator(), g=AC.createGain();
var o2=AC.createOscillator(), g2=AC.createGain();
o.type='sawtooth'; o2.type='square';
o.frequency.setValueAtTime(330,t0); o.frequency.exponentialRampToValueAtTime(130,t0+0.3);
o2.frequency.setValueAtTime(336,t0); o2.frequency.exponentialRampToValueAtTime(133,t0+0.3);
g.gain.setValueAtTime(0.0001,t0);
g.gain.exponentialRampToValueAtTime(0.2,t0+0.03);
g.gain.exponentialRampToValueAtTime(0.0001,t0+0.34);
g2.gain.value=0.3;
o.connect(g); o2.connect(g2); g2.connect(g); g.connect(AC.destination);
o.start(t0); o2.start(t0); o.stop(t0+0.36); o2.stop(t0+0.36);
}catch(e){}
}
function honk(){ honkT=1; honkSound(); }
var btnSlow=$('btnSlow'), btnFast=$('btnFast');
function setMode(s){
targetS = s;
btnSlow.setAttribute('aria-pressed', s===1 ? 'true':'false');
btnFast.setAttribute('aria-pressed', s===3 ? 'true':'false');
}
btnSlow.addEventListener('click',function(){ setMode(1); });
btnFast.addEventListener('click',function(){ setMode(3); });
window.addEventListener('keydown',function(e){
if(e.code==='Space'){ e.preventDefault(); honk(); }
if(e.key==='1') setMode(1);
if(e.key==='2') setMode(3);
});
var rider=$('wingspan');
rider.addEventListener('pointerdown',function(e){ e.preventDefault(); honk(); });
var scene=$('scene');
scene.addEventListener('pointerenter',function(){ hover=true; });
scene.addEventListener('pointerleave',function(){ hover=false; });
if(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches){
S=0.5; targetS=0.5; btnSlow.setAttribute('aria-pressed','false');
}
var last=performance.now();
function frame(now){
var dt=Math.min(0.05,(now-last)/1000); last=now;
S += (targetS-S)*Math.min(1,dt*2.2);
tt += dt*(0.5+0.5*Math.min(S,2));
var ground = (90+200*S)*dt;
dist += ground;
var cad = (70+150*S)*dt;
th = (th + cad)%360;
if(honkT>0) honkT=Math.max(0,honkT-dt*2.4);
var bob = Math.sin(th*D2R*2)*3.2;
if(honkT>0) bob -= Math.sin(honkT*22)*7*honkT;
far.setAttribute('transform','translate('+(-(dist*0.18)%1600).toFixed(1)+' 0)');
mid.setAttribute('transform','translate('+(-(dist*0.5)%1600).toFixed(1)+' 0)');
road.setAttribute('transform','translate('+(-(dist*1.3)%1600).toFixed(1)+' 0)');
lane.setAttribute('stroke-dashoffset',(-dist*1.3).toFixed(1));
wavePh -= ground*0.22;
waveFar.setAttribute('transform','translate('+(-wavePh%250).toFixed(1)+' 0)');
waveNear.setAttribute('transform','translate('+(-wavePh*1.5%250).toFixed(1)+' 0)');
cloudData.forEach(function(c){
c.x += (c.v+28*S)*dt;
if(c.x>1750) c.x=-320;
c.el.setAttribute('transform','translate('+c.x.toFixed(1)+' '+c.y.toFixed(1)+') scale('+c.s+')');
});
var sr=(now*0.004)%360;
if(Math.round(sr)!==lastShade){ lastShade=Math.round(sr); sun.setAttribute('transform','rotate('+sr.toFixed(1)+' 1315 165)'); }
var thd=th.toFixed(1);
wheelR.setAttribute('transform','rotate('+thd+' 640 740)');
wheelF.setAttribute('transform','rotate('+thd+' 980 740)');
chainA.setAttribute('stroke-dashoffset',(-th*0.55).toFixed(1));
var hipY=HIP.y+bob;
pelican.setAttribute('transform','translate(0 '+bob.toFixed(2)+') rotate('+(bob*0.4).toFixed(2)+' 760 540)');
var legT=[0,180];
for(var i=0;i<2;i++){
var a=(th+legT[i])*D2R;
var ax=BB.x+CR*Math.cos(a), ay=BB.y+CR*Math.sin(a);
var k=ik(HIP.x,hipY,ax,ay);
var p=(i===0?legF:legB);
p.setAttribute('d','M'+HIP.x+' '+hipY.toFixed(1)+' L'+k.x.toFixed(1)+' '+k.y.toFixed(1)+' L'+ax.toFixed(1)+' '+ay.toFixed(1));
var pd=(i===0?pedalF:pedalB);
pd.setAttribute('transform','translate('+ax.toFixed(1)+' '+ay.toFixed(1)+') rotate('+((th+legT[i]+90)%360).toFixed(1)+')');
}
wingPhase += dt*(hover?14:4.2);
var wa = (hover? -26+Math.sin(wingPhase)*30 : -16+Math.sin(wingPhase)*14);
wing.setAttribute('transform','rotate('+wa.toFixed(1)+' 752 408)');
if(honkT>0){
var px=1022, py=384, sc=1+0.55*honkT;
pouch.setAttribute('transform','translate('+px+' '+py+') scale('+sc.toFixed(3)+' '+(1+0.45*honkT).toFixed(3)+') translate('+(-px)+' '+(-py)+')');
}else pouch.removeAttribute('transform');
honkfx.setAttribute('opacity',honkT>0?honkT.toFixed(2):0);
if(honkT>0){
var gs=1+(1-honkT)*1.3;
honkArcs.setAttribute('transform','translate(1120 326) scale('+gs.toFixed(2)+') translate(-1120 -326)');
honkTxt.setAttribute('transform','rotate(-8 1128 292) translate(0 '+((1-honkT)*-24).toFixed(1)+')');
}
tail.setAttribute('transform','rotate('+(Math.sin(tt*3)*4).toFixed(1)+' 668 448)');
flag.setAttribute('transform','rotate('+(Math.sin(tt*5+1)*12).toFixed(1)+' 614 512)');
boat.setAttribute('transform','translate(0 '+(Math.sin(tt*0.9)*6).toFixed(1)+') rotate('+(Math.sin(tt*0.7)*3).toFixed(1)+' 1452 596)');
var w = (S>1.7)? Math.min(1,(S-1.7)/1.3) : 0;
if(Math.round(w*100)!==lastW){
lastW=Math.round(w*100);
winds.setAttribute('opacity',(w*0.85).toFixed(2));
w1.setAttribute('stroke-dasharray','70 90'); w1.setAttribute('stroke-dashoffset',(-dist*3%160).toFixed(0));
w2.setAttribute('stroke-dasharray','50 110'); w2.setAttribute('stroke-dashoffset',(-dist*3.6%160).toFixed(0));
w3.setAttribute('stroke-dasharray','90 70'); w3.setAttribute('stroke-dashoffset',(-dist*2.8%160).toFixed(0));
}
var km=Math.round(8+6*S*S);
if(km!==lastK){ lastK=km; $('kmh').textContent=km; }
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
})();
</script>
</body>
</html>