兔年祝福页纯CSS实现:从DOM结构到动效交互全解析
2026/9/15 4:58:10 网站建设 项目流程

简介:2023兔个吉祥新年快乐代码资源包,专为前端初学者与节日祝福页面爱好者设计,用HTML+CSS+JS三件套快速实现一只可爱兔子的动态迎春效果。资源共4个文件,包含HTML结构文件、CSS样式文件、JavaScript交互脚本和一张效果图预览,压缩包仅378KB,轻量易用,下载后直接打开即可运行,无需复杂环境配置。整个视觉分为背景与兔子两部分,背景营造节日氛围,兔子则作为主体形象,融入“送走吉祥虎,迎来如意兔”的贺岁寓意。代码结构清晰,CSS负责造型与布局,JavaScript驱动动画与交互,HTML整合整体结构,适合用来学习基础动画、定位布局与节日氛围页面搭建。兔在中国文化中象征机智、谨慎与美好希望,页面设计巧妙呼应这一意涵,增添传统韵味。已有1507人学习下载,适合想在春节场景中快速获得源码参考或临摹练习的新手,也可作为贺卡或页面设计灵感。

1. 把“兔个吉祥”拆开看:背景层和兔子层的双轨实现

拿到这个新年快乐代码,2023兔个吉祥源码.zip,解压后只有四个文件:index.htmlstyle.cssscript.js和三张效果图。项目的直观做法是「一个页面 + 一只兔子」,但拆开看,它其实包含两条独立的渲染线:背景层负责新年氛围,兔子层负责主体视觉。背景用的是 CSS 渐变和点缀动画,兔子则完全由 DOM 元素加border-radius绘制,没有一张图片素材。这意味着就算不懂绘图软件,也能在半小时内从零复现一只带表情且可交互的兔子。对于想练手 CSS 图形学基础的前端初学者,以及需要快速出新年 H5 祝福页的活动运营开发者,这个资源比直接套模板更值得拆。

2. 从 DOM 结构到 CSS 兔子的绘制逻辑

2.1 先定画布:把页面当成坐标系

打开index.html能看到整体结构:一个容器#page包住背景层.bg,兔子.rabbit单独作为一层挂在容器下。这里的核心设计是让兔子层脱离文档流(position: absolute),否则页面一旦有滚动条,兔子就会跟着字体排布跑位。

常见做法是在 CSS 里给#page设置position: relative; overflow: hidden,再让.rabbit绝对定位在容器中央。如下面这段基础样式:

#page { position: relative; width: 100vw; height: 100vh; overflow: hidden; background: linear-gradient(180deg, #0f2027 0%, #203a43 50%, #2c5364 100%); } .rabbit { position: absolute; left: 50%; top: 55%; transform: translate(-50%, -50%); width: 320px; height: 400px; z-index: 2; }

这里的left: 50%transform: translate(-50%, -50%)是经典居中写法,不管视口多宽,兔子的几何中心都能贴近画布中心。z-index: 2保证兔子盖住背景层。背景渐变用三段深蓝到墨绿的过渡,营造除夕夜深邃的空中感,而不是刺眼的大红大绿,这样兔子身上的暖色才有对比度。

2.2 兔子本体:用 border-radius 拉出长耳朵和圆脸

接下来是重头戏:如何用 CSS 画兔子。这里没有任何 SVG 或图片,纯粹靠div的宽高加border-radius来塑形。第一个技术点是「耳朵的曲线不是普通椭圆,而是带一侧收窄的叶片形」。常见实现是设定宽 80px、高 200px 的块,然后给左上角和右上角分别设置不同圆角:border-radius: 50% 50% 40% 40% / 60% 60% 40% 40%。这是两套值组合,斜杠前控制水平方向,斜杠后控制垂直方向,可以做到上端圆润、下端略收。

<div class="rabbit"> <div class="ear left-ear"></div> <div class="ear right-ear"></div> <div class="head"> <div class="eye left-eye"></div> <div class="eye right-eye"></div> <div class="cheek left-cheek"></div> <div class="cheek right-cheek"></div> <div class="mouth"></div> </div> <div class="body"></div> </div>

对应 CSS 片段:

.ear { position: absolute; top: 0; width: 72px; height: 190px; background: #f7e8d0; border: 2px solid #e2c7a8; border-radius: 50% 50% 40% 40% / 60% 60% 40% 40%; } .left-ear { left: 52px; transform: rotate(-12deg); } .right-ear { right: 52px; transform: rotate(12deg); }

rotate让左右耳朵向外撇,比竖直更接近真实兔子的神态。真正让耳朵「立起来」的细节是内耳:在耳内部加一个更小的圆角块,颜色用浅粉#f3b8b0,同时利用position: absolute定位于耳朵中心,就形成两层结构。

头部用一整块div模拟:

.head { position: absolute; top: 120px; left: 50%; transform: translateX(-50%); width: 220px; height: 200px; background: #fdf1df; border-radius: 50% 50% 45% 45% / 55% 55% 45% 45%; }

注意这里头部圆角没有直接用 50% 拉成正圆,而是宽度大于高度、上下略扁。兔子脸是偏圆但下巴稍尖的结构,所以第二组圆角值让上半部分更饱满,下半部分略收。动手改的时候你会发现:只调这一行,脸就从「皮球」变成「兔脸」。

2.3 五官与表情:伪元素减少 DOM 数量

眼睛、腮红、嘴巴如果每个都写一个div,DOM 节点会多出一倍。源码里更可能的实现是直接用::before::after这两个伪元素,每个.eye只负责一个圆点,无需额外标签。

.eye { position: absolute; top: 70px; width: 16px; height: 22px; background: #2b2b2b; border-radius: 50%; } .left-eye { left: 55px; } .right-eye { right: 55px; } .eye::after { content: ""; position: absolute; top: 4px; left: 4px; width: 6px; height: 8px; background: #fff; border-radius: 50%; }

伪元素画的是眼睛上的高光点,叫“眼神光”。没有这个小点,兔子的眼睛是一团死黑;加上后就显得湿润、有生气。这里的关键是::after的定位基准是.eye自身,所以left: 4px相对眼珠位置偏移即可,不需要重新算全局坐标。

三瓣嘴也是一个隐藏细节:它是两个重叠的小圆角块拼成的倒 Y 形。常见做法是把一个 14px 见方的块旋转 45 度变成菱形,再用border-radius: 40%把底部拉圆,最后用overflow: hidden隐藏超出部分。这样只用一个元素就能模拟兔子嘴部的自然裂缝。

2.4 身体和四肢:放对层级比画得复杂更重要

身体是兔子视觉重心的下半部分。源码里身体应该是一个比头略宽但高度较小的块:

.body { position: absolute; top: 280px; left: 50%; transform: translateX(-50%); width: 260px; height: 180px; background: #fbefdc; border-radius: 50% 50% 45% 45% / 60% 60% 40% 40%; z-index: -1; }

这里注意z-index: -1的作用:身体作为背景层贴在头后面,才能形成头和身体的前后关系。如果没有这一层负层级,头会直接覆盖住身体顶部的弧线,看上去像一只没有脖子的圆球兔。改成z-index: -1后,头自然浮在身体之上,轮廓线才干净。

四肢在本项目里不一定显式存在,因为蹲坐姿势的兔子前肢可以和身体融为一体——这是这类资源常见的简化思路。真正让身体有「蹲坐感」的是底部弧线:用border-radius第三组值把下半部分收得比上半部分窄,模拟兔腿蜷起的形状。

3. 背景层的氛围营造:渐变、点缀素材与动效节奏

3.1 渐变夜空与暗部层次

背景的作用不是把页面铺满鲜艳颜色,而是通过暗色调衬托兔子的暖色。index.html里引入的style.css开头的渐变用了三段式深蓝黑,和单纯的#000不同,这种接近夜空的层次能给页面增加纵深感。我在复现时一般会再加一层半透明径向渐变模拟月光:

.bg::before { content: ""; position: absolute; top: 10%; left: 50%; transform: translateX(-50%); width: 500px; height: 500px; background: radial-gradient(circle, rgba(255, 245, 200, 0.25) 0%, transparent 60%); border-radius: 50%; z-index: 1; }

这一段在兔子背后打出一道柔和的月晕,让兔子的轮廓不被深色背景吃掉。radial-gradient的第一个参数是中心色,第二参数是边缘透明色,transparent 60%表示从中心到 60% 位置渐变结束,之后全透明,这样光晕才有软边而不是硬光圈。

背景除了渐变,还需要有可远可近的装饰元素。这类新年祝福页里最常见的是飘浮的星星、云朵或者金色圆点。这些元素如果用图片,会额外增加请求;用 CSS 画才是轻量方案。星星可以用一个扁平方块旋转 45 度模拟四角星,也可以用box-shadow多重投影一次性画出多个光点:

.stars { position: absolute; top: 20%; left: 10%; width: 4px; height: 4px; background: #fff; border-radius: 50%; box-shadow: 100px 40px 0 #fff, 240px 120px 0 rgba(255, 255, 255, 0.8), 420px 60px 0 #fff, 600px 200px 0 rgba(255, 255, 255, 0.6); animation: twinkle 3s infinite alternate; }

box-shadow在这里不是做阴影,而是借用多值投影生成一组分散的小点。这种方式不增加任何 DOM 节点,一个.stars元素就能铺出整片星野。参数里100px 40px表示第一个投影的 X、Y 偏移,0是模糊半径,#fff是颜色。换数值就能调整星位的疏密。

3.2 装饰文字的排版细节

页面上「兔个吉祥」这类文案通常放在背景层和兔子层之间,不能压在兔子正脸。源码里大概率用的是绝对定位 + 文字阴影实现描边感:

.title { position: absolute; top: 8%; left: 0; right: 0; text-align: center; font-size: clamp(28px, 6vw, 64px); font-weight: 900; color: #fcd45a; text-shadow: 0 2px 8px rgba(0, 0, 0, 0.4); letter-spacing: 0.12em; z-index: 3; }

clamp(28px, 6vw, 64px)是响应式字号的最佳实践:最小 28px,最大 64px,中间值随视口宽度(vw)平滑过渡。这样手机上不会因为 64px 的字把整行挤出屏幕,电脑上也不会小到缺乏视觉重量。配letter-spacing拉开字间距,能避免中文字挤成一团。

3.3 动效节奏表:别让每个元素同时动起来

背景层的动效如果全开,用户视线会被拽得乱七八糟。给一个我在复现这个资源时用的动效参数参考表,可直接抄:

元素动画类型时长方向/循环作用
星星层twinkle透明度3salternate往返制造闪烁感
月晕层无动画--静态背景,稳定视觉
云朵float水平移动18sinfinite常播增加纵向深度
兔子耳朵shake轻微旋转2.4sease-in-out延迟 1s 再启动,避免抢镜
标题文字无动画--保持文字可读性

注意这里耳朵的动画要加animation-delay,否则页面一加载兔子就开始摇头,用户还没看清整体构图就被动效干扰了。延迟一秒后的轻微摇耳既有「活过来」的感觉,又不打扰阅读标题。

@keyframes shake { 0%, 100% { transform: rotate(-6deg); } 50% { transform: rotate(6deg); } }

因为左右耳初始旋转角不同(rotate(-12deg)rotate(12deg)),所以shake动画里要使用绝对值而不是相对值。如果动画写成from { transform: rotate(0) },会先让耳朵瞬间归位再开始动,观感上会出现一次硬跳。

4. 打开 index.html 前要处理的三个关键细节

4.1 中文乱码与文档声明

解压这个 zip 后,直接双击index.html在浏览器里打开是最常见的验证方式。但如果index.html里缺少<meta charset="UTF-8">,页面上所有中文都会变成乱码。这是类似 zip 源码包最容易被忽略的问题。只要在 head 里加上这一行就能修正:

<meta charset="UTF-8">

建议同时加上viewport设置,否则在手机浏览器里页面会按 980px 宽度渲染,整体缩小到看不清:

<meta name="viewport" content="width=device-width, initial-scale=1.0">

这段代码让页面宽度跟设备宽度一致,是移动端预览的关键。缺了它,src 里再怎么调vw也救不回缩放问题。

4.2transformposition: absolute的层级坑

在改源码时会遇到一种典型问题:兔子的耳朵明明设置了z-index: 2,但头部还是盖住了耳朵底部。原因在于z-index不是全局生效的,它只在同一个层叠上下文里比较。当一个元素设置了positionz-index时,会创建独立的层叠上下文,内部的子元素 z-index 再高也只是相对于父级内部比较。解决办法是让头部和耳朵都放在同一个.rabbit容器下,并且容器本身设置较低的z-index,再由耳朵和头部内部的 z-index 决定互相覆盖。

.rabbit { z-index: 2; } .ear { z-index: 1; } .head { z-index: 3; }

这里必须保证.rabbit没有创建额外层叠上下文,所以不要在.rabbit上设置transform之外再叠加opacityfilter,那些属性也会触发新的上下文。

4.3 动效卡顿与filter属性滥用

背景星星闪烁如果用filter: brightness()实现,在低端安卓机上会掉帧明显。因为filter会触发整个元素的离屏渲染,每一帧都要重新计算像素。轻量替代是只改动opacity,它走合成器线程,GPU 参与,不需要重排布局。下面是这个资源里更推荐的闪烁写法:

function twinkle(el) { const stars = document.querySelectorAll(el); stars.forEach((star, i) => { star.style.animationDelay = `${i * 0.3}s`; }); }

这段 JS 配合 CSS 中animation: fadeStar 2s infinite alternate,让每颗星星的闪烁时间错开 0.3 秒,就形成波次闪烁。animationDelay写在 CSS 里也有效,但用 JS 动态设置的好处是星星数量多变时不用手写一串nth-child选择器。

4.4 常见问题定位表

下面这组判断逻辑是根据这个资源的项目结构整理的,能覆盖大多数解压后打不开的报错场景:

现象定位方向排查方式
双击后静态页面空白HTML 中 CSS 文件路径不对检查<link rel="stylesheet" href="style.css">
背景有颜色但兔子消失兔子容器宽高为 0 或被overflow裁切.rabbit上临时加border: 1px solid red
汉字显示为方块或问号缺少charset声明确认<meta charset="UTF-8">在 head 最前面
移动端页面上兔子跑位视口缩放未禁用viewport设置
耳朵动画开始前出现一次位置跳动关键帧中transform写漏旋转角确保关键帧里带上初始rotate
点击页面无交互反应script.js加载顺序错误<script src="script.js"></script>放 body 末尾

排查第一项时尤其注意:如果文件夹里同时存在style.csscss/style.css,浏览器会优先按 HTML 里的路径找,路径不匹配时不会报错,只会全部样式丢失。

5. script.js 里值得抄的三个微交互写法

5.1 点兔子换祝福语:用数组替代多次 DOM 查询

页面上「兔个吉祥」如果只是静态文字就显得单薄。这个资源里script.js最常见的交互便是点击兔子切换不同的祝福文案。与其写四五个if/else,不如把文案放进数组,再用一个索引变量循环切换。注意,此资源下标从 0 开始计数有利于取模运算:

const messages = [ "兔年送只兔,全家乐悠悠", "送走吉祥虎,迎来如意兔", "前兔似锦,大展宏兔", "2023 兔个吉祥" ]; const title = document.querySelector(".title"); let index = 0; document.querySelector(".rabbit").addEventListener("click", () => { index = (index + 1) % messages.length; title.textContent = messages[index]; });

这里(index + 1) % messages.length是循环数组的标准写法,%取余让索引到末尾后自动回 0,省去手动判断越界的代码。用textContent而不是innerHTML还有一个实际好处:当文案里出现<&这类字符时,innerHTML会把它解析成标签或实体,textContent则保证原样输出。

5.2 跟随兔子的鼠标点缀

很多类似源码里会做「鼠标移动时兔子眼睛跟着转」的效果。但直接用mousemove更新眼睛位置会造成高频重排,导致 CPU 占用飙升。更经济的方式是监听mousemove时只更新 Custom Properties(CSS 变量),让 transform 的运算发生在合成阶段:

const rabbit = document.querySelector(".rabbit"); rabbit.addEventListener("mousemove", (e) => { const rect = rabbit.getBoundingClientRect(); const x = e.clientX - rect.left - rect.width / 2; const y = e.clientY - rect.top - rect.height / 2; rabbit.style.setProperty("--eye-x", `${x / 20}px`); rabbit.style.setProperty("--eye-y", `${y / 20}px`); });

CSS 侧配合:

.eye { transform: translate(var(--eye-x, 0), var(--eye-y, 0)); }

setProperty到 CSS 变量上,每帧只有变量值变化,不触发 DOM 重排。变量默认值0写在var()第二个参数里,即使 JS 没有执行也不会报错。除以 20 是为了控制眼球位移幅度,数值越大移动越迟钝,适合营造眼球轻微转动的真实感。

5.3requestAnimationFrame控制背景粒子

如果要在背景里加飘动的细雪或金色粒子,setInterval每 16ms 跑一次容易在主线程忙碌时积压任务。requestAnimationFrame是更合适的方案,它能跟屏幕刷新率同步,页面切到后台时自动暂停,避免无谓消耗:

function createParticle() { const p = document.createElement("div"); p.className = "particle"; p.style.left = Math.random() * 100 + "vw"; p.style.animationDuration = Math.random() * 4 + 4 + "s"; document.querySelector(".bg").appendChild(p); setTimeout(() => p.remove(), 8000); } setInterval(createParticle, 600);

Math.random() * 100 + "vw"把粒子横向位置随机铺满屏幕,animationDuration用 4 到 8 秒之间的随机时长让粒子下落速度有差异。setTimeoutp.remove()是防止粒子无限堆积导致 DOM 节点泄漏,这个细节在低配手机上影响特别明显。由于每个粒子都是独立元素,数量一多就会卡;这里的setInterval周期是 600ms 而非 100ms,就是刻意控制页面同时存在的粒子数不超过 20 个。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询