news 2026/9/9 10:58:15

用HTML、CSS与JavaScript打造三只宠物的互动展示小站

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用HTML、CSS与JavaScript打造三只宠物的互动展示小站

看到这个标题,很多读者可能会好奇:文章是打算晒三只小动物的日常吗?其实不是。这是我在整理前端练习素材时冒出来的一个灵感——与其拿千篇一律的“Todo List”或“登录表单”练手,不如做一个充满童趣的宠物展示小网站,让三只性格迥异的“小家伙”作为主角,串起 HTML、CSS、JavaScript 的核心知识点。

这篇实战教程会从一个纯静态页面开始,逐步实现宠物卡片展示、互动按钮(投喂、抚摸、玩耍)、心情与精力状态管理、本地存储、夜间模式切换等常见功能。整个过程不依赖任何框架,不需要安装 Node.js,不涉及后端服务,只要有一台电脑、一个浏览器和任意一个文本编辑器就能完成。读完并动手实践后,你将掌握:

  • HTML 语义化标签的合理组织方式;
  • CSS Flex / Grid 布局、动画和主题切换的写法;
  • JavaScript 对象数组、事件监听、DOM 渲染和 localStorage 持久化;
  • 一个完整小项目从设计到运行的思路闭环。

如果你刚学完前端基础,正愁没有合适的项目练手,或者想快速做一个可爱风格的展示页面送给朋友,这篇文章可以直接照着抄。

1. 项目背景与功能规划

1.1 这个项目解决什么问题

很多初学前端的人会遇到一个尴尬阶段:语法都看懂了,但一打开编辑器不知道写什么。做计算器太枯燥,做后台管理模板又涉及太多框架知识。而“宠物展示页”这类项目刚好处于“简单但不简陋”的区间——它足够轻量,能让初学者把注意力集中在三件核心事情上:

  1. 用语义化 HTML 搭建页面骨架;
  2. 用 CSS 控制视觉表现和布局;
  3. 用 JavaScript 让页面“动”起来。

同时,“三只小宠物”的设定天然具备叙事性,写代码的过程更像是给三只虚拟角色做成长记录页。看着页面逐渐成型、按钮产生响应、状态被保存,这种成就感是单纯的语法练习无法带来的。

1.2 功能模块拆解

在动手写代码之前,先明确页面需要具备哪些能力。我不建议一上来就写,先花两分钟做拆解,后面代码组织会清晰很多。

模块功能说明涉及技术点
宠物信息展示展示三只宠物的头像、名字、年龄、性格标签和简介HTML 结构、CSS 卡片布局
状态面板每只宠物拥有心情值、精力值两个数值属性JavaScript 对象、动态渲染
互动按钮投喂、抚摸、玩耍三种操作,影响数值变化事件监听、函数封装
数据持久化刷新页面后状态不被重置localStorage
主题切换白天/夜间模式一键切换CSS 变量、class 切换
响应式布局在手机和电脑上都能正常浏览Flex / Grid 媒体查询

1.3 三只“小家伙”的人设

为了让项目有辨识度,我给三只宠物设定了简单的人设。你在复制代码时可以随意改名、换性格标签。

  • 橘子:一只喜欢晒太阳的小橘猫,性格慵懒,最爱小鱼干,心情值掉得最慢;
  • 布丁:一只精力旺盛的小柯基,热爱奔跑,玩耍时精力消耗最快;
  • 棉花糖:一只毛茸茸的小白兔,胆子小,被抚摸时心情值涨得最快。

这三个人设不仅让页面有温度,还会影响后续 JavaScript 参数设计。比如每只宠物的数值增减幅度可以不同,这正好引出“配置数据驱动渲染”的思想。

2. 环境准备与项目结构

2.1 开发环境说明

本项目介绍的是纯前端静态页面,对运行环境的要求非常低。你可以选择任意一种方式创建项目:

  • 使用 VS Code 等编辑器新建文件夹,手动创建index.htmlstyle.cssscript.js三个文件;
  • 直接复制下面的完整代码,在本地保存后双击index.html用浏览器打开;
  • 如果你更习惯在线编辑,也可以使用 CodePen、JSFiddle 等在线工具直接粘贴运行。

版本方面不需要过度纠结。示例代码使用的是 ES6 语法(constlet、模板字符串、箭头函数),只要浏览器版本不是特别老旧即可。建议使用 Chrome、Edge 或 Firefox 最新版测试。

2.2 项目目录结构

推荐创建如下目录:

cute-three-pets/ ├── index.html ├── style.css └── script.js

其中:

  • index.html负责页面结构,是项目的入口;
  • style.css负责全部样式,包括布局、颜色、动画和响应式规则;
  • script.js负责宠物数据、互动逻辑、存储读写和主题切换。

把结构、样式、行为分离是前端开发的基本规范。即使以后项目变大,这种分层也会让你的代码更容易维护。

3. 核心设计思路拆解

3.1 用 JavaScript 对象描述宠物数据

整个项目的核心是“宠物数据”。如果直接把三只宠物写死在 HTML 里,那么互动逻辑和渲染逻辑都会变得很僵硬。更合理的做法是:先用 JavaScript 对象数组保存宠物信息,然后动态生成卡片。

const pets = [ { id: 'cat', name: '橘子', avatar: '🐱', type: '小橘猫', age: '2岁', desc: '喜欢晒太阳,最爱小鱼干。', tags: ['慵懒', '黏人'], mood: 80, energy: 70, moodChange: { feed: 5, stroke: 8, play: -10 }, energyChange: { feed: 6, stroke: 2, play: -15 } }, { id: 'dog', name: '布丁', avatar: '🐶', type: '小柯基', age: '1岁半', desc: '精力旺盛,每天都要出门跑三圈。', tags: ['活泼', '贪玩'], mood: 70, energy: 90, moodChange: { feed: 4, stroke: 5, play: 12 }, energyChange: { feed: 5, stroke: 1, play: -20 } }, { id: 'rabbit', name: '棉花糖', avatar: '🐰', type: '小白兔', age: '8个月', desc: '胆子很小,但熟悉之后特别粘人。', tags: ['温柔', '胆小'], mood: 60, energy: 55, moodChange: { feed: 5, stroke: 12, play: -6 }, energyChange: { feed: 4, stroke: 3, play: -8 } } ];

这段代码中:

  • moodenergy是每只宠物的当前状态值,范围建议控制在 0 到 100;
  • moodChangeenergyChange是三种互动操作对数值的影响幅度;
  • feed表示投喂,stroke表示抚摸,play表示玩耍。

把参数设计成这样,后续新增一只宠物只需要往数组里追加一个对象,不需要修改渲染逻辑,这其实就是“数据驱动视图”的雏形。

3.2 渲染函数:让数据和 UI 保持同步

修改数据之后必须让页面重新更新,这个操作我们称之为“渲染”。常规做法是写一个renderCards函数,循环遍历pets数组,生成对应的卡片 HTML 字符串,然后插入到页面容器中。

function renderCards() { const container = document.getElementById('pet-list'); container.innerHTML = pets.map(pet => { return ` <article class="pet-card">document.getElementById('pet-list').addEventListener('click', function (e) { const btn = e.target.closest('.btn'); if (!btn) return; const card = btn.closest('.pet-card'); const petId = card.dataset.id; const action = btn.dataset.action; handleAction(petId, action); });

e.target.closest('.btn')会向上查找最近的.btn元素。如果点击位置不在按钮上,closest返回null,这时直接return即可。这种写法在列表项很多、需要频繁增删元素的场景中非常实用。

3.4 数值变化与边界处理

handleAction函数负责更新宠物数据。这里要注意一个问题:心情值和精力值不能无限上涨或下跌,应该被限制在 0 到 100 之间。

function handleAction(petId, action) { const pet = pets.find(p => p.id === petId); if (!pet) return; pet.mood += pet.moodChange[action]; pet.energy += pet.energyChange[action]; pet.mood = Math.max(0, Math.min(100, pet.mood)); pet.energy = Math.max(0, Math.min(100, pet.energy)); saveData(); renderCards(); }

Math.max(0, Math.min(100, value))是一种非常常见的数值裁剪写法:先限制上限 100,再限制下限 0。如果你不这样做,投喂多次后数值可能变成 120,精力也有可能变成负数。

saveData()负责把宠物数组写入 localStorage。这里需要先把 JavaScript 对象转换成 JSON 字符串,因为 localStorage 只能存储字符串。

function saveData() { localStorage.setItem('cute-three-pets', JSON.stringify(pets)); } function loadData() { const saved = localStorage.getItem('cute-three-pets'); if (saved) { const parsed = JSON.parse(saved); if (Array.isArray(parsed) && parsed.length) { parsed.forEach((item, index) => { pets[index].mood = item.mood; pets[index].energy = item.energy; }); } } }

loadData中做了一层防御性判断,因为 localStorage 里的内容可能被用户手动修改或删除,不校验就直接使用,可能让页面崩溃。这个习惯在真实项目中很重要。

4. 完整实战案例:三只宠物的互动小站

4.1 创建项目结构

第一步,新建一个文件夹,命名为cute-three-pets。在文件夹内创建三个文件:

  • index.html
  • style.css
  • script.js

如果你使用的是 VS Code,可以直接在编辑器里创建这些文件,然后安装一个名为Live Server的插件,右键index.html选择“Open with Live Server”,就能在浏览器中实时预览效果。如果没有插件,直接双击index.html也能运行,只是修改代码后需要手动刷新浏览器。

4.2 编写 HTML 骨架

index.html是所有内容的载体。页面整体分为三块区域:顶部导航、宠物卡片主舞台、底部页脚。我把首页展示区与相册趣味区都放在了主容器内,让页面内容更丰富。

<!-- 文件路径:index.html --> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>可爱的三小只~</title> <link rel="stylesheet" href="style.css"> </head> <body> <header class="site-header"> <h1>🐾 可爱的三小只~</h1> <p>这是一座由 HTML、CSS 和 JavaScript 搭建的宠物小站</p> <button id="theme-toggle" class="theme-btn">🌙 夜间模式</button> </header> <main class="container"> <section class="intro"> <h2>欢迎来到萌宠之家</h2> <p>点击每只宠物卡片上的按钮,投喂、抚摸或陪它玩耍,看看它们的情绪和精力会发生什么变化。所有状态都会保存在你的浏览器中,刷新页面也不会消失。</p> </section> <section id="pet-list" class="pet-list"> <!-- 宠物卡片由 JavaScript 动态生成 --> </section> <section class="gallery"> <h2>生活时光</h2> <div class="gallery-grid"> <div class="gallery-item">🌞 午后晒太阳</div> <div class="gallery-item">🏃 草地狂奔</div> <div class="gallery-item">🥕 偷吃胡萝卜</div> </div> </section> </main> <footer class="site-footer"> <p>Made with ❤️ by HTML / CSS / JavaScript</p> </footer> <script src="script.js"></script> </body> </html>

这段 HTML 中有几个值得注意的细节:

  • <meta name="viewport" ...>是移动端适配的基础配置,没有它,手机浏览器会自动缩放页面;
  • <script>标签放在<body>末尾,是为了保证脚本执行时,页面中的 DOM 元素已经全部加载完成;
  • <section id="pet-list" class="pet-list">是一个空容器,里面的卡片内容将由 JavaScript 动态插入。

4.3 编写 CSS 样式

CSS 是整个页面“可爱感”的来源。我会通过 CSS 变量定义配色,方便后续切换夜间模式。核心样式包括:

  • 卡片布局使用 Grid,在宽屏下三列排列,窄屏下自动降为单列;
  • 状态条采用两层嵌套结构,外层是灰色背景,内层宽度由 JavaScript 控制;
  • 按钮使用圆角与 hover 效果;
  • 夜间模式通过给<body>添加night类实现。
/* 文件路径:style.css */ :root { --bg-color: #fff8f0; --text-color: #4a3f35; --card-bg: #ffffff; --card-shadow: rgba(255, 180, 130, 0.25); --primary: #ff9a76; --primary-light: #ffd4c2; --bar-bg: #f0e6dd; --tag-bg: #fff0e8; } body.night { --bg-color: #2b2b36; --text-color: #ece5dd; --card-bg: #3a3a48; --card-shadow: rgba(0, 0, 0, 0.3); --bar-bg: #55556a; --tag-bg: #494958; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; background-color: var(--bg-color); color: var(--text-color); transition: background-color 0.4s ease, color 0.4s ease; line-height: 1.6; } .site-header { text-align: center; padding: 40px 20px 20px; } .site-header h1 { font-size: 32px; } .theme-btn { margin-top: 16px; padding: 8px 18px; border: none; border-radius: 20px; background-color: var(--primary); color: #fff; font-size: 14px; cursor: pointer; transition: opacity 0.3s; } .theme-btn:hover { opacity: 0.9; } .container { max-width: 1080px; margin: 0 auto; padding: 20px; } .intro { text-align: center; margin-bottom: 32px; } .intro h2 { font-size: 24px; margin-bottom: 8px; } .intro p { max-width: 600px; margin: 0 auto; color: var(--text-color); opacity: 0.8; } .pet-list { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; margin-bottom: 40px; } .pet-card { background-color: var(--card-bg); border-radius: 20px; padding: 24px; box-shadow: 0 10px 24px var(--card-shadow); text-align: center; transition: transform 0.3s ease, box-shadow 0.3s ease; } .pet-card:hover { transform: translateY(-4px); } .pet-avatar { font-size: 72px; line-height: 1; margin-bottom: 12px; } .pet-type { display: block; margin: 4px 0 10px; font-size: 14px; opacity: 0.7; } .pet-tags { margin-bottom: 12px; } .tag { display: inline-block; background-color: var(--tag-bg); color: var(--primary); border-radius: 12px; padding: 4px 12px; font-size: 12px; margin: 0 4px 6px; } .pet-desc { font-size: 14px; opacity: 0.85; margin-bottom: 16px; min-height: 40px; } .status-bar { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; font-size: 13px; } .bar { flex: 1; height: 10px; background-color: var(--bar-bg); border-radius: 6px; overflow: hidden; } .bar-inner { height: 100%; border-radius: 6px; transition: width 0.4s ease; } .bar-inner.mood { background: linear-gradient(90deg, #ffb199, #ff8259); } .bar-inner.energy { background: linear-gradient(90deg, #fddb92, #f5a64c); } .actions { display: flex; justify-content: center; gap: 10px; margin-top: 16px; } .btn { padding: 8px 14px; border: none; border-radius: 16px; background-color: var(--primary-light); color: var(--text-color); font-size: 13px; cursor: pointer; transition: background-color 0.3s, transform 0.2s; } .btn:hover { background-color: var(--primary); color: #fff; transform: scale(1.05); } .gallery { margin: 40px 0; } .gallery h2 { text-align: center; margin-bottom: 20px; } .gallery-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; } .gallery-item { background-color: var(--card-bg); border-radius: 16px; padding: 28px 12px; text-align: center; font-size: 18px; box-shadow: 0 6px 16px var(--card-shadow); } .site-footer { text-align: center; padding: 30px 20px; opacity: 0.7; font-size: 14px; } @media (max-width: 768px) { .pet-list { grid-template-columns: 1fr; } .gallery-grid { grid-template-columns: 1fr; } }

这段 CSS 中值得学习的地方是 CSS 变量(Custom Properties)的使用。:root中定义的变量在全局可用,body.night中重新定义同名变量后,所有引用var(--bg-color)var(--text-color)的地方都会自动使用新值。这种机制比“写死颜色再逐个覆盖”要干净得多。

4.4 编写 JavaScript 逻辑

script.js承担三类职责:维护数据、处理交互、保存状态。完整代码如下:

// 文件路径:script.js const pets = [ { id: 'cat', name: '橘子', avatar: '🐱', type: '小橘猫', age: '2岁', desc: '喜欢晒太阳,最爱小鱼干。', tags: ['慵懒', '黏人'], mood: 80, energy: 70, moodChange: { feed: 5, stroke: 8, play: -10 }, energyChange: { feed: 6, stroke: 2, play: -15 } }, { id: 'dog', name: '布丁', avatar: '🐶', type: '小柯基', age: '1岁半', desc: '精力旺盛,每天都要出门跑三圈。', tags: ['活泼', '贪玩'], mood: 70, energy: 90, moodChange: { feed: 4, stroke: 5, play: 12 }, energyChange: { feed: 5, stroke: 1, play: -20 } }, { id: 'rabbit', name: '棉花糖', avatar: '🐰', type: '小白兔', age: '8个月', desc: '胆子很小,但熟悉之后特别粘人。', tags: ['温柔', '胆小'], mood: 60, energy: 55, moodChange: { feed: 5, stroke: 12, play: -6 }, energyChange: { feed: 4, stroke: 3, play: -8 } } ]; function saveData() { localStorage.setItem('cute-three-pets', JSON.stringify(pets)); } function loadData() { const saved = localStorage.getItem('cute-three-pets'); if (saved) { try { const parsed = JSON.parse(saved); if (Array.isArray(parsed) && parsed.length === pets.length) { parsed.forEach((item, index) => { pets[index].mood = item.mood; pets[index].energy = item.energy; }); } } catch (error) { console.warn('本地数据解析失败,使用默认数据', error); } } } function renderCards() { const container = document.getElementById('pet-list'); container.innerHTML = pets.map(pet => { return ` <article class="pet-card">const MAX_VALUE = 100; const MIN_VALUE = 0;

把最大最小值抽成常量,比在代码里直接写1000更容易维护。当业务规则变化时,只需要修改常量定义。

6.3 交互反馈要即时

按钮点击后,数值的变动不应该只在数字文本上体现,还要通过颜色、动画、提示文案等方式强化反馈。比如:

  • 心情值增加时,状态条颜色由浅变深;
  • 数值变化时,数字增加一个小幅缩放的动画;
  • 投喂操作后,在卡片角落弹出“小鱼干 +1”的小提示。

这些细节会让页面更有生命力,但实现成本并不高。

6.4 善用浏览器存储,但要注意容量和权限

localStorage 的容量一般是 5MB 左右,适合保存小体积的配置项和状态数据,不适合存储图片、大文本。同时,localStorage 只能存储字符串,对象的读写必须经过JSON.stringifyJSON.parse。另外,用户如果开启隐私模式或禁用站点数据,localStorage 可能不可用,所以需要提前用try...catch包住读取操作。

6.5 CSS 命名规范

示例中使用了类似.pet-card.status-bar.bar-inner的命名方式。如果你接手更大的项目,可以尝试 BEM 命名法(Block Element Modifier),例如.pet-card__avatar.pet-card__action。命名规范的主要价值是让样式结构更清晰,方便团队协作和维护。

6.6 兼容性与前缀

现代浏览器对 CSS 变量、Flex、Grid 的支持度已经很高,但如果你需要兼容老旧浏览器,可能需要考虑:

  • display: grid提供 flex 回退布局;
  • 为渐变、border-radius等属性添加-webkit-前缀;
  • 使用 autoprefixer 等构建工具自动处理。

不过在本项目中,面向现代浏览器即可,不需要过度处理兼容性。

7. 扩展方向与总结

到这里,一个完整的“可爱的三小只~”互动页面已经可以做出来了。它包含了一个前端页面从“设计数据结构”到“渲染视图”,再到“处理交互”和“持久化存储”的完整链条。通过这个项目,你会更深刻地理解下面几个核心概念:

  • 数据驱动渲染:页面内容是数据的映射;
  • 事件委托:动态元素的统一事件处理思路;
  • 状态管理:数值变化后如何同步视图并持久化;
  • 主题切换:CSS 变量在换肤场景中的实际应用。

如果你希望继续深入,这里还有几个明确的扩展方向。

第一个方向是接入后端。宠物数据不再硬编码在pets数组中,而是通过fetch('/api/pets')从后端接口获取。这一步会让你接触到异步编程、接口设计、错误处理等内容,对巩固 JavaScript 基础很有帮助。

第二个方向是引入前端框架。把这个静态页面迁移到 Vue 或 React 中,你会发现之前手动renderCards、手动维护innerHTML的工作,框架都帮你完成了。对比起来,你会更容易理解“响应式”和“组件化”的意义。

第三个方向是给项目增加更多玩法。例如:

  • 为每只宠物增加“喂食记录”和“成长值”,连续喂养 7 天解锁新头像;
  • 增加宠物对话气泡,不同心情状态输出不同文案;
  • 使用 Canvas 或第三方动画库,让宠物做出跑步、飞翔等动作。

技术学习的路径从来不是靠背语法,而是靠一个又一个可运行、可展示、可分享的小作品往前走。“可爱的三小只~”就是一个很好的起点。如果你在实际搭建过程中遇到了其他问题,欢迎对照上面的排查表逐项检查,也可以重新阅读对应章节,理解每一步为什么这样设计。代码和笔记都放在手边,动手多敲几遍,收获会更大。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 10:57:56

AI辅助开发学习小网页:从提示词到静态页面完整实践

要用 AI 做学习工网页&#xff0c;最容易低估的反而是需求拆得不够细。一开始我以为不过是把单词卡、口算题丢给 AI&#xff0c;让它吐一个 HTML 页面出来就算完事。真跑过几轮之后发现&#xff0c;一套稳定的做法要从“需求描述 → 提示词 → 代码生成 → 本地验证 → 数据批量…

作者头像 李华
网站建设 2026/9/8 5:14:57

爱奇艺C方向笔试题深度解析:从指针内存到数据结构

作为2019年参加过秋招的人&#xff0c;爱奇艺这套C方向笔试题&#xff08;A&#xff09;我印象还挺深的。那时候刷题刷到头秃&#xff0c;拿到卷子一看&#xff0c;倒不是难到无从下手&#xff0c;而是很多题看着眼熟&#xff0c;真要手写代码或者辨析概念&#xff0c;却处处是…

作者头像 李华
网站建设 2026/9/9 10:55:48

浏览器本地批量视频编辑:技术原理与最小Demo实现

当第一次看到 Apollodorus Video 这个项目时&#xff0c;最值得关注的点不是它做了多少花哨的滤镜&#xff0c;而是它把“批量视频编辑”和“浏览器本地运行”这两件事放在了一起。传统视频批处理要么依赖安装客户端软件&#xff0c;要么把素材上传到云端转码服务&#xff1b;而…

作者头像 李华
网站建设 2026/9/5 2:55:17

通信电子考研专业课真题刷题方法:信号与系统、通信原理高分攻略

备考通信电子类研究生&#xff0c;很多同学在专业课复习上容易走两个极端&#xff1a;要么一头扎进教材反复看&#xff0c;要么疯狂刷题但抓不住重点。作为一个过来人&#xff0c;我的感受是&#xff0c;教材决定了你的知识下限&#xff0c;而真题决定了你的应试上限。尤其对于…

作者头像 李华
网站建设 2026/9/6 8:46:36

用 Tauri 构建 coding-agent 驱动的桌面放置游戏实战

之前在浏览 Show HN 的时候&#xff0c;我刷到一个很有意思的项目&#xff1a;An idle desktop incremental game driven by coding-agent。直译过来就是“一款由 coding-agent 驱动的桌面放置类增量游戏”。放置游戏大家应该都不陌生&#xff0c;coding-agent 在最近两年也是热…

作者头像 李华
网站建设 2026/9/5 17:21:49

开关柜接头红外过热检测:千张级VOC数据集拆解与实战价值分析

简介&#xff1a;本资源是面向电力系统智能运维、计算机视觉算法研发及电气设备状态监测领域的专业图像数据集&#xff0c;聚焦开关柜接头红外过热故障的自动识别与定位问题。包内共2000个文件&#xff0c;包含1000张高质量红外热成像JPG图像及对应VOC格式XML标注文件&#xff…

作者头像 李华