📅 我们继续 50 个小项目挑战!—— InsectCatchGame组件

仓库地址:https://github.com/SunACong/50-vue-projects

项目预览地址:https://50-vue-projects.vercel.app/

在这里插入图片描述


今天,我将带你一步步构建一个有趣的小游戏——“抓昆虫”(Catch The Insect),使用 Vue 3 的组合式 API (<script setup>) 和 Tailwind CSS 来打造一个响应式、交互性强的游戏应用。这个游戏虽然简单,但包含了状态管理、计时器、动画、事件处理等前端开发中的关键概念。


🎮 游戏概述

“抓昆虫”是一款看似简单实则极具挑战性的游戏。玩家需要在屏幕上点击快速移动的昆虫来得分,但随着时间推移,昆虫会越来越多,最终变得几乎不可能完成。这不仅考验反应速度,也是一场耐心的较量!

🎯 游戏目标

  • 选择你喜欢(或最讨厌)的昆虫作为目标。
  • 在限定时间内尽可能多地点击昆虫以获得高分。
  • 体验“不可能完成”的挑战乐趣。

🛠️ 技术栈与核心概念

技术/概念 说明
Vue 3 <script setup> 使用最新的组合式 API 语法糖,让代码更简洁、逻辑更清晰。
ref / computed 管理响应式数据和派生状态。
onMounted / onBeforeUnmount 生命周期钩子,用于初始化和清理资源(如定时器)。
Tailwind CSS 实现快速布局和样式设计,支持响应式与动画效果。
localStorage(可选扩展) 可用于保存最高分记录。
动态样式绑定 :style 控制昆虫的位置、旋转和缩放动画。
事件监听与处理 处理点击、右键菜单、图片加载错误等。

🖼️ 游戏界面结构

游戏分为三个主要界面:

1. 开始屏幕(Start Screen)

<div v-if="currentScreen === 'start'" ...>
  <h1>Catch The Insect</h1>
  <button @click="currentScreen = 'select'">Play Game</button>
</div>
  • 居中显示标题和开始按钮。
  • 点击按钮后跳转到选择昆虫界面。

2. 昆虫选择界面(Select Insect)

<div v-else-if="currentScreen === 'select'" ...>
  <h1>What is your "favorite" insect?</h1>
  <ul>
    <li v-for="insect in insectsList" :key="insect.id">
      <button @click="selectInsect(insect)">
        <p>{{ insect.name }}</p>
        <img :src="insect.src" alt="insect.name" />
      </button>
    </li>
  </ul>
</div>
  • 展示四种昆虫供玩家选择:苍蝇、蚊子、蜘蛛、蟑螂。
  • 每个昆虫按钮包含名称和图片。
  • 点击后调用 selectInsect() 函数,进入游戏主界面。

3. 游戏主界面(Game Screen)

<div v-else-if="currentScreen === 'game'" ...>
  <h3>Time: {{ formattedTime }}</h3>
  <h3>Score: {{ score }}</h3>

  <div v-if="messageVisible" class="...">Are you annoyed yet?...</div>

  <!-- 昆虫元素 -->
  <div v-for="(insect, index) in activeInsects" ...>
    <img :src="selectedInsect.src" ... />
  </div>
</div>
  • 显示当前时间和得分。
  • 当分数超过 19 时,弹出挑衅性提示:“Are you annoyed yet? You are playing an impossible game!!”
  • 动态渲染所有活跃的昆虫实例。

💡 核心逻辑实现

🧠 响应式状态管理

const currentScreen = ref('start')
const selectedInsect = ref(null)
const score = ref(0)
const time = ref(0)
const messageVisible = ref(false)
const activeInsects = ref([])
const gameTimer = ref(null)
const insectTimer = ref(null)

这些 ref 变量构成了游戏的核心状态,Vue 会自动追踪它们的变化并更新视图。


⏱ 计时与昆虫生成

游戏计时器
function startGame() {
  gameTimer.value = setInterval(() => time.value++, 1000)
}

每秒增加一次时间。

昆虫生成机制
function createInsect() {
  const x = Math.random() * (window.innerWidth - 100) + 50
  const y = Math.random() * (window.innerHeight - 100) + 50
  const rotation = Math.random() * 360

  activeInsects.value.push({ x, y, rotation, caught: false })

  // 下一只昆虫出现时间:1~1.5秒之间
  insectTimer.value = setTimeout(createInsect, 1000 + Math.random() * 500)
}
  • 随机生成位置(避开边缘50px防止溢出)。
  • 随机旋转角度增加视觉多样性。
  • 使用 setTimeout 实现递归调用,持续生成新昆虫。

🎯 捕捉昆虫逻辑

function catchInsect(index) {
  activeInsects.value[index].caught = true
  score.value++

  if (score.value > 19) messageVisible.value = true

  // 200ms后移除昆虫
  setTimeout(() => {
    activeInsects.value.splice(index, 1)
  }, 200)

  // 500ms后生成新昆虫
  setTimeout(createInsect, 500)
}
  • 点击昆虫后标记为 caught,触发动画(缩放至0)。
  • 分数加1。
  • 弹出“挑衅”消息。
  • 延迟移除昆虫,避免立即消失影响体验。
  • 快速生成新昆虫,加快游戏节奏。

🎨 动画与样式控制

昆虫动画效果
<div
  :style="{
    top: `${insect.y}px`,
    left: `${insect.x}px`,
    transform: `translate(-50%, -50%) scale(${insect.caught ? 0 : 1}) rotate(${insect.rotation}deg)`,
  }"
  class="transition-transform duration-300 ease-in-out"
>
  • translate(-50%, -50%):使昆虫中心对齐其坐标点。
  • scale(0):被捕获时缩放为0,实现“消失”动画。
  • rotate(...):随机旋转增加真实感。
  • transition-transform:平滑过渡缩放和旋转效果。
消息提示动画
<div v-if="messageVisible" class="... opacity-100 transition-all duration-500">

利用 opacitytransition 实现淡入淡出效果。


🖼 图片错误处理

function handleImageError(event) {
  event.target.src = 'https://via.placeholder.com/100?text=No+Image'
}

防止因图片链接失效导致界面错乱,提升健壮性。


🧹 资源清理

onBeforeUnmount(() => {
  if (gameTimer.value) clearInterval(gameTimer.value)
  if (insectTimer.value) clearTimeout(insectTimer.value)
})

避免内存泄漏,确保组件卸载时清除所有定时器。


📊 数据与计算属性

const formattedTime = computed(() => {
  const minutes = Math.floor(time.value / 60).toString().padStart(2, '0')
  const seconds = (time.value % 60).toString().padStart(2, '0')
  return `${minutes}:${seconds}`
})

使用 computed 实时格式化时间为 MM:SS 格式,自动更新。


🐞 潜在问题与优化建议

问题 建议解决方案
频繁创建/销毁 DOM 节点 可考虑使用对象池(Object Pooling)复用昆虫元素,减少性能开销。
图片资源依赖外部链接 将图片本地化或使用 CDN 稳定地址,避免加载失败。
无结束机制 可添加“游戏结束”逻辑,例如:时间到达一定值后停止生成新昆虫,或设置最大昆虫数量上限。
缺乏音效与反馈 添加点击音效、得分动画等增强沉浸感。
移动端适配 当前 cursor-pointer 和点击事件在移动端可能体验不佳,需测试并优化触摸交互。

🎨 Tailwind CSS 样式重点

类名 作用 应用场景
relative 设置元素为相对定位,允许其内部的绝对定位元素相对于它进行定位。 用于根 div,确保内部昆虫元素可以相对于整个屏幕进行绝对定位。
h-screen w-screen 设置高度和宽度为视口高度和宽度的 100%。 确保背景颜色覆盖整个浏览器窗口。
overflow-hidden 隐藏超出容器的内容。 防止任何溢出内容显示在游戏界面之外。
bg-[#516dff] 自定义背景颜色,使用任意颜色值。 给游戏界面设置特定的背景色。
font-[Press_Start_2P] 引入自定义字体(需先通过 @font-face 或 Google Fonts 加载)。 为游戏文本应用特定的游戏风格字体。
text-white 设置文本颜色为白色。 提高对比度,使文字在深色背景下清晰可见。
transition-margin 结合其他类使用来创建基于边距变化的过渡效果。 在不同屏幕之间切换时提供平滑过渡效果。
duration-500 指定过渡效果的持续时间为 500ms。 让页面切换或状态变化更加流畅。
ease-out 定义过渡的速度曲线,开始快结束慢。 使动画看起来更自然。
flex 启用 Flexbox 布局。 用于垂直和水平居中内容。
items-center justify-center 使用 Flexbox 居中对齐子元素。 让标题和按钮在屏幕上居中显示。
px-6 py-4 设置内边距:左右各 6 个单位,上下各 4 个单位。 调整按钮的内边距,使其看起来更加舒适。
hover:opacity-90 当鼠标悬停时降低透明度。 提供视觉反馈,表明按钮是可交互的。
focus:outline-none 移除聚焦时的默认轮廓。 改善用户体验,特别是对于非键盘导航用户。
list-none 移除列表项的默认样式。 清除昆虫选择列表的默认样式,以便自定义设计。
m-4 设置所有方向的外边距为 4 个单位。 给每个昆虫选项添加适当的间距。
border-2 border-white 设置边框宽度为 2px 并指定颜色为白色。 突出显示昆虫选择按钮。
hover:bg-white hover:text-[#516dff] 当鼠标悬停时改变背景颜色和文本颜色。 提供交互反馈,帮助用户识别可点击区域。
absolute 将元素位置设为绝对定位,相对于最近的已定位祖先元素。 允许精确控制昆虫的位置。
transition-transform duration-300 ease-in-out 定义变换属性的过渡效果,持续时间 300ms,速度曲线为 ease-in-out。 实现昆虫捕捉时的缩放动画。
object-contain 调整图片大小以适应容器,同时保持宽高比。 确保昆虫图片不会失真。
top-5 left-5 right-5 分别设置距离顶部、左侧和右侧的距离为 5 个单位。 控制计分板的位置。

🏁 总结

这个项目非常适合用来学习 Vue 3 的响应式系统、生命周期管理以及如何结合 CSS 实现流畅的动画效果。它既是一个小游戏,也是一个绝佳的学习案例。

  1. 难度递增系统:随着分数提高,昆虫移动速度加快或出现更多种类。
  2. 排行榜功能:结合 localStorage 保存最高分。
  3. 主题切换:白天/黑夜模式。
  4. 昆虫行为模拟:让昆虫有简单的行为,如躲避鼠标、随机游走。

现在,你准备好挑战自己的耐心了吗?快去试试看能坚持多久!🪰


👉 下一篇,我们将完成收尾工作,完成项目的下半部分主要是介绍以下课程的合作者About组件和一个Footer组件,也是项目的最后一篇文章。🚀

感谢阅读,欢迎点赞、收藏和分享 😊

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐