ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

原生JavaScript与Canvas:打造七夕晚风粒子动画页面

原生JavaScript与Canvas:打造七夕晚风粒子动画页面 七夕的礼物困局通常不是预算问题而是“表达成本”问题。同样一句“七夕快乐”有人发文字有人发红包也有人把自己写的一个网页链接发过去。后者之所以特别不是因为它多高端而是它把情绪拆成了可交互的细节晚风是动的涟漪是圆的爱意是能点开的。这篇文章想做的就是一个完全不依赖框架的小型前端实战用原生 HTML、CSS 和 JavaScript 做一个“晚风定义”主题页面。页面会有一层持续流动的粒子来模拟晚风点击任意位置会产生涟漪文字会一句一句敲出来最后还有一个点击触发 99 枚爱心粒子的“七夕奖励”彩蛋。整段文案对应着那句“该怎么定义这场晚风是涟漪是汹涌是遇见你之后的每一天都觉得很走运。”你可以把它当作送给某个人的礼物也可以把它当作一次前端动画练手。它的技术点非常集中canvas 2D、requestAnimationFrame、DOM 事件、简单的日期计算。全部代码跑通大概 200 行左右难的不是代码本身而是理解每一段为什么这样写。读完这篇文章你会得到一个完整可运行的页面也顺便把“粒子动画 背景交互”这套常见前端玩法吃透。1. 这篇文章真正要解决的问题七夕前后打开社交平台最常见的状态有两种一种是在纠结送什么另一种是刷到别人的浪漫案例后开始焦虑。如果本身是开发者或者正在学前端其实有一个比较优解——亲手做一个网页。网页的可定制性极强文案、配色、动画、音乐都能按自己的审美调整而且发布成本很低一个公开链接就能完成分享。但很多人在“从哪开始”这一步被卡住。用框架需要考虑打包、部署、兼容用现成模板改起来不够顺手里面可能还塞了一堆用不到的依赖直接找网上的开源表白页面很多代码写得很杂复制过来甚至跑不起来更谈不上理解原理。所以这篇文章的目标很明确不用任何第三方库不引入 Node 构建工具只用浏览器原生能力写出一个完整可运行、可修改的七夕主题网页。读完你会获得四样东西一份清晰的目录结构知道每个文件职责是什么一份直接可复制的前端代码覆盖页面结构、样式和逻辑对 canvas 动画、粒子系统、打字机效果、本地静态服务的完整理解遇到“动画不显示”“手机上卡顿”“本地打不开”时的排查路径。这里还要先说明“奖励 99”的边界。在本文中它是视觉彩蛋点击按钮后从屏幕中心喷出 99 枚爱心粒子配合“七夕快乐”的文案。如果你的真实需求是发放 99 份实体奖品那必须走完整的运营合规流程前端页面本身承担不了权益发放。所有标注“奖励”的按钮在真实产品里都要对应真实、合法的权益说明不能只是一个空壳。2. 基础概念与核心原理先建立一个整体认知任何一个网页至少由结构、样式、行为三部分构成。结构是 HTML决定页面有哪些元素样式是 CSS决定元素看起来什么样、如何布局行为是 JavaScript决定页面如何响应用户操作。七夕页面也不例外背景晚风动画用 canvas 绘制文字和按钮用 HTML/CSS 布局点击涟漪和打字机效果由 JavaScript 实现。接下来要理解 canvas 是什么。canvas 是 HTML5 提供的一块画布JavaScript 通过它的 2D 上下文在上面绘制图形。它不像普通 HTML 元素那样以大量 DOM 节点存在而是像素级绘制。绘制本身没有“记忆”每一帧都要重画一次。所以动画的本质是清空画布、重绘所有图形、再清空、再重绘循环往复。把每个粒子的坐标、速度、透明度和大小记录在 JavaScript 对象里每一帧更新这些值就能形成流动的视觉。这里有一个容易混淆的对比CSS 动画和 canvas 动画的区别。CSS 动画适合单个元素的位移、旋转、淡入淡出浏览器会自动做过渡计算canvas 动画适合大量对象同时运动比如几百个粒子、星星、烟花。如果试图用 CSS 去写 100 个粒子的运动DOM 节点太多会导致性能压力用 canvas 可以在一张画布上统一绘制消耗的是绘制能力而不是 DOM 结构。晚风粒子这种数量级适合用 canvas。比较项CSS 动画canvas 动画适合对象少量 DOM 元素的过渡效果大量粒子的独立运动实现方式声明 transition/animation 关键帧每帧调用 JS 绘制 API性能瓶颈DOM 节点过多每帧绘制逻辑复杂时较消耗 CPU上手难度较低需要理解坐标系统和帧循环适合场景按钮、弹窗、卡片动效粒子、烟花、星空、涟漪动画循环的驱动方式也值得说清楚。JavaScript 里有两种常见选择setInterval 和 requestAnimationFrame。setInterval 指定每多少毫秒执行一次它不关心浏览器当前是否真的需要绘制页面切到后台时会继续消耗资源。requestAnimationFrame 则是告诉浏览器“下一帧绘制前请调用我”浏览器会在合适的时机回调掉帧时会自动降频切后台时会暂停更省电也更平滑。所以本项目的动画循环使用 requestAnimationFrame。还有一个概念是“晚风定义”。题目文案问的是一个抽象感受程序没办法直接描述“晚风”但可以把风转译成一组视觉参数粒子从右下往左上缓慢移动偏移量随机透明度偏低覆盖整个屏幕。这些参数组合在一起观看者才会感觉到一阵“温柔的晚风”。所以所谓的定义在代码里就是状态和参数。这个思路理解清楚后想调整任何效果都有方向可循。3. 环境准备与前置条件这个项目对环境的要求非常低甚至可以说不存在传统意义的“环境安装”负担。你只需要一个现代浏览器和一个文本编辑器。现代浏览器推荐 Chrome 或 Edge开发者工具对前端调试最友好文本编辑器可以是 VS Code也可以是 Sublime Text、Notepad甚至系统自带的记事本都能跑通。是否需要安装 Node.js不需要。这个项目没有 npm 依赖、没有打包步骤CSS 和 JS 都是原生文件。是否需要安装 Live Server不是必须但推荐。直接用浏览器双击打开 index.html大部分情况下也能运行不过在 file:// 协议下localStorage、音频等能力可能受浏览器限制。用本地静态服务器访问更接近真实线上环境调试也省事。建议的文件目录结构如下late-wind/ ├── index.html ├── style.css └── script.js三个文件放在同一个目录里。为什么分成三个而不是全部写在一个 HTML 里主要考虑到实用性。分离之后你可以单独调整样式、单独修改逻辑后续部署到任意静态服务器时也更符合常见规范。如果只是为了快速验证写成单文件也可以但本文按工程习惯来做。4. 核心流程拆解整个实现过程可以分成六步定文案、搭结构、写动画、加交互、补日期、做彩蛋。每一步之间耦合不大很适合边写边验证。下面按顺序拆开说明并给出关键代码片段。完整代码在下一章你可以先看明白流程再直接复制。4.1 确定页面文案与交互流程页面文案沿用原材料里的核心表达“该怎么定义这场晚风”“是涟漪是汹涌”“是遇见你之后的每一天都觉得很走运。”“七夕快乐。”我建议把这段文案做成打字机效果让文字一句一句出现而不是一次性堆在屏幕上。这样既保留情绪节奏也能让观看者的视线跟着文案走。交互流程可以设计成页面打开后晚风粒子持续流动用户点击任意位置产生涟漪点击中央的“领取七夕奖励”按钮屏幕中心喷出 99 枚爱心粒子。从体验角度看这个流程的节奏比较合理先有氛围再有内容最后给用户一个主动触发的惊喜。很多节日运营页面的基本逻辑也是这个框架氛围铺垫、情感内容、转化动作。4.2 搭建 DOM 结构页面结构只需要三层canvas 背景层、文案内容层、按钮和提示区域。canvas 放在最底层用于绘制晚风粒子和涟漪content 区域用来承载打字机文案、日期提示和按钮。这里需要注意script 标签要放在 body 底部因为 JavaScript 执行时需要保证所有 DOM 节点已经存在。canvas idwindCanvas/canvas main classcontent p classtyping idtypingText/p p classdays iddaysText/p button classreward-btn idrewardBtn领取七夕奖励/button div classreward-tip idrewardTip/div /main这种结构的优点是职责清晰。canvas 是画布不会影响文字区域的布局content 用绝对定位或 flex 布局居中可以适配不同尺寸的屏幕。后续要加背景音乐、抽奖组件或其他元素都可以在 main 内继续扩展。4.3 定义晚风粒子系统粒子的本质是一个个普通 JavaScript 对象。每个粒子有自己的 x、y 坐标速度 vx、vy大小 size透明度 opacity 和颜色 color。创建足够多的粒子后每一帧动画里执行两步先 update 更新位置再 draw 绘制自己。class Particle { constructor() { this.x Math.random() * window.innerWidth; this.y Math.random() * window.innerHeight; this.vx (Math.random() - 0.3) * 1.2; this.vy (Math.random() - 0.8) * 0.8; this.size 2 Math.random() * 4; } update() { this.x this.vx; this.y this.vy; } }这里有一个容易踩的坑晚风不是垂直下落也不是完全没有规则地乱飘而是有一个主导方向。为了让视觉接近“晚风”粒子的整体移动方向应该偏左上并且每个粒子的速度有小范围随机差异。控制这个手感的核心参数就是 vx、vy 的取值范围。想让风更柔和就把数值范围缩小想让风更明显就调大位移量。4.4 涟漪交互涟漪是画布上的一个扩散圆环。每次点击 canvas 时把点击坐标 push 进一个 ripples 数组。每个涟漪对象包含 x、y、当前半径、最大半径和透明度。在动画循环中每帧更新半径并绘制圆环等到透明度归零后从数组中删除。canvas.addEventListener(click, e { ripples.push(new Ripple(e.clientX, e.clientY)); });这本身不复杂但需要注意数值限制。如果不限制最大半径圆环会一直在画布上扩散影响性能如果不降低透明度叠加多次点击后画面会变得混乱。所以涟漪既要设置 maxRadius也要在每一帧里逐步减少 opacity。4.5 打字机与纪念日计算打字机不涉及动画帧而是用 setTimeout 递归地控制字符串截取长度。多行文案不能简单地在同一行追加应该按行依次执行当前行打完后停顿一下再开始下一行。日期计算也简单用目标日期减去今天的日期得到毫秒差再除以一天的毫秒数。这个页面里的纪念日适合用来展示“遇见你之后已经走运多少天”把用户自己的关系开始日期写进代码即可。需要注意时区问题建议使用new Date(2020-07-07)这种字符串方式不要直接写new Date()的本地时间否则跨时区访问会出现偏差。4.6 “奖励 99”彩蛋点击按钮后连续创建 99 个爱心粒子加入粒子数组让它们从页面中心向四周散开。为什么是 99是为了呼应标题里的“奖励 99”。在实际代码里它只是渲染参数不指向任何真实价值。为了保证视觉效果不是一瞬间全部出现我会给每个粒子设置一个setTimeout延迟比如每隔 30 毫秒放出一个粒子这样 99 个爱心会在 3 秒左右陆续飞出来观感上更接近“正在送达”的仪式感。5. 完整示例与代码实现下面给出完整可运行的三份文件。为了保证文章的完整性我没有把代码拆成零散片段而是按实际使用方式整理。建议你也保持这个目录结构后面修改和部署会清楚很多。5.1 文件index.html!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title晚风定义 · 七夕快乐/title link relstylesheet hrefstyle.css /head body canvas idwindCanvas/canvas main classcontent p classtyping idtypingText/p p classdays iddaysText/p button classreward-btn idrewardBtn领取七夕奖励/button div classreward-tip idrewardTip/div /main script srcscript.js/script /body /html这份 HTML 的职责只有三件事引入样式、摆放页面骨架、在底部引入 JavaScript。没有多余的依赖也没有内联脚本。页面标题里的“晚风定义”会直接显示在浏览器标签页上。5.2 文件style.css* { margin: 0; padding: 0; box-sizing: border-box; } html, body { width: 100%; height: 100%; overflow: hidden; font-family: PingFang SC, Microsoft YaHei, sans-serif; } #windCanvas { position: fixed; top: 0; left: 0; z-index: 0; } .content { position: relative; z-index: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100vh; padding: 24px; text-align: center; color: #fff; text-shadow: 0 2px 12px rgba(0, 0, 0, 0.35); } .typing { min-height: 1.6em; font-size: clamp(20px, 4vw, 36px); font-weight: 600; letter-spacing: 2px; margin-bottom: 16px; } .days { font-size: clamp(14px, 2.4vw, 18px); opacity: 0.9; margin-bottom: 40px; } .reward-btn { border: 1px solid rgba(255, 255, 255, 0.7); background: rgba(255, 255, 255, 0.15); color: #fff; padding: 12px 28px; font-size: 16px; border-radius: 999px; cursor: pointer; transition: background 0.3s ease, transform 0.2s ease; } .reward-btn:hover { background: rgba(255, 255, 255, 0.3); transform: scale(1.05); } .reward-tip { margin-top: 18px; min-height: 1.4em; font-size: 15px; opacity: 0.95; }样式部分有两个关键点。第一html 和 body 设置了overflow: hidden避免页面出现滚动条让 canvas 固定占满整个视口。第二.content用 flex 垂直居中文字层级通过z-index: 1浮在 canvas 上方。按钮使用半透明背景加圆角视觉上更轻盈和“晚风”的主题一致。5.3 文件script.js// 晚风定义粒子 涟漪 打字机 七夕彩蛋 // 文件路径late-wind/script.js const canvas document.getElementById(windCanvas); const ctx canvas.getContext(2d); let width 0; let height 0; let particles []; let ripples []; let running true; const COLORS [#ffe6f0, #ffd1dc, #ffb3c1, #fff0e6, #f9e7fe]; function resizeCanvas() { width canvas.width window.innerWidth; height canvas.height window.innerHeight; } window.addEventListener(resize, resizeCanvas); resizeCanvas(); function randomBetween(min, max) { return min Math.random() * (max - min); } function drawHeart(ctx, x, y, size) { ctx.save(); ctx.translate(x, y); ctx.beginPath(); ctx.moveTo(0, size * 0.35); ctx.bezierCurveTo(size * 0.55, -size * 0.35, size * 1.2, size * 0.4, 0, size * 1.1); ctx.bezierCurveTo(-size * 1.2, size * 0.4, -size * 0.55, -size * 0.35, 0, size * 0.35); ctx.fill(); ctx.restore(); } class Particle { constructor() { this.x randomBetween(0, width); this.y randomBetween(0, height); this.size randomBetween(2, 6); this.vx randomBetween(-0.3, 0.6); this.vy randomBetween(-0.8, -0.2); this.opacity randomBetween(0.2, 0.7); this.color COLORS[Math.floor(Math.random() * COLORS.length)]; this.shape dot; } update() { this.x this.vx; this.y this.vy; if (this.x width this.size) this.x -this.size; if (this.x -this.size) this.x width this.size; if (this.y height this.size) this.y -this.size; if (this.y -this.size) this.y height this.size; } draw() { ctx.globalAlpha Math.max(0, Math.min(1, this.opacity)); ctx.fillStyle this.color; if (this.shape heart) { drawHeart(ctx, this.x, this.y, this.size); } else { ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fill(); } } } class Ripple { constructor(x, y) { this.x x; this.y y; this.radius 0; this.maxRadius randomBetween(80, 160); this.opacity 0.7; } update() { this.radius 2; this.opacity - 0.015; } draw() { if (this.opacity 0) return; ctx.globalAlpha Math.max(0, this.opacity); ctx.strokeStyle #ffffff; ctx.lineWidth 2; ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); ctx.stroke(); } } function createParticles(count) { for (let i 0; i count; i) { particles.push(new Particle()); } } function animate() { if (!running) return; ctx.clearRect(0, 0, width, height); particles.forEach(p { p.update(); p.draw(); }); ripples ripples.filter(r r.opacity 0 r.radius r.maxRadius); ripples.forEach(r { r.update(); r.draw(); }); ctx.globalAlpha 1; requestAnimationFrame(animate); } canvas.addEventListener(click, e { ripples.push(new Ripple(e.clientX, e.clientY)); }); const TYPING_TEXTS [ 该怎么定义这场晚风, 是涟漪是汹涌, 是遇见你之后的每一天, 都觉得很走运。, 七夕快乐。 ]; const typingEl document.getElementById(typingText); let lineIndex 0; let charIndex 0; function typeLine() { if (lineIndex TYPING_TEXTS.length) return; const current TYPING_TEXTS[lineIndex]; if (charIndex current.length) { typingEl.textContent current.slice(0, charIndex); charIndex; setTimeout(typeLine, 120); } else { charIndex 0; lineIndex; setTimeout(typeLine, 500); } } const startDate new Date(2020-07-07); const today new Date(); const diffDays Math.floor((today - startDate) / (1000 * 60 * 60 * 24)); document.getElementById(daysText).textContent 遇见你之后已经走运 ${diffDays} 天; const rewardBtn document.getElementById(rewardBtn); const rewardTip document.getElementById(rewardTip); rewardBtn.addEventListener(click, () { rewardsDraw(); rewardTip.textContent 99 枚晚风爱心正在送达七夕快乐。; }); function rewardsDraw() { for (let i 0; i 99; i) { setTimeout(() { const p new Particle(); p.shape heart; p.size randomBetween(8, 14); p.vx randomBetween(-1.5, 1.5); p.vy randomBetween(-2.5, -0.5); p.x width / 2 randomBetween(-80, 80); p.y height / 2 randomBetween(-80, 80); p.color COLORS[i % COLORS.length]; p.opacity 1; particles.push(p); }, i * 30); } } createParticles(80); typeLine(); animate();这段代码是核心重点说明几个逻辑resizeCanvas 每次窗口变化时更新 canvas 宽高避免画面拉伸变形。Particle 和 Ripple 是两个独立的类一个负责连续运动一个负责一次性扩散职责清晰。draw 方法里先把 globalAlpha 设置到一个合理范围再设置 fillStyle避免透明度残留导致图形忽深忽浅。蛋糕彩蛋使用 drawHeart 绘制爱心代替了图片素材页面因此完全不需要加载外部图片。startDate 是纪念日文案会显示到今天的实际天数你可以改成自己的日期。5.4 如何改成自己的文案和日期最直接的做法是改 TYPING_TEXTS 数组里的字符串改成你真正想说的话。这里建议保持字符串顺序和标点风格因为打字机的换行依赖数组的顺序。日期就修改new Date(2020-07-07)这一行的值。颜色如果想要更贴近自己的审美可以改 COLORS 数组使用你喜欢的十六进制色值。如果你想修改“奖励 99”的数值把 rewardsDraw 里的循环次数从 99 改成其他数字即可。如果改成较大的数字建议同步调大i * 30的延迟否则大量粒子会在短时间内同时出现造成一定程度的性能波动。6. 运行结果与效果验证写完代码后不要直接双击 index.html最好用本地静态服务器方式访问。下面以 Python 为例在 late-wind 目录下打开终端执行cd late-wind python3 -m http.server 8080启动成功后浏览器访问http://localhost:8080如果使用的是 VS Code也可以安装 Live Server 插件右键 index.html 选择 Open with Live Server效果是等价的。页面正常打开后你应该依次看到以下效果浏览器标题显示“晚风定义 · 七夕快乐”背景有浅粉色粒子持续缓慢地飘动方向整体偏左上方中央区域开始打字机输出文案第一行是“该怎么定义这场晚风”日期行显示“遇见你之后已经走运 N 天”点击页面任意位置出现一圈白色扩散涟漪点击“领取七夕奖励”按钮按钮下方出现提示随后从屏幕中心陆续喷出 99 枚爱心粒子。判断是否成功除了看视觉效果还要打开浏览器开发者工具找到 Console 标签页。如果有一段红色的报错信息优先看报错中的文件和行号。正常情况下Console 应该是干净的没有任何报错。如果页面能显示但动画不动说明 requestAnimationFrame 循环可能被异常终止了或者 JavaScript 文件没有正确加载。在 Chrome 里按 F12 打开开发者工具切到 Network 面板也可以确认 style.css 和 script.js 的请求状态是否为 200。如果这两个文件返回 404通常是因为三个文件不在同一个目录下或者文件名大小写不一致。7. 常见问题与排查思路这部分给出一份可直接复用的排查表按“现象→原因→排查方式→解决”的顺序整理。问题现象可能原因排查方式解决方案页面打开后只有白色背景没有粒子script.js 加载失败或报错打开 Console 看是否有报错Network 看 script.js 状态确认文件路径和文件名一致修复 JS 语法错误canvas 粒子位置错位图形被拉伸没有在窗口尺寸变化时同步更新 canvas 宽高缩小浏览器窗口看是否错位检查 resizeCanvas 是否绑定 window resize 事件调用 resizeCanvas并重新执行 canvas.width/height 赋值页面出现滚动条动画区域超出屏幕html/body 没有设置 overflow: hidden查看样式是否生效给 html、body 加上 overflow: hiddenheight 设为 100%点击“领取七夕奖励”没有反应按钮事件未绑定或 script.js 在 DOM 之前执行确认 script 标签在 body 底部Console 查询 rewardBtn 是否为 null将 script 放到 body 最后或使用 DOMContentLoaded 包裹逻辑手机上频繁点击按钮后卡顿粒子数量持续增加canvas 每帧绘制量过大打开性能面板观察帧率或注释部分新增粒子验证限制粒子总数或加入冷却时间避免短时间多次触发日期显示为 NaN 或计算错误日期格式不适合当前运行时环境Console 输出 diffDays 和 startDate 的值使用稳定的日期字符串格式例如 new Date(2020-07-07)双击打开 index.html 一切正常但部署到服务器后样式丢失静态资源路径或服务器根目录配置问题检查 Network 中 css/js 请求路径将三个文件保持同目录结构上传使用相对路径引用 css/js这里单独说一下性能问题。页面初始只有 80 个粒子对现代浏览器来说压力很小。但彩蛋每次点击都会追加 99 个爱心粒子如果连续点击三次以上粒子总数可能达到 300 以上低端手机上会开始掉帧。遇到这种情况可以设置一个冷却时间比如点击后 5 秒内忽略重复点击或者在粒子数量超过 400 时不再新增。这是最容易忽略的优化点。8. 最佳实践与工程建议这个项目虽然小但完整走了一遍纯前端页面的开发流程。为了让它能在真实分享场景里表现稳定下面几条建议值得注意。8.1 性能优化粒子数量不是越多越好。80 个粒子已经能提供足够的“晚风流动感”再加上 99 个爱心彩蛋已经接近移动端的舒适上限。如果一定要增强效果优先调整粒子的颜色、大小和透明度而不是无脑堆数量。另外animate 循环里每次 clearRect 都是全屏清空在 Retina 屏幕上开发者可以做 devicePixelRatio 适配但本项目不做也不会影响基本使用。还有一个细节容易被忽略粒子的透明度控制和 globalAlpha 重置。每帧绘制结束时把 globalAlpha 设回 1否则下一次循环可能出现透明度叠加画面会越来越淡或越来越浓。代码里已经做了这个处理自己在扩展时要保持这个习惯。8.2 移动端适配HTML 已经加入了 viewport meta 标签这是移动端显示正常的基础。点击事件在移动端也适用但如果你想在手机上有更细腻的触摸反馈可以把 click 换为 pointerdown 或 touchstart。要注意的是touchstart 触发频率更高如果每次都创建多个涟漪可能导致性能下降。实际项目中更合理的做法是触摸时先判断 touches 数量再决定是否创建涟漪。8.3 部署与分享部署一个静态页面有很多低成本方式。GitHub Pages、Netlify、Vercel、对象存储都可以。由于这个项目不需要构建直接把三个文件上传到静态托管服务即可。部署后需要在真实手机浏览器里再测一遍重点看三点页面是否能正常打开、点击彩蛋是否流畅、日期文案是否正确显示。如果你想给这个页面增加背景音乐可以准备一首有版权授权或原创的 mp3在 HTML 里加入audio标签然后在 script.js 里监听用户点击按钮时调用 play()。要注意浏览器自动播放策略通常要求用户主动交互后才能播放所以在页面加载时直接 play 很可能会被拦截。8.4 安全与合规提醒这类节日页面如果要发给别人不要收集任何个人隐私信息也不要夹带跳转链接以外的小程序等跳转能力。如果页面里有“奖励”按钮但实际不兑现任何权益一定要在文案里写明是彩蛋效果避免给对方造成误解。如果要在真实运营场景里发奖前端只是一个展示层后端必须做实名、资格校验、发放记录、防刷控制并且遵守平台运营规则和当地法律法规。还有一个工程层面的建议文案不要写死在逻辑代码里可以单独抽成一个配置对象。这样以后改节日文案不用在打字机代码里找字符串。比如const CONFIG { typingTexts: [], startDate: 2020-07-07, rewardCount: 99 };这个习惯对后续维护很重要尤其是这种带有情绪文案的页面经常会被改来改去。9. 总结与后续学习方向到这里这个“晚风定义”页面就完整跑通了。你实际做过的核心点包括原生 canvas 粒子动画、requestAnimationFrame 帧循环、点击涟漪交互、打字机效果、纪念日计算以及一个完全不需要图片素材的爱心彩蛋。如果你之前只是用框架写过页面这次会明显感觉到原生实现并没有想象中复杂反而更可控。如果接下来想继续深入有三个方向值得探索第一把粒子改成更复杂的形状比如花瓣、星星或者用随机几何图形做一个抽象晚风第二引入 Web Audio API用代码合成一段简单的背景旋律替代版权不明的音频文件第三用 Three.js 或者其他 3D 方案把晚风粒子放到立体空间里做出更强烈的沉浸感。当然扩展的前提是先熟悉本文这套“对象状态 每帧重绘”的思维模型。如果你是在七夕前看到这篇文章推荐的路径很简单按第 5 章代码跑通把文案改成自己的故事用本地服务器验证一遍再部署成一个链接发出去。晚风到底怎么定义每个人都有自己的答案。代码只是把答案变成了一种可以被打开、被看见、被保存的形式。
返回列表