简介:一套以兔年新春为主题的HTML互动页面源码,模拟微信红包点击开奖效果,适合前端入门学习者与需要快速搭建节日活动页的开发者参考。页面将HTML结构、CSS样式与JavaScript动画结合,通过按钮触发、随机金额生成及过渡动画还原拆红包体验,能直观学习DOM操作、事件监听与前端动效实现思路。
资源包共1771个文件,压缩后仅6.6MB,以1751个svg矢量图标为主,辅以css样式表、js交互脚本、woff2/ttf字体、gif动效等。svg提供丰富的兔年与节日元素,css负责布局、配色与红包开合过渡,js处理点击逻辑、随机金额与requestAnimationFrame动画,整体目录清晰,可直接浏览器运行调试。
目前已有6935人学习使用。下载后可获得完整可运行的兔年新年红包源码,并可从代码中提炼出事件绑定、CSS keyframes动画、帧率控制等通用技巧,便于迁移至其他H5活动或营销页面,对前端综合实践很有帮助。
1. HTML新年快乐2023的落地方式:一个兔年红包文件跑通全场
除夕前夜,我在群里收到一个链接,点开是红底金边的兔年贺卡,点击封面后整张卡翻转过来,屏幕底部升起烟花,祝福语一个字一个字往外蹦。那不是视频,也不是小程序,而是一个纯粹的 index.html。标题里提到的「HTML新年快乐2023源代码,兔年新年红包代码」,指的就是这种用一个文件搞定封面、兔子、开红包和烟花动效的制作方式。它不需要 Node,不需要构建工具,编辑器加浏览器就能改、能跑。适合刚开始接触 HTML 的人当作第一个完整作品来抄,也适合前端熟手在节日活动页里复用这套「拆红包」交互。下面我按一个红包页面从骨架到发布的真实顺序,把这份源码拆开讲透。
2. 兔年红包页面的 HTML 骨架:DOCTYPE、meta 与双面卡片分区
2.1 为什么这种节日页用原生 HTML 就够
节日红包页的生命周期很短,通常春节前三天上线、元宵后下线,访问峰值集中在除夕晚上。这种场景最忌讳引入构建链和重框架——一次npm install的时间,页面可能都已经发出去了。原生 HTML + CSS + 少量 JavaScript 的优势在于:没有任何外部依赖,不会出现 CDN 挂掉导致页面白屏;文件可以被直接传给同事,双击就能打开预览;部署时扔到任意静态服务器上就是完整站点。
标题里的「源代码」三个字,说明读者要的不是思路而是能直接落地的文件。我的做法是保持三个文件的结构:index.html管内容、style.css管外观、main.js管交互。这样改文案时不用翻样式,调动画时不用动结构。下文按这三个文件逐一说明参数和理由。
2.2 head 里的参数:HTML 能否正常渲染全看这里
很多搜「HTML新年快乐2023源代码」的人拿到代码后双击打开,发现页面是乱码,问题八成出在 head 里少了编码声明。下面是红包页 head 的完整写法:
<!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> <meta name="description" content="2023兔年新年快乐红包,点击拆开领取祝福"> <meta name="theme-color" content="#c7000b"> <title>2023 兔年红包 · 新年快乐</title> <link rel="stylesheet" href="style.css"> </head>charset="utf-8"解决中文乱码,这是 HTML 文件最先被解析的声明。viewport里的width=device-width让页面在手机上按设备宽度渲染,viewport-fit=cover是针对 iPhone 刘海屏的适配,缺了它红包底部可能被 Home 指示条遮住。theme-color控制 Android Chrome 地址栏颜色,取值和红包主色一致,视觉上更完整。description虽然在页面上不可见,但如果这个页面被部署到服务器上,它会成为搜索引擎结果里显示的摘要,也就是「源代码」类搜索词命中率的一个重要因素。
2.3 红包卡片的双面结构:封面与内页的 HTML 分区
红包的本质是一张「可翻转的卡片」,HTML 结构要按这个逻辑来分。封面是用户看到的第一面,内页是翻转后才出现的祝福内容。代码如下:
<body> <main class="envelope-scene"> <div class="redpacket" id="redpacket"> <section class="face" aria-label="红包封面"> <div class="face-head"> <span class="seal">2023</span> <h1 class="slogan">新年快乐</h1> </div> <!-- SVG 兔子内联放置,避免多余的图片请求 --> <svg id="rabbit-face" viewBox="0 0 160 120" role="img" aria-label="兔年吉祥兔子"> <!-- 兔子图形在第 3 章展开 --> </svg> <p class="hint">点击拆开红包</p> </section> <section class="inner" aria-label="红包内容"> <svg id="rabbit-inner" viewBox="0 0 160 120" role="img" aria-label="内页兔子"> <!-- 兔子图形在第 3 章展开 --> </svg> <p class="greeting" id="greeting"></p> <button type="button" id="again">再拆一次</button> </section> </div> </main> <canvas id="fireworks" aria-hidden="true"></canvas> <script src="main.js"></script> </body>.face和.inner是同一坐标系下的两个绝对定位层,后续用 CSS 的transform: rotateY(180deg)控制翻转。main.js放在 body 末尾,是因为脚本要操作的redpacket、greeting、fireworks此时已经在 DOM 里就绪,不需要再监听DOMContentLoaded。canvas用来承载烟花粒子,它盖不住红包的原因在于后续样式里会给它设置pointer-events: none。
代码里我把兔子做成了内联 SVG,而不是<img src="rabbit.png">。原因很实际:图片文件一旦丢失或路径写错,红包页面上就会出现一个破图图标,节日场景里这是致命伤。SVG 内联后,整个页面只有一个 HTML 文件加一个 CSS 加一个 JS,互相之间的依赖关系简单到了不会出错的程度。
3. 兔年视觉:内联 SVG 兔子、剪纸兔字与红包封面的 CSS 实现
3.1 用 SVG 画一只兔子:坐标、颜色与可调参数
兔年红包的核心视觉符号是兔子。网上有现成的兔年插画素材,但那些大多是 PNG 或 JPG,放大模糊、还牵扯版权。我一般会直接用内联 SVG 画一只极简兔子,代码在下面:
<svg viewBox="0 0 160 120" role="img" aria-label="兔年兔子"> <!-- 左耳 --> <ellipse cx="58" cy="26" rx="15" ry="36" fill="#f8e0d4" stroke="#e0a98f" stroke-width="3"/> <ellipse cx="58" cy="26" rx="6" ry="22" fill="#f4b8c8"/> <!-- 右耳 --> <ellipse cx="102" cy="26" rx="15" ry="36" fill="#f8e0d4" stroke="#e0a98f" stroke-width="3"/> <ellipse cx="102" cy="26" rx="6" ry="22" fill="#f4b8c8"/> <!-- 头 --> <ellipse cx="80" cy="72" rx="42" ry="36" fill="#fdf0e7" stroke="#e0a98f" stroke-width="3"/> <!-- 腮红 --> <circle cx="54" cy="78" r="9" fill="#ffb3a7" opacity="0.7"/> <circle cx="106" cy="78" r="9" fill="#ffb3a7" opacity="0.7"/> <!-- 眼睛 --> <circle cx="68" cy="68" r="4" fill="#4a2e1d"/> <circle cx="92" cy="68" r="4" fill="#4a2e1d"/> <!-- 鼻子 --> <ellipse cx="80" cy="80" rx="5" ry="3.5" fill="#e8836f"/> </svg>SVG 的viewBox="0 0 160 120"定义了一个 160×120 的逻辑坐标系,之后所有图形都按这个坐标画,页面缩放时不变形。兔子的结构分四层:耳朵用瘦长椭圆,脑袋用扁椭圆,腮红和眼睛用正圆。耳朵内部的粉色小椭圆是耳廓,fill="#f4b8c8"让兔子从侧面看有层次感。眼睛的#4a2e1d是深咖啡色而不是纯黑,目的是让整体视觉柔和一些,和红包的金色元素更搭。
如果想把兔子换成其他形象,只需要改这些椭圆和圆形的cx、cy、rx、ry数值。比如把耳朵的ry从 36 改成 20,兔子就变成了圆脸的猫咪;把腮红去掉,再加一对角,就成了生肖牛。这套参数化画法对不擅长设计的人是最友好的,调整数值即可出效果。
3.2 红包封面的 CSS:渐变、烫金与背景暗纹
封面要让用户第一眼认出「这是红包」,而不是一张普通的红卡片。红色要有层次,金色要能作为点缀但不喧宾夺主。下面是style.css中封面和整体布局的关键部分:
:root { --red-main: #c7000b; --red-dark: #8f0508; --red-light: #e83a3a; --gold: #f2c46d; --paper: #fdf6ec; } * { margin: 0; padding: 0; box-sizing: border-box; } html, body { height: 100%; } body { display: flex; align-items: center; justify-content: center; background: radial-gradient(circle at 50% 30%, #fdf0e7 0%, #f3d9b8 100%); font-family: "PingFang SC", "Microsoft YaHei", sans-serif; overflow: hidden; } .redpacket { position: relative; width: min(320px, 86vw); height: min(440px, 74vh); transform-style: preserve-3d; transition: transform 0.7s cubic-bezier(0.2, 0.7, 0.3, 1.1); cursor: pointer; } .face, .inner { position: absolute; inset: 0; border-radius: 18px; backface-visibility: hidden; overflow: hidden; } .face { background: repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.04) 0px, rgba(255, 255, 255, 0.04) 6px, transparent 6px, transparent 12px), linear-gradient(160deg, var(--red-light) 0%, var(--red-main) 45%, var(--red-dark) 100%); box-shadow: 0 12px 40px rgba(140, 20, 10, 0.35); display: flex; flex-direction: column; align-items: center; justify-content: space-between; padding: 28px 20px; } .seal { display: inline-block; padding: 6px 16px; border: 2px solid var(--gold); border-radius: 8px; color: var(--gold); font-size: 20px; font-weight: 700; letter-spacing: 4px; } .slogan { color: var(--gold); font-size: 34px; letter-spacing: 8px; text-shadow: 0 2px 6px rgba(0, 0, 0, 0.25); }CSS 变量统一放在:root,方便替换主题色。如果明年不是兔年而是龙年,你只需要把--gold换成更适合的青色,把--red-main调暗一点,整套页面的气质就变了。background上叠加了两层:repeating-linear-gradient生成斜向暗纹,模拟红包纸的编织纹理;linear-gradient从亮红到暗红过渡,让红包有立体感而不是一块纯色。
width: min(320px, 86vw)和height: min(440px, 74vh)是响应式的关键,小屏手机上红包宽度不超过视口的 86%,高度不超过视口的 74%,保证拆红包时内页文字不会被挤出屏幕。整套布局用flex居中,无论横屏竖屏红包始终在正中间。
3.3 剪纸「兔」字与印章效果的替代实现
生肖元素除了画兔子,还可以在红包封面上放一个剪纸风格的「兔」字。如果引入剪纸图片,又回到了图片依赖的问题上。我的做法是用 CSS 的text-shadow模拟剪纸的层叠感:
.cut-rabbit { font-size: 72px; font-weight: 900; color: var(--red-main); text-shadow: 2px 0 0 var(--gold), -2px 0 0 var(--gold), 0 2px 0 var(--gold), 0 -2px 0 var(--gold); } .rabbit-char { display: inline-block; background: var(--red-main); color: var(--gold); font-size: 26px; font-weight: 700; padding: 8px 14px; border-radius: 50%; border: 3px solid var(--gold); }.cut-rabbit的四个方向的阴影组合起来,相当于给文字描了一圈金边,视觉上接近剪纸的镂空感。.rabbit-char则是圆形印章,配合.face-head里已有的2023方块印章,形成「方块印章 + 圆形生肖章」的组合,这是传统红包封面上最常见的元素排布。
如果你觉得 CSS 剪纸字不够精细,可以把第 3.1 节的 SVG 兔子直接复制一份放在.cut-rabbit的位置,再把字号去掉。两种方案并存的好处是,你可以按当天手头的素材灵活切换,而不需要重新设计布局。
4. 兔年红包开合与烟花:CSS 3D 翻转、Web Audio 音效和 canvas 粒子
4.1 拆红包的翻转交互:两个 class 控制完整动效
红包从「未拆」到「已拆」的状态切换,核心是一段 CSS transition。.redpacket默认rotateY(0deg)展示封面,加上.open类后旋转 180 度露出内页。完整代码如下:
.redpacket.open { transform: rotateY(180deg); } .inner { transform: rotateY(180deg); background: linear-gradient(160deg, var(--paper) 0%, #f7e8d5 100%); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; padding: 24px; }.face和.inner都设置了backface-visibility: hidden,意思是「背面不可见」。封面默认朝前,内页预先旋转 180 度藏在后面;当父容器旋转 180 度后,封面转到背面隐藏,内页转到正面显示。transition的cubic-bezier(0.2, 0.7, 0.3, 1.1)是一个先慢后快再轻微回弹的缓动曲线,比默认的ease更有「拆红包」的手感。
注意.inner的gap: 16px,它控制内页里兔子、祝福文案、按钮三者之间的间距。如果以后要加一张二维码或一张合影,只需要改这个值就能调整整体疏密,不需要逐个元素去加margin。
4.2 用 Web Audio 合成新春音效:不需要任何音频文件
红包打开时最好有一声音效,但<audio src="ting.mp3">在本地文件协议下经常加载失败,而且大部分浏览器会拦截未经过用户交互就自动播放的音频。更稳的方案是用 Web Audio API 在浏览器里现场合成音效——零外部文件,完全绕开加载和跨域问题。
let audioStarted = false; function playChime() { if (audioStarted) return; audioStarted = true; const ctx = new AudioContext(); const master = ctx.createGain(); master.gain.value = 0.35; master.connect(ctx.destination); // 第一声:高音“叮” playTone(ctx, master, 1318.5, 0, 0.8, 'sine'); // 第二声:低音“当”,延迟 0.18 秒,模拟敲锣 playTone(ctx, master, 196, 0.18, 1.4, 'triangle'); } function playTone(ctx, dest, freq, delay, duration, type) { const osc = ctx.createOscillator(); const gain = ctx.createGain(); const t0 = ctx.currentTime + delay; osc.type = type; osc.frequency.value = freq; gain.gain.setValueAtTime(0.0001, t0); gain.gain.exponentialRampToValueAtTime(0.5, t0 + 0.02); gain.gain.exponentialRampToValueAtTime(0.0001, t0 + duration); osc.connect(gain); gain.connect(dest); osc.start(t0); osc.stop(t0 + duration + 0.1); }AudioContext必须在用户点击之后创建,所以我把playChime()放在点击事件里调用。createGain是一个音量控制节点,master.gain.value = 0.35把整体音量压到 35%,避免突然出声吓到用户。playTone里用exponentialRampToValueAtTime控制音量从极低升到峰值再衰减,模拟出「叮——」的自然余音,而不是简单粗暴的方波爆音。振荡器类型上,sine干净适合高音,triangle柔和适合低频锣声。
这套合成音效完全依赖浏览器能力,不会产生任何网络请求,因此在file://协议下双击打开 HTML 文件时依然能正常出声。
4.3 canvas 烟花粒子:参数与发射逻辑
烟花是本页最复杂的部分,但核心逻辑也就是「生成粒子、给初速度、每一帧更新位置、绘制、衰减」。代码如下:
const canvas = document.getElementById('fireworks'); const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; const particles = []; const COLORS = ['#f2c46d', '#e83a3a', '#fff2c8', '#ff8a5c']; function launchFirework() { const x = window.innerWidth * (0.2 + Math.random() * 0.6); const y = window.innerHeight * (0.15 + Math.random() * 0.4); const color = COLORS[Math.floor(Math.random() * COLORS.length)]; const count = 70 + Math.floor(Math.random() * 40); for (let i = 0; i < count; i++) { const angle = (Math.PI * 2 * i) / count; const speed = 1.5 + Math.random() * 2.5; particles.push({ x, y, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed, life: 1, decay: 0.012 + Math.random() * 0.015, color }); } } function drawParticles() { ctx.clearRect(0, 0, canvas.width, canvas.height); for (let i = particles.length - 1; i >= 0; i--) { const p = particles[i]; p.x += p.vx; p.y += p.vy; p.vy += 0.045; // 模拟重力 p.vx *= 0.985; // 空气阻力 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, 2.2, 0, Math.PI * 2); ctx.fill(); } ctx.globalAlpha = 1; requestAnimationFrame(drawParticles); } function startFireworks() { launchFirework(); setInterval(() => { if (document.hidden) return; launchFirework(); }, 900); }粒子属性里有几个值得调的参数。decay是粒子寿命衰减速度,0.012 到 0.027 之间的随机值让每个粒子的消失时间不一致,炸开后会有先后掉落的感觉。p.vy += 0.045是重力加速度,数值越大烟花越早往下坠,想要「高开花、慢下落」就调小到 0.02 左右。vx *= 0.985是空气阻力,让粒子慢慢减速而不是匀速飘走。document.hidden的检查是防止页面切到后台时定时器还在白白跑粒子,消耗手机电量。
4.4 开红包的完整事件时序
交互的时序决定了「拆红包」的节奏感。事件被整理如下:
| 时间点 | 触发动作 | 对应代码 |
|---|---|---|
| 0ms | 用户点击红包封面 | packet.classList.add('open') |
| 0ms | 播放合成音效 | playChime() |
| 180ms | 封面开始翻转,音效第二声响起 | CSS transition 执行中 |
| 900ms | 翻转完成,内页完全可见 | transition 结束 |
| 900ms | 触发第一朵烟花 | startFireworks() |
| 900ms 起 | 每 900ms 追加一朵烟花 | setInterval |
| 1200ms | 祝福文案逐字出现 | typeGreeting() |
const MSG = '兔年大吉,万事顺遂'; function tieUp() { const packet = document.getElementById('redpacket'); const againBtn = document.getElementById('again'); let isOpening = false; packet.addEventListener('click', () => { if (isOpening) return; isOpening = true; packet.classList.add('open'); playChime(); startFireworks(); typeGreeting(); }); againBtn.addEventListener('click', (e) => { e.stopPropagation(); packet.classList.remove('open'); document.getElementById('greeting').textContent = ''; isOpening = false; audioStarted = false; }); } function typeGreeting() { const el = document.getElementById('greeting'); let i = 0; const timer = setInterval(() => { el.textContent = MSG.slice(0, i + 1); i++; if (i >= MSG.length) clearInterval(timer); }, 90); } tieUp();isOpening标志位防止用户在翻转动画未完成时反复点击导致动画错乱。againBtn上的stopPropagation很关键:按钮本身在.inner里面,点击「再拆一次」如果冒泡到父容器,会立刻重新触发open,红包还没合上又被打开了。audioStarted = false的重置让「再拆一次」后音效可以重新播放,同时每次点击都会创建新的 AudioContext,旧的会被浏览器自动回收。
5. 把 HTML 源码真正发出去:本地验证、单文件合并与改动技巧
5.1 双击打不开或样式丢失时的排查顺序
搜「HTML新年快乐2023源代码」的人最常见的困境不是代码不对,而是拿到了代码却跑不起来。双击 index.html 出现乱码,检查 head 里有没有<meta charset="utf-8">;样式完全没有生效,说明style.css的路径不对,确认index.html里<link>的href和 css 文件在同一目录;点击红包没反应,打开浏览器控制台(F12)看main.js有没有报错,最常见的错误是document.getElementById拿不到元素——这通常是因为 script 标签写到了 head 里而 body 还没加载完。
cd rabbit-redpacket python3 -m http.server 8080本地起一个静态服务能解决file://协议下的部分兼容问题。上面代码在项目目录执行后,浏览器打开http://localhost:8080即可访问。不想装 Python 的可以用npx serve .,一样的效果。
5.2 合并成单个 HTML:把样式和脚本内联回去
如果想把页面作为单个 HTML 文件发给同事,直接把style.css的内容用<style>标签包起来放进 head,把main.js用<script>标签包起来放回 body 末尾即可。合并后代码结构如下:
<!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>2023 兔年红包</title> <style> /* 原 style.css 全部内容 */ </style> </head> <body> <!-- 原 body 结构 --> <script> // 原 main.js 全部内容 </script> </body> </html>内联之后文件体积会上升到 20KB 左右,对静态页面没有影响。但要留意:script 必须放在 body 最后,否则getElementById在 DOM 未就绪时拿不到元素。
5.3 改动技巧:年份、生肖与文案的替换入口
想让这个红包明年继续用,翻到第 3.2 节的:root,把--red-main从红色换成青色,把--gold从金色换成银色,一份「青蛇年红包」的主题就出来了。SVG 兔子的耳朵椭圆如果改成细长的等腰三角形,再拉长头部,就是蛇的抽象造型。祝福文案在main.js顶部的MSG常量里改,一次性把「兔年大吉」换成「龙行龘龘」这类当年热词即可。
如果要把红包发给别人并引导对方扫码领奖,把第 2.3 节.inner里的<p class="greeting">后面直接插入<img src="qrcode.png">即可,但注意图片要走相对路径,不要用本地绝对路径C:/Users/...,否则别人打开时图片必挂。
打开控制台确认 Network 面板里没有红色 404 记录,再在手机浏览器里跑一遍完整拆封流程,确认音效在「用户点击后」才初始化——这个红包页面就能稳定跑完整个打开过程了。
本文还有配套的精品资源,点击获取