基于Canvas和Web Audio API的交互式烟花动画网页游戏
一个基于 Canvas 和 Web Audio API 的交互式烟花动画网页
目录
一、整体架构
┌─────────────────────────────────────────────────────────────┐ │ HTML 结构 │ ├─────────────────────────────────────────────────────────────┤ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │ │ SVG 图标 │ │ Canvas容器 │ │ 控制面板/菜单 │ │ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ ├─────────────────────────────────────────────────────────────┤ │ CSS 样式 │ ├─────────────────────────────────────────────────────────────┤ │ JavaScript 逻辑 │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │ │ │ FireworkSound│ │ Store │ │ Shell/Star/Spark │ │ │ │ 音效模块 │ │ 状态管理 │ │ 粒子系统 │ │ │ └──────────────┘ └──────────────┘ └──────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ 二、HTML 结构部分
1. 头部元信息
<metacharset="UTF-8"><title>2026新年快乐!万事如意!</title><metaname="viewport"content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">说明:
- 设置字符编码为 UTF-8
- 页面标题显示新年祝福
viewport-fit=cover适配 iPhone X 系列刘海屏- 禁用用户缩放,防止误操作
2. iOS PWA 支持
<metaname="apple-mobile-web-app-capable"content="yes"><metaname="apple-mobile-web-app-status-bar-style"content="black-translucent"><metaname="apple-mobile-web-app-title"content="烟花盛宴"><linkrel="apple-touch-icon"href="..."><linkrel="manifest"href="data:application/json;base64,...">说明:
- 支持添加到 iOS 主屏幕
- 半透明状态栏适配刘海屏
- 内联 PWA manifest(Base64 编码)
3. SVG 图标定义
<svgxmlns="http://www.w3.org/2000/svg"><symbolid="icon-play"viewBox="0 0 24 24"><pathd="M8 5v14l11-7z"/></symbol><symbolid="icon-pause"viewBox="0 0 24 24"><pathd="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></symbol><symbolid="icon-close"viewBox="0 0 24 24">...</symbol><symbolid="icon-settings"viewBox="0 0 24 24">...</symbol><symbolid="icon-shutter-fast"viewBox="0 0 24 24">...</symbol><symbolid="icon-shutter-slow"viewBox="0 0 24 24">...</symbol></svg>说明:
- 使用 SVG symbol 定义可复用的图标
- 通过
<use href="#icon-xxx">引用 - 优点:矢量图标、可缩放、体积小
4. Canvas 画布容器
<divid="canvas-container"><canvasid="trails-canvas"></canvas><!-- 拖尾轨迹层 --><canvasid="main-canvas"></canvas><!-- 主画面层 --></div>双层 Canvas 设计原理:
| 层级 | 作用 | 渲染方式 |
|---|---|---|
trails-canvas | 绘制烟花拖尾效果 | 渐变透明覆盖实现残影 |
main-canvas | 绘制当前帧的粒子和UI元素 | 每帧清空重绘 |
5. 控制面板
<divid="controls"><divid="pause-btn"class="btn"><svgfill="white"width="24"height="24"><usehref="#icon-pause"></use></svg></div><divid="shutter-btn"class="btn"><svgfill="white"width="24"height="24"><usehref="#icon-shutter-slow"></use></svg></div><divid="settings-btn"class="btn"><svgfill="white"width="24"height="24"><usehref="#icon-settings"></use></svg></div></div>按钮功能:
- 暂停/播放:控制动画运行
- 快门模式:切换长曝光效果
- 设置:打开设置菜单
6. 设置菜单
<divid="menu"><divid="menu__header">设置</div><form><divclass="form-option form-option--select"><label>烟花类型</label><selectid="shell-type"></select></div><divclass="form-option form-option--select"><label>烟花大小</label><selectid="shell-size"></select></div><divclass="form-option form-option--checkbox"><label><inputid="auto-launch"type="checkbox"/><span>自动发射</span></label></div><divclass="form-option form-option--checkbox"><label><inputid="finale-mode"type="checkbox"/><span>终场模式</span></label></div><divclass="form-option form-option--checkbox"><label><inputid="hide-controls"type="checkbox"/><span>隐藏控制</span></label></div><divclass="form-option form-option--checkbox"><label><inputid="sound-enabled"type="checkbox"checked/><span>声音效果</span></label></div><divclass="form-option form-option--select"><label>声音类型</label><selectid="sound-type"></select></div></form></div>7. 首次交互提示(iOS 适配)
<divid="audio-prompt"><divid="audio-prompt__icon">🎆</div><divid="audio-prompt__text">点击屏幕开始烟花盛宴</div><divid="audio-prompt__hint">Tap to start fireworks</div></div>说明:
- iOS Safari 要求用户交互后才能播放音频
- 此提示引导用户点击以解锁音频
三、CSS 样式部分
1. 全局样式与触摸优化
*{position: relative;box-sizing: border-box;-webkit-tap-highlight-color: transparent;/* 移除点击高亮 */-webkit-touch-callout: none;/* 禁用长按菜单 */-webkit-user-select: none;/* 禁止选择文字 */user-select: none;touch-action: manipulation;/* 禁用双击缩放 */}html{background-color: #000;}body{overflow: hidden;color:rgba(255, 255, 255, 0.5);font-family:"Russo One", arial, sans-serif;overscroll-behavior: none;/* 禁用橡皮筋效果 */padding:env(safe-area-inset-top)env(safe-area-inset-right)env(safe-area-inset-bottom)env(safe-area-inset-left);/* 安全区域 */}2. 混合模式
#canvas-container canvas{position: absolute;mix-blend-mode: lighten;/* 变亮混合模式 */}说明:
lighten混合模式使亮色粒子在黑色背景上更加突出- 多个亮色叠加会产生更亮的效果
3. 动画过渡
.hide{opacity: 0;visibility: hidden;transition: opacity 0.3s, visibility 0.3s;}说明:
- 使用 opacity + visibility 组合实现平滑的显示/隐藏动画
- visibility 延迟隐藏,避免点击穿透
4. 音频提示动画
#audio-prompt__icon{font-size: 80px;animation: pulse 1.5s ease-in-out infinite;}@keyframes pulse{0%, 100%{transform:scale(1);opacity: 1;}50%{transform:scale(1.1);opacity: 0.7;}}四、JavaScript 核心模块
1. FireworkSound 音效模块
const FireworkSound ={ctx:null,// AudioContext 实例enabled:true,// 是否启用声音soundType:'realistic',// 当前声音类型isUnlocked:false,// iOS 音频解锁状态init(){// 初始化 Web Audio APIthis.ctx =new(window.AudioContext || window.webkitAudioContext)();},unlock(){// iOS 音频解锁:播放静音缓冲区if(this.isUnlocked)returntrue;if(this.ctx.state ==='suspended'){this.ctx.resume();}const buffer =this.ctx.createBuffer(1,1,22050);const source =this.ctx.createBufferSource(); source.buffer = buffer; source.connect(this.ctx.destination); source.start(0);this.isUnlocked =true;},playLaunch(){/* 发射音效 */},playBurst(size){/* 爆炸音效 */}};Web Audio API 核心概念:
| 组件 | 作用 | 示例用途 |
|---|---|---|
AudioContext | 音频上下文,管理所有音频节点 | new AudioContext() |
OscillatorNode | 振荡器,生成周期性波形 | 正弦波、方波、锯齿波 |
GainNode | 增益节点,控制音量 | 音量包络、淡入淡出 |
BiquadFilter | 双二阶滤波器 | 低通、高通、带通滤波 |
BufferSource | 缓冲源,播放采样数据 | 白噪声、爆炸声 |
真实爆炸声实现原理:
playRealisticBurst(size =1){// 1. 生成白噪声缓冲区const duration =0.6+ size *0.2;const bufferSize =this.ctx.sampleRate * duration;const buffer =this.ctx.createBuffer(1, bufferSize,this.ctx.sampleRate);const data = buffer.getChannelData(0);for(let i =0; i < bufferSize; i++){const t = i / bufferSize;const noise = Math.random()*2-1;// 随机噪声 [-1, 1]const envelope = Math.pow(1- t,1.5);// 指数衰减包络 data[i]= noise * envelope;}// 2. 创建低通滤波器模拟爆炸闷响const filter =this.ctx.createBiquadFilter(); filter.type ='lowpass'; filter.frequency.setValueAtTime(3000* size,this.ctx.currentTime); filter.frequency.exponentialRampToValueAtTime(200,this.ctx.currentTime + duration *0.7);// 3. 音量包络:快速起音,缓慢衰减const gain =this.ctx.createGain(); gain.gain.setValueAtTime(0,this.ctx.currentTime); gain.gain.linearRampToValueAtTime(volume,this.ctx.currentTime +0.02);// 起音 20ms gain.gain.exponentialRampToValueAtTime(0.01,this.ctx.currentTime + duration);// 衰减// 4. 连接节点并播放 noise.connect(filter); filter.connect(gain); gain.connect(this.ctx.destination); noise.start(this.ctx.currentTime);}声音类型对照表:
| 类型 | 中文名 | 特点 |
|---|---|---|
| realistic | 真实 | 白噪声 + 低频冲击 + 噼啪声 |
| cinematic | 电影感 | 深沉隆隆声 + 闪烁火花 |
| classic | 经典烟花 | 清脆爆破声 |
| retro | 复古8位 | 方波游戏机风格 |
| soft | 柔和轻柔 | 正弦波轻柔音效 |
| synth | 电子合成 | 多振荡器合成器 |
| drum | 鼓点贝斯 | 底鼓 + 军鼓 + 踩镲 |
| whistle | 哨子爆破 | 口哨式发射 + 爆破 |
2. Store 状态管理
const store ={_listeners:newSet(),// 订阅者集合state:{paused:false,longExposure:false,menuOpen:false,config:{shell:'Random',size:'3',autoLaunch:true,finale:false,hideControls:false,soundEnabled:true,soundType:'realistic'}},setState(nextState){this.state = Object.assign({},this.state, nextState);this._dispatch();// 通知所有订阅者this.persist();// 持久化到 localStorage},subscribe(listener){this._listeners.add(listener);return()=>this._listeners.delete(listener);},load(){// 从 localStorage 加载配置},persist(){// 保存配置到 localStorage}};发布-订阅模式流程:
用户操作 → updateConfig() → store.setState() → _dispatch() → renderApp() ↓ 更新 DOM 3. Shell 烟花类
classShell{constructor(options){this.size = options.size;// 爆炸半径this.starCount = options.starCount;// 星星数量this.starLife = options.starLife;// 星星寿命(毫秒)this.color = options.color;// 颜色this.glitter = options.glitter;// 闪光类型this.pistil = options.pistil;// 是否有花心this.ring = options.ring;// 是否环形}launch(position, launchHeight){// 1. 计算发射位置和目标高度const launchX = position *(width - hpad *2)+ hpad;const launchY = height;const burstY = minHeight -(launchHeight *(minHeight - vpad));const launchDistance = launchY - burstY;// 2. 计算发射速度(抛物线运动近似)const launchVelocity = Math.pow(launchDistance *0.04,0.64);// 3. 创建彗星(上升的火球)const comet = Star.add( launchX, launchY,this.color ||COLOR.White, Math.PI,// 向上发射 launchVelocity, launchVelocity *400// 寿命); comet.heavy =true;// 减少空气阻力 comet.sparkFreq =16;// 火花频率// 4. 设置死亡回调为爆炸 comet.onDeath=()=>this.burst(comet.x, comet.y);// 5. 播放发射音效 FireworkSound.playLaunch();}burst(x, y){// 1. 计算爆炸速度const speed =this.size /96;// 2. 创建粒子弧(圆形分布)createParticleArc(0,PI_2,this.starCount,1,(angle)=>{const star = Star.add( x, y,this.color, angle, Math.pow(Math.random(),0.45)* speed,// 非线性速度分布this.starLife + Math.random()*this.starLife *this.starLifeVariation ); star.onDeath = onDeath;// 设置死亡效果});// 3. 播放爆炸音效const soundSize = Math.min(2,this.size /200); FireworkSound.playBurst(soundSize);}}烟花类型对照表:
| 类型 | 中文名 | 特点 | 视觉效果 |
|---|---|---|---|
| Crysanthemum | 菊花 | 经典球形爆炸,密集粒子 | 大型圆形散开 |
| Palm | 棕榈 | 垂直拖尾,像棕榈树 | 向下垂落 |
| Ring | 圆环 | 环形爆炸 | 空心圆环 |
| Crossette | 十字 | 爆炸后分裂成四个小星星 | 二次爆炸 |
| Floral | 花簇 | 小型二次爆炸 | 多层绽放 |
| Crackle | 噼啪 | 金色火花,噼啪声 | 金色闪烁 |
| Willow | 垂柳 | 长时间下坠的火花 | 长尾下垂 |
| Falling Leaves | 落叶 | 飘落的金色粒子 | 缓慢飘落 |
| Horse Tail | 马尾 | 水平拖尾 | 横向展开 |
4. Star 星星粒子
const Star ={drawWidth:3,airDrag:0.98,airDragHeavy:0.992,active:createParticleCollection(),// 按颜色分组的活跃粒子_pool:[],// 对象池,复用粒子对象add(x, y, color, angle, speed, life, speedOffX, speedOffY){// 从池中获取或创建新对象const instance =this._pool.pop()||{}; instance.x = x; instance.y = y; instance.prevX = x;// 上一帧位置(用于绘制轨迹) instance.prevY = y; instance.color = color; instance.speedX = Math.sin(angle)* speed +(speedOffX ||0); instance.speedY = Math.cos(angle)* speed +(speedOffY ||0); instance.life = life; instance.sparkFreq =0;// 火花频率this.active[color].push(instance);return instance;},returnInstance(instance){ instance.onDeath && instance.onDeath(instance); instance.onDeath =null;this._pool.push(instance);// 回收到池中}};对象池模式优势:
- 避免频繁创建/销毁对象
- 减少垃圾回收(GC)压力
- 提高大量粒子时的性能
5. Spark 火花粒子
const Spark ={drawWidth:0.75,airDrag:0.9,active:createParticleCollection(),_pool:[],add(x, y, color, angle, speed, life){const instance =this._pool.pop()||{}; instance.x = x; instance.y = y; instance.prevX = x; instance.prevY = y; instance.color = color; instance.speedX = Math.sin(angle)* speed; instance.speedY = Math.cos(angle)* speed; instance.life = life;this.active[color].push(instance);return instance;}};Star 与 Spark 的区别:
| 特性 | Star | Spark |
|---|---|---|
| 大小 | 3px | 0.75px |
| 空气阻力 | 0.98 | 0.9 |
| 用途 | 主要烟花粒子 | 闪光效果 |
| 寿命 | 较长 | 较短 |
6. 渲染循环
functionupdate(frameTime, lag){if(!canInteract())return;const timeStep = frameTime * simSpeed;const speed = simSpeed * lag;// 1. 更新全局状态(自动发射等)updateGlobals(timeStep, lag);// 2. 计算物理参数const starDrag =1-(1- Star.airDrag)* speed;// 空气阻力系数const sparkDrag =1-(1- Spark.airDrag)* speed;const gAcc = timeStep /1000*GRAVITY;// 重力加速度// 3. 更新所有粒子COLOR_CODES_W_INVIS.forEach(color=>{// 更新星星 Star.active[color].forEach((star, i, stars)=>{ star.life -= timeStep;if(star.life <=0){ stars.splice(i,1); Star.returnInstance(star);}else{// 保存上一帧位置 star.prevX = star.x; star.prevY = star.y;// 物理更新 star.x += star.speedX * speed; star.y += star.speedY * speed; star.speedX *= starDrag; star.speedY *= starDrag; star.speedY += gAcc;// 重力// 旋转效果if(star.spinRadius){ star.spinAngle += star.spinSpeed * speed; star.x += Math.sin(star.spinAngle)* star.spinRadius * speed; star.y += Math.cos(star.spinAngle)* star.spinRadius * speed;}// 火花效果if(star.sparkFreq){ star.sparkTimer -= timeStep;while(star.sparkTimer <0){ star.sparkTimer += star.sparkFreq; Spark.add(star.x, star.y, star.sparkColor,...);}}}});// 更新火花 Spark.active[color].forEach((spark, i, sparks)=>{// 类似星星的物理更新...});});// 4. 渲染render(speed);}7. 渲染函数
functionrender(speed){const{ dpr, width, height }= mainStage;const trailsCtx = trailsStage.ctx;const mainCtx = mainStage.ctx;// 1. 更新天空颜色colorSky(speed);// 2. 设置变换 trailsCtx.scale(dpr, dpr); mainCtx.scale(dpr, dpr);// 3. 绘制拖尾效果(渐变黑色覆盖) trailsCtx.globalCompositeOperation ='source-over'; trailsCtx.fillStyle =`rgba(0, 0, 0, ${longExposure ?0.0025:0.1* speed})`; trailsCtx.fillRect(0,0, width, height);// 4. 设置混合模式 trailsCtx.globalCompositeOperation ='lighten';// 5. 清空主画布 mainCtx.clearRect(0,0, width, height);// 6. 绘制爆炸闪光while(BurstFlash.active.length){const bf = BurstFlash.active.pop();const gradient = trailsCtx.createRadialGradient(bf.x, bf.y,0, bf.x, bf.y, bf.radius); gradient.addColorStop(0.05,'white'); gradient.addColorStop(0.25,'rgba(255, 160, 20, 0.2)'); gradient.addColorStop(1,'rgba(255, 160, 20, 0)'); trailsCtx.fillStyle = gradient; trailsCtx.fillRect(bf.x - bf.radius, bf.y - bf.radius, bf.radius *2, bf.radius *2);}// 7. 绘制星星轨迹 trailsCtx.lineWidth = Star.drawWidth; trailsCtx.lineCap ='round';COLOR_CODES.forEach(color=>{const stars = Star.active[color]; trailsCtx.strokeStyle = color; trailsCtx.beginPath(); stars.forEach(star=>{ trailsCtx.moveTo(star.x, star.y); trailsCtx.lineTo(star.prevX, star.prevY);}); trailsCtx.stroke();});// 8. 绘制火花 trailsCtx.lineWidth = Spark.drawWidth; trailsCtx.lineCap ='butt';// 类似星星的绘制...// 9. 绘制速度条if(speedBarOpacity){ mainCtx.globalAlpha = speedBarOpacity; mainCtx.fillStyle =COLOR.Blue; mainCtx.fillRect(0, height -6, width * simSpeed,6); mainCtx.globalAlpha =1;}// 10. 重置变换 trailsCtx.resetTransform(); mainCtx.resetTransform();}8. 天空颜色计算
const currentSkyColor ={r:0,g:0,b:0};const targetSkyColor ={r:0,g:0,b:0};functioncolorSky(speed){const maxSkySaturation =30;// 最大天空饱和度const maxStarCount =500;// 达到最大亮度所需的星星数量let totalStarCount =0; targetSkyColor.r =0; targetSkyColor.g =0; targetSkyColor.b =0;// 累计所有颜色的亮度COLOR_CODES.forEach(color=>{const tuple =COLOR_TUPLES[color];const count = Star.active[color].length; totalStarCount += count; targetSkyColor.r += tuple.r * count; targetSkyColor.g += tuple.g * count; targetSkyColor.b += tuple.b * count;});// 非线性映射,模拟人眼感知const intensity = Math.pow(Math.min(1, totalStarCount / maxStarCount),0.3);// 归一化并应用强度const maxColorComponent = Math.max(1, targetSkyColor.r, targetSkyColor.g, targetSkyColor.b); targetSkyColor.r = targetSkyColor.r / maxColorComponent * maxSkySaturation * intensity; targetSkyColor.g = targetSkyColor.g / maxColorComponent * maxSkySaturation * intensity; targetSkyColor.b = targetSkyColor.b / maxColorComponent * maxSkySaturation * intensity;// 平滑过渡const colorChange =10; currentSkyColor.r +=(targetSkyColor.r - currentSkyColor.r)/ colorChange * speed; currentSkyColor.g +=(targetSkyColor.g - currentSkyColor.g)/ colorChange * speed; currentSkyColor.b +=(targetSkyColor.b - currentSkyColor.b)/ colorChange * speed;// 应用到背景 appNodes.canvasContainer.style.backgroundColor =`rgb(${currentSkyColor.r |0}, ${currentSkyColor.g |0}, ${currentSkyColor.b |0})`;}五、用户交互
1. 点击发射
functionhandlePointerStart(event){ activePointerCount++; FireworkSound.unlock();// iOS 音频解锁 FireworkSound.resume();const btnSize =44;// 点击顶部按钮区域if(event.y < btnSize){if(event.x < btnSize){togglePause();return;}if(event.x > mainStage.width/2- btnSize/2&& event.x < mainStage.width/2+ btnSize/2){toggleLongExposure();return;}if(event.x > mainStage.width - btnSize){toggleMenu();return;}}// 点击画布发射烟花if(!canInteract())return;if(updateSpeedFromEvent(event)){ isUpdatingSpeed =true;}elseif(event.onCanvas){launchShellFromConfig(event);}}2. 键盘快捷键
functionhandleKeydown(event){if(event.keyCode ===80)togglePause();// P 键 - 暂停if(event.keyCode ===79)toggleMenu();// O 键 - 菜单if(event.keyCode ===27)toggleMenu(false);// ESC 键 - 关闭菜单}3. 自动发射序列
functionstartSequence(){if(isFirstSeq){ isFirstSeq =false;const shell =newShell(crysanthemumShell(shellSizeSelector())); shell.launch(0.5,0.5);// 首发居中return2400;}if(finaleSelector()){// 终场模式:快速连续发射seqRandomShell();if(currentFinaleCount < finaleCount){ currentFinaleCount++;return170;// 170ms 间隔}else{ currentFinaleCount =0;return6000;// 暂停 6 秒}}// 随机选择发射模式const rand = Math.random();if(rand <0.2)returnseqSmallBarrage();// 小型连发if(rand <0.6)returnseqRandomShell();// 单发if(rand <0.8)returnseqTwoRandom();// 双发returnseqTriple();// 三发}六、性能优化要点
| 优化技术 | 说明 | 效果 |
|---|---|---|
| 对象池 | 复用 Star/Spark 对象 | 减少 GC 压力 |
| 颜色分组 | 按颜色分组批量渲染 | 减少 Canvas 状态切换 |
| 双缓冲 | trails-canvas 保留历史轨迹 | 实现拖尾效果 |
| requestAnimationFrame | 与屏幕刷新同步 | 流畅动画 |
| 空气阻力模拟 | 粒子速度衰减 | 自然消散效果 |
| 设备像素比适配 | scale(dpr, dpr) | 高清显示 |
七、iOS 适配
1. Meta 标签优化
<!-- 视口适配刘海屏 --><metaname="viewport"content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"><!-- iOS Web App 支持 --><metaname="apple-mobile-web-app-capable"content="yes"><metaname="apple-mobile-web-app-status-bar-style"content="black-translucent"><metaname="apple-mobile-web-app-title"content="烟花盛宴"><!-- 禁用电话号码识别 --><metaname="format-detection"content="telephone=no"><!-- PWA Manifest --><linkrel="manifest"href="data:application/json;base64,...">2. CSS 触摸优化
*{-webkit-tap-highlight-color: transparent;/* 移除点击高亮 */-webkit-touch-callout: none;/* 禁用长按菜单 */-webkit-user-select: none;/* 禁止选择文字 */touch-action: manipulation;/* 禁用双击缩放 */}body{overscroll-behavior: none;/* 禁用橡皮筋效果 */padding:env(safe-area-inset-*);/* 适配安全区域 */}3. Web Audio 解锁
unlock(){if(this.isUnlocked)returntrue;// iOS 要求:必须在用户交互中创建并播放音频if(this.ctx.state ==='suspended'){this.ctx.resume();}// 播放静音缓冲区解锁音频const buffer =this.ctx.createBuffer(1,1,22050);const source =this.ctx.createBufferSource(); source.buffer = buffer; source.connect(this.ctx.destination); source.start(0);this.isUnlocked =true;}4. 手势防护
// 禁用 iOS 缩放手势 document.addEventListener('gesturestart',(e)=> e.preventDefault()); document.addEventListener('gesturechange',(e)=> e.preventDefault()); document.addEventListener('gestureend',(e)=> e.preventDefault());// 防止双击缩放let lastTouchEnd =0; document.addEventListener('touchend',(e)=>{const now = Date.now();if(now - lastTouchEnd <=300){ e.preventDefault();} lastTouchEnd = now;});5. iOS 支持功能表
| 功能 | 状态 | 说明 |
|---|---|---|
| 添加到主屏幕 | ✅ | 支持全屏运行 |
| 声音播放 | ✅ | 用户交互后解锁 |
| 刘海屏适配 | ✅ | 安全区域 padding |
| 禁用缩放 | ✅ | 手势防护 |
| 禁用橡皮筋效果 | ✅ | overscroll-behavior |
| 触摸响应 | ✅ | Pointer Events |
八、文件依赖
外部库
├── [email protected] - 全屏 API 兼容库 ├── [email protected] - Canvas 舞台管理 └── MyMath.js - 数学工具函数 本地文件
└── style.css - 额外样式(可选) 总结
这是一个功能完整的交互式烟花动画网页,主要特点:
- 视觉效果:双层 Canvas 实现拖尾效果,混合模式增强亮度
- 音效系统:8 种不同风格的音效,Web Audio API 实时生成
- 物理模拟:重力、空气阻力、粒子生命周期
- 状态管理:发布-订阅模式,localStorage 持久化
- 性能优化:对象池、颜色分组、requestAnimationFrame
- 跨平台:完整的 iOS/移动端适配