简介这是一份面向前端开发者与H5互动页面学习者的开红包特效实战资源聚焦社交、电商场景中点击拆红包的完整交互实现。包内共9个文件以5个png与1个jpg图片素材、1个js脚本、1个css样式表和1个html页面为主压缩包约130KB结构轻量便于直接运行与二次改造。素材覆盖红包开启前后的多张状态图脚本部分基于jQuery处理点击事件与DOM更新样式表负责过渡与关键帧动画页面文件串联整体流程。读者可从中了解CSS3动画与JavaScript事件如何配合完成拆包动效学习随机金额生成、图片资源替换与响应式适配的基本思路并参考模块化组织方式将红包组件快速迁移到自己的活动页中。目前已有1075人学习下载适合希望低成本掌握H5互动特效实现细节的前端入门与进阶开发者参考。1. 拆开一个「h5开红包特效」它到底在哪些场景里跑谁需要它春节活动页、直播间抽奖弹窗、线下扫码领券用户点下去的那一瞬间屏幕中央弹出一个红包金色粒子炸开、红包翻转、金额数字滚动最后停在「8.88 元」。这套动作就是通常说的 h5开红包特效。它不是一段 CSS 动画那么简单而是「触发时机 视觉层叠 金额数据 兼容兜底」四件事咬在一起的结果。做活动运营的、做直播的前端 h5 的、接企业微信里嵌页面的基本都会碰到它。真正难的地方不在动画本身而在于低端安卓机上粒子一多就掉帧微信内置浏览器对某些合成属性支持不一致金额还没从接口回来动画就已经跑完了。这篇就把这套特效从选型、实现、参数到踩坑完整走一遍目标是你看完能直接在自己项目里复现并且知道哪些参数不能乱调。2. 红包特效的技术选型CSS 动画、Canvas 还是 Lottie2.1 三种主流方案的能力边界在动手写第一行代码之前先想清楚用哪条路。h5开红包特效的视觉构成一般分三层底层是红包本体图片或 DOM中层是粒子/金币飞散顶层是金额数字和按钮。不同层适合的技术不一样。纯 CSS 动画transform opacity keyframes适合红包翻转、缩放、按钮呼吸这类结构简单的动作。优点是代码量小、GPU 合成友好、调试直观缺点是粒子数量一多DOM 节点爆炸低端机上直接卡成幻灯片。我一般把 CSS 动画用在红包本体和按钮上粒子另想办法。Canvas 2D 适合粒子系统、金币雨、光晕扩散这类需要大量重复绘制的效果。一个 canvas 上画几百个粒子性能远好于几百个 div。代价是要自己管 requestAnimationFrame 循环、自己处理 devicePixelRatio代码复杂度上去了。Lottie 适合设计师已经用 AE 做好、导出成 JSON 的复杂序列动画。它的优势是还原度高、设计师能直接交付劣势是 JSON 体积可能几百 KB首屏加载慢而且动态改金额数字这类需求不好接。如果活动页对包体积敏感Lottie 要谨慎。方案适合层包体积低端机表现动态改数据CSS 动画红包本体、按钮极小好容易Canvas 2D粒子、金币雨小较好容易Lottie整体序列动画较大一般较难2.2 我的组合策略与选型理由实际项目里我不会只用一种。常见做法是红包本体用一张 webp 图 CSS transform 做翻转和缩放粒子层单独开一个绝对定位的 canvas 盖在上面金额数字用 DOM 做滚动。这样三层各司其职任何一层出问题都好定位。为什么红包本体不用 canvas 画因为红包的视觉细节纹理、阴影、圆角用图片还原成本最低而且图片可以被浏览器缓存。为什么粒子一定要用 canvas因为粒子数量通常在 80 到 300 之间用 DOM 的话每次重排重绘都是灾难。为什么金额用 DOM 而不是画在 canvas 上因为金额需要能被无障碍读取、能被自动化测试抓到、字体渲染也更清晰。选型定下来之后接下来就是把这套结构真正搭起来。下一章从零开始写一个能跑的最小版本。3. 从零实现一个可复用的开红包特效3.1 页面结构与层级约定先把 DOM 骨架定好。层级关系决定了 z-index 和定位方式一开始乱后面很难改。我的习惯是三层容器外层负责全屏遮罩和居中中层放红包本体粒子 canvas 单独一层盖在最上面但 pointer-events 设为 none避免挡住按钮点击。!-- 红包特效容器fixed 全屏默认隐藏 -- div idredpacket-layer classrp-layer aria-hiddentrue !-- 遮罩点击可关闭也可设为不可关闭 -- div classrp-mask/div !-- 红包本体用图片 CSS 动画 -- div classrp-body img classrp-cover src./redpacket.webp alt红包 / div classrp-amountspan idrp-number0.00/span 元/div button idrp-open-btn classrp-btn开/button /div !-- 粒子层盖在最上面不接收点击 -- canvas idrp-canvas classrp-canvas/canvas /div这段结构里rp-layer用position: fixed铺满视口rp-canvas用pointer-events: none保证不拦截按钮。aria-hidden在隐藏时设为 true显示时改 false兼顾无障碍。红包本体用图片而不是纯 CSS 画是为了还原设计稿的质感。3.2 红包翻转与金额滚动的 CSS 实现红包「开」的动作本质是封面向上翻开或缩放消失露出金额。我用的是封面rotateX翻转配合scale时长控制在 400ms 左右太快看不清太慢用户等得烦。.rp-layer { position: fixed; inset: 0; display: none; z-index: 9999; } .rp-layer.show { display: flex; align-items: center; justify-content: center; } .rp-mask { position: absolute; inset: 0; background: rgba(0,0,0,.55); } .rp-body { position: relative; width: 60vw; max-width: 320px; text-align: center; } .rp-cover { width: 100%; display: block; transform-origin: top center; transition: transform .4s cubic-bezier(.4,0,.2,1), opacity .4s; } /* 打开后封面向上翻走 */ .rp-body.opened .rp-cover { transform: rotateX(-120deg) scale(.9); opacity: 0; } .rp-amount { font-size: 32px; font-weight: 700; color: #ffd24d; opacity: 0; transition: opacity .3s .2s; } .rp-body.opened .rp-amount { opacity: 1; } .rp-canvas { position: absolute; inset: 0; pointer-events: none; }这里的关键参数是transform-origin: top center让封面从顶部翻下去符合「掀开红包盖」的直觉。cubic-bezier(.4,0,.2,1)是常用的缓出曲线起步快、收尾稳。金额的transition-delay设 0.2s等封面翻到一半再出现视觉上更连贯。注意rotateX在部分老安卓 WebView 上需要加-webkit-前缀构建工具一般会自动补但手写时别漏。3.3 Canvas 粒子系统的核心循环粒子是这套特效里最吃性能的部分。核心思路是维护一个粒子数组每帧更新位置和透明度画到 canvas 上生命周期结束就移除。下面是一个精简但可用的实现。// 初始化 canvas处理高清屏模糊问题 function initCanvas(canvas) { const dpr window.devicePixelRatio || 1; const rect canvas.getBoundingClientRect(); canvas.width rect.width * dpr; canvas.height rect.height * dpr; const ctx canvas.getContext(2d); ctx.scale(dpr, dpr); // 关键按 dpr 缩放否则高清屏发虚 return { ctx, w: rect.width, h: rect.height }; } // 生成一批粒子从中心向外飞散 function spawnParticles(count, w, h) { const cx w / 2, cy h / 2; const list []; for (let i 0; i count; i) { const angle Math.random() * Math.PI * 2; const speed 2 Math.random() * 4; list.push({ x: cx, y: cy, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed - 2, // 整体略微向上 life: 1, // 生命值从 1 衰减到 0 decay: 0.012 Math.random() * 0.01, size: 2 Math.random() * 3, color: Math.random() 0.5 ? #ffd24d : #ff5a5a }); } return list; } // 每帧更新并绘制 function tick(ctx, particles, w, h) { ctx.clearRect(0, 0, w, h); for (let i particles.length - 1; i 0; i--) { const p particles[i]; p.x p.vx; p.y p.vy; p.vy 0.12; // 重力让粒子有下落感 p.life - p.decay; if (p.life 0) { particles.splice(i, 1); continue; } ctx.globalAlpha p.life; ctx.fillStyle p.color; ctx.beginPath(); ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); ctx.fill(); } ctx.globalAlpha 1; }逻辑说明initCanvas里按devicePixelRatio放大画布再scale回来这是高清屏不发虚的关键漏了这步在 iPhone 上粒子边缘会糊。spawnParticles用极坐标随机方向vy额外减 2 让粒子整体偏上模拟「喷出来」的感觉。tick里重力0.12是调过的值太大粒子掉得太快没有飘散感太小会一直往上飘。decay控制粒子存活时长0.012 大约对应 80 帧、1.3 秒左右。参数怎么改粒子数量建议 80 到 150超过 200 在千元机上会明显掉帧speed范围 2 到 6 比较自然重力 0.1 到 0.15 之间微调。这些值没有标准答案拿真机试。3.4 触发时机与金额数据的对接动画再好看如果金额还没从接口回来就开跑用户会看到「0.00」闪一下再跳成真实金额体验很差。正确顺序是用户点击 → 请求接口 → 拿到金额 → 先渲染金额 → 再启动动画。async function openRedPacket() { const layer document.getElementById(redpacket-layer); const body layer.querySelector(.rp-body); const numberEl document.getElementById(rp-number); const btn document.getElementById(rp-open-btn); btn.disabled true; // 防重复点击 try { const res await fetch(/api/redpacket/draw, { method: POST }); const data await res.json(); if (!data || typeof data.amount ! number) throw new Error(bad data); // 先写金额再触发动画避免数字跳变 numberEl.textContent data.amount.toFixed(2); body.classList.add(opened); // 动画启动后再跑粒子 const canvas document.getElementById(rp-canvas); const { ctx, w, h } initCanvas(canvas); const particles spawnParticles(120, w, h); let rafId; (function loop() { tick(ctx, particles, w, h); if (particles.length) rafId requestAnimationFrame(loop); else cancelAnimationFrame(rafId); })(); } catch (e) { // 失败兜底提示重试不要卡在 loading btn.disabled false; alert(网络开小差了请重试); } }这段代码里几个细节值得说btn.disabled true是防连点的第一道闸接口慢的时候用户会狂点。金额先写 DOM 再classList.add(opened)保证动画开始时数字已经是对的。粒子循环用requestAnimationFrame而不是setInterval前者跟随屏幕刷新率后台标签页会自动暂停省电。失败分支一定要把按钮恢复可点否则用户卡死只能刷新页面。到这里一个能跑的最小版本就完整了。但能跑不等于线上稳下一章专门讲那些真机上才会暴露的坑。4. 真机上的避坑清单从掉帧到微信兼容4.1 低端安卓机粒子掉帧现象开发机上丝滑一到千元安卓机上粒子变成一格一格甚至整个页面卡住。原因粒子数量过多或者每帧都在创建新对象触发 GC。spawnParticles里如果每帧都push新粒子垃圾回收会周期性卡顿。解决把粒子数量压到 80 到 120粒子对象复用用对象池而不是每帧新建tick里避免splice频繁操作大数组可以标记死亡后统一清理。另外把 canvas 尺寸控制在红包区域大小不要铺满全屏绘制面积小一半性能就好一截。4.2 微信内置浏览器的合成层问题现象红包翻转动画在微信里出现闪烁、残影或者翻转时红包图片撕裂。原因微信 X5 内核部分安卓机型对transform和opacity的合成层处理不一致尤其是元素同时有border-radius和overflow: hidden时。解决给做动画的元素加will-change: transform或transform: translateZ(0)强制提升为合成层避免在动画元素上同时用box-shadow和border-radius如果还是闪把翻转改成scaleopacity的组合牺牲一点立体感换稳定。这个坑没有银弹只能真机测。4.3 金额接口慢导致的动画错位现象用户点了「开」红包先翻开了金额过了两秒才跳出来中间显示 0.00。原因动画触发和接口返回没有做顺序控制代码里先add(opened)再await fetch。解决严格按 3.4 的顺序先await拿到金额、写进 DOM再触发动画。如果接口确实慢加一个 loading 态比如按钮转圈让用户知道在请求而不是干等。接口超时设 3 秒超时走兜底文案。4.4 重复点击导致多次抽奖现象用户手快点了三下接口被调了三次可能多领红包或报错。原因只靠btn.disabled不够因为异步请求发出到按钮禁用之间有时间差快速点击仍可能穿透。解决在函数入口用一个同步的标志位锁比如if (isOpening) return; isOpening true;请求结束或失败再置回 false。disabled是给用户看的视觉反馈标志位才是真正的闸。4.5 高清屏 canvas 发虚现象iPhone 上粒子边缘模糊像蒙了一层雾。原因canvas 的width/height用的是 CSS 像素没有乘以devicePixelRatio。解决就是 3.3 里initCanvas的做法先按 dpr 放大画布尺寸再ctx.scale(dpr, dpr)。注意每次窗口 resize 或横竖屏切换都要重新初始化否则尺寸对不上。5. 进阶把开红包特效做成可配置、可复用的组件5.1 参数外置与主题切换线上活动经常要换皮春节红色、618 橙色、双十一紫色。如果每次都改源码维护成本很高。我的做法是把颜色、粒子数量、动画时长抽成配置对象组件初始化时传入。const RP_CONFIG { theme: { particleColors: [#ffd24d, #ff5a5a], amountColor: #ffd24d, maskOpacity: 0.55 }, particleCount: 120, gravity: 0.12, flipDuration: 400, // 与 CSS transition 时长保持一致 apiTimeout: 3000 }; function createRedPacket(userConfig {}) { const cfg { ...RP_CONFIG, ...userConfig }; // 把主题色写进 CSS 变量CSS 里用 var() 引用 const layer document.getElementById(redpacket-layer); layer.style.setProperty(--rp-amount-color, cfg.theme.amountColor); layer.style.setProperty(--rp-mask-opacity, cfg.theme.maskOpacity); return { cfg, open: () openRedPacket(cfg) }; }配置外置之后换主题只改一个对象不用碰动画逻辑。flipDuration要和 CSS 里的transition时长对齐否则金额出现的时机和封面翻转对不上。CSS 变量在微信内置浏览器里支持良好可以放心用。5.2 用真机验证代替「我觉得没问题」特效类需求最忌讳只在 Chrome DevTools 里看。DevTools 的模拟器性能远好于真机很多掉帧、闪烁问题在模拟器里根本复现不出来。我的习惯是准备三台设备一台 iPhone看高清屏和 Safari 表现、一台中端安卓看主流表现、一台千元机看性能下限。每次改完动画参数三台都过一遍。验证时重点看三个指标动画是否掉帧用 Chrome 远程调试看 FPS、金额出现时机是否和动画对齐、连续点击是否只触发一次。这三个过了基本就能上线。5.3 一个我踩过的教训早期做这类特效我总想把粒子调得越多越炫觉得「炸得越满越有氛围」。结果一次春节活动千元机上直接卡到用户以为页面死了投诉一堆。后来我把粒子数量从 300 砍到 100反而没人说不好看因为动画流畅本身就是体验的一部分。特效这东西克制比堆料难也更值钱。参数能调小就别调大真机跑一遍再决定。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?