vue3实现一个接球小游戏

使用 Vue3 + ts + canvas 实现一个web端接球小游戏,主要交互包括:操作键盘的【<】【>】来滑动手柄去接球、游戏开始、游戏暂停、游戏继续、游戏重新开始、游戏失败,用到的知识包括:ts、canvas绘图、事件监听器的添加与移除,定时器的使用与移除。效果如下图所示:

接球小游戏

详细代码:
模版部分

<template><div class="game"><canvas ref="canvasRef" id="breakout-canvas" width="500" height="500"></canvas><div class="btn-box"><el-button color="#529b2e" :disabled="['processing', 'pausing'].includes(status)" :icon="Sunrise"@click="handleStartGame">{{ status === 'end' ? 'Restart' : 'Start' }} game</el-button><el-button color="#c45656" :disabled="['default', 'end'].includes(status)" :icon="MostlyCloudy"@click="handlePauseOrContinueGame">{{ status === 'pausing' ? 'continue' : 'Pause' }} game</el-button></div></div>
</template>

TS部分

<script setup lang="ts">
import { onMounted, onUnmounted, reactive, ref, toValue } from 'vue'
import { ElMessage } from 'element-plus'
import {Sunrise,MostlyCloudy,
} from '@element-plus/icons-vue'
const paddleHeight = 10
const paddleWidth = 75
const BallRadius = 10const canvasRef = ref<HTMLCanvasElement>()
const intervalRef = ref<Number>()
const status = ref<'default' | 'processing' | 'pausing' | 'end'>('default')// 球的坐标
const ballPosition = reactive<{ x: number; y: number }>({ x: 0, y: 0 })
// 球每次移动的距离
const step = reactive<{ dx: number; dy: number }>({ dx: 2, dy: -2 })// 手柄的坐标 x 
const paddleX = ref<number>(0)
const rightPress = ref<boolean>(false)
const leftPress = ref<boolean>(false)const init = () => {const { width, height } = toValue(canvasRef)paddleX.value = (width - paddleWidth) / 2ballPosition.x = width / 2ballPosition.y = height - 30draw()
}// 绘制球
const drawBall = () => {const { x, y } = toValue(ballPosition)const { width, height } = toValue(canvasRef)const canvas = canvasRef.valueconst ctx = canvas.getContext('2d')// 清除上一个阶段的球,clearRect:指定矩形区域,让清除部分完全透明ctx.clearRect(0, 0, width, height)ctx.beginPath()ctx.arc(x, y, BallRadius, 0, Math.PI * 2, true)ctx.fillStyle = '#0095DD'ctx.fill()ctx.closePath()
}// 绘制手柄
const drawPaddle = () => {const { width, height } = toValue(canvasRef)if (toValue(rightPress)) {paddleX.value = Math.min(toValue(paddleX) + 7, width - paddleWidth)} else if (toValue(leftPress)) {paddleX.value = Math.max(toValue(paddleX) - 7, 0)}const canvas = canvasRef.valueconst ctx = canvas.getContext('2d')ctx.rect(paddleX.value, height - paddleHeight, paddleWidth, paddleHeight)ctx.fillStyle = "#0095DD"ctx.fill()ctx.closePath()
}const draw = () => {drawBall()drawPaddle()const { width, height } = toValue(canvasRef)const { x, y } = toValue(ballPosition)const { dx, dy } = toValue(step)// 判断球的 x 坐标 位置是否到达画布边缘,如果是,则调整stepif (x + dx > width - BallRadius || x + dx < BallRadius) {step.dx = -step.dx}// 判断球的 y 坐标 位置是否到达画布边缘,如果是,则调整stepif (y + dy < BallRadius) {step.dy = -step.dy} else if (y + dy > height - BallRadius) {// 球在手柄上,则也继续游戏if(x > toValue(paddleX) && x < toValue(paddleX) + paddleWidth){step.dy = -step.dy} else {// 球在手柄外面,游戏结束handleGameOver()return}}ballPosition.x += step.dxballPosition.y += step.dy
}const handleStartGame = () => {if (toValue(status) === 'end') {init()step.dx = 2step.dy = -2status.value = 'default'return}ElMessage.success('游戏开始!')status.value = 'processing'intervalRef.value = setInterval(() => draw(), 10)
}const handlePauseOrContinueGame = () => {const msg = status.value === 'pausing' ? '继续' : '暂停'ElMessage.info(`游戏${msg}`)if (status.value === 'pausing') {status.value = 'processing'intervalRef.value = setInterval(() => draw(), 10)} else {status.value = 'pausing'clearInterval(Number(intervalRef.value))}
}const handleGameOver = () => {ElMessage.error('游戏失败!')status.value = 'end'clearInterval(Number(intervalRef.value))
}const keyDownHandler = (e: KeyboardEvent) => {if (['Right', 'ArrowRight'].includes(e.key)) {rightPress.value = true} else if (['Left', 'ArrowLeft'].includes(e.key)) {leftPress.value = true}
}const keyUpHandler = (e: KeyboardEvent) => {if (['Right', 'ArrowRight'].includes(e.key)) {rightPress.value = false} else if (['Left', 'ArrowLeft'].includes(e.key)) {leftPress.value = false}
}onMounted(() => {init()window.addEventListener('keydown', keyDownHandler, false)window.addEventListener('keyup', keyUpHandler, false)
})onUnmounted(() => {clearInterval(Number(intervalRef.value))window.removeEventListener('keydown', keyDownHandler, false)window.removeEventListener('keyup', keyUpHandler, false)
})
</script>

Scss部分

<style lang="scss" scoped>
.game {min-height: 675px;height: 100vh;width: 100vw;display: flex;align-items: center;justify-content: center;flex-direction: column;background-image: url(@/assets/ground.png);background-repeat: no-repeat;background-size: cover;background-position: center;
}canvas {box-shadow: -15px 15px 15px rgba(236, 245, 255, 1);background: linear-gradient(230deg,#337ecc 0%,#d9ecff 100%);display: block;border-radius: 10px;
}.btn-box {width: 300px;margin: 50px auto;display: flex;justify-content: center;align-items: center;justify-content: space-between;
}
</style>

同志们,完整的项目地址为:完整代码,本人会不定时的在这个项目中更新一些页面开发,欢迎大家有问题随时咨询奥,一起学习!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/45406.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

优化 Java 数据结构选择与使用,提升程序性能与可维护性

优化 Java 数据结构选择与使用&#xff0c;提升程序性能与可维护性 引言 在软件开发中&#xff0c;数据结构的选择是影响程序性能、内存使用以及代码可维护性的关键因素之一。Java 作为一门广泛使用的编程语言&#xff0c;提供了丰富的内置数据结构&#xff0c;如数组、链表、…

JavaSE 面向对象程序设计进阶 IO 综合练习 利用糊涂包生成假数据 随机点名器 登录案例

目录 生成假数据 利用糊涂包生成假数据 随机点名器 综合练习 生成假数据 制造假数据 制造假数据也是开发中的一个能力 在各个网上爬取数据 这是其中一个方法 爬取网站中的内容 import cn.hutool.core.io.FileUtil;import java.io.IOException; import java.io.InputSt…

编程语言的选择:如何根据项目需求找到最合适的语言

在软件开发过程中&#xff0c;选择合适的编程语言是项目成功的关键之一。不同的编程语言拥有各自独特的特性和优势&#xff0c;适用于不同类型的项目。本文将从项目需求、团队技能、语言特性等多个角度&#xff0c;探讨如何为特定项目找到最合适的编程语言。 明确项目需求 首…

n2. Web相关工具

Web相关工具 1. http协议状态码2. Web相关工具2.1 links2.2 wget2.3 curl 3. httpd的压力测试工具 1. http协议状态码 http协议状态码分类 1xx&#xff1a;100-101 信息提示 2xx&#xff1a;200-206 成功 3xx&#xff1a;300-307 重定向 4xx&#xff1a;400-415 错误类信息&a…

昇思25天学习打卡营第24天 | LSTM+CRF序列标注

内容介绍&#xff1a; 序列标注指给定输入序列&#xff0c;给序列中每个Token进行标注标签的过程。序列标注问题通常用于从文本中进行信息抽取&#xff0c;包括分词(Word Segmentation)、词性标注(Position Tagging)、命名实体识别(Named Entity Recognition, NER)等。以命名实…

【JavaScript 算法】二分查找:快速定位目标元素

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、算法原理二、算法实现三、应用场景四、优化与扩展五、总结 二分查找&#xff08;Binary Search&#xff09;是一种高效的查找算法&#xff0c;适用于在有序数组中快速定位目标元素。相比于线性查找&#xff0c;二分查找…

护(H)网(W)行动正当时:你对HW知多少,一文带你全面了解护网行动

引言&#xff1a;2016年我国发布了《网络安全法》&#xff08;于2017年6月1日正式生效&#xff09;&#xff0c;明确规定了关键信息基础设施的运营者必须制定网络安全事件应急预案&#xff0c;并定期进行演练&#xff0c;为HW行动的开展提供了法律依据&#xff0c;通过红蓝对抗…

嵌入式裸机开发与 Linux 开发

引言 嵌入式系统在现代电子设备中占有重要地位&#xff0c;其开发模式主要分为裸机开发和基于操作系统&#xff08;如 Linux&#xff09;的开发。本文将详细介绍嵌入式裸机开发和 Linux 开发的特点、优缺点&#xff0c;并进行对比分析&#xff0c;以帮助读者更好地理解和选择合…

js 移动数组元素的几个方法

位置交换 /*** param {any[]} arr - 原始数组。* param {number} fromIndex - 当前元素所在位置索引。* param {number} toIndex - 移动到交换的位置索引。* returns {any[]} 返回修改后的数组。*/ const swapItem function(arr, fromIndex, toIndex) {arr[toIndex] arr.spl…

35、php 实现构建乘积数组、正则表达式匹配

题目&#xff1a; uniapp-v3是基于vue3语法的&#xff0c;在hbuilderx中运行即可 Project setup npm install Compiles and hot-reloads for development npm run serve Compiles and minifies for production npm run build 在HBuilderX中导入src文件打包;打包H5手机版可以…

Unity 中使用状态机模式来管理UI

1. 清晰的状态管理 状态机模式允许你以结构化的方式管理不同的UI状态。每个状态&#xff08;比如主菜单、设置菜单、游戏中界面等&#xff09;都有其独立的行为和属性&#xff0c;这使得管理复杂UI逻辑变得更加清晰和可维护。 2. 简化的状态切换 状态机模式可以简化不同UI状…

报表控件DevExpress Reporting中文教程 - 如何创建穿透钻取报表?

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表。 钻取报表允许用户通过单击主/活动报表文档中的…

Android的dtbo文件介绍

文章目录 设备树&#xff08;Device Tree&#xff09;设备树覆盖&#xff08;Device Tree Overlay, DTO&#xff09;dtbo文件的作用使用流程示例 dtbo 文件是 Android 设备中的设备树覆盖文件&#xff08;Device Tree Blob Overlay&#xff09;。它用于动态地修改设备树配置&am…

智能酒精壁炉与会所会客厅的氛围搭配

智能酒精壁炉与会所会客厅的氛围搭配可以创造出现代、高雅且舒适的环境&#xff0c;提升客人的整体体验。以下是如何将智能酒精壁炉与会所会客厅氛围相协调的几点建议&#xff1a; 现代化与高品位感&#xff1a; 智能酒精壁炉展现出现代化的设计和高科技特点&#xff0c;与会所…

应急响应-战后溯源反制社会工程学

&#x1f3bc;个人主页&#xff1a;金灰 &#x1f60e;作者简介:一名简单的大一学生;易编橙终身成长社群的嘉宾.✨ 专注网络空间安全服务,期待与您的交流分享~ 感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持&#xff01;❤️ &#x1f34a;易编橙终身成长社群&#…

开源的混合AI搜索引擎;定制 Claude 3 Haiku 模型; 和gpt-4o同样Transformer架构的开源视觉语言模型;离线自动转录工具

✨ 1: MemFree MemFree是一款开源的混合AI搜索引擎&#xff0c;可搜索个人知识库和互联网。 MemFree 是一个开源的混合AI搜索引擎&#xff0c;可以同时在你的个人知识库&#xff08;如书签、笔记、文档等&#xff09;和互联网中进行搜索。这款搜索引擎的主要特点包括&#xf…

嵌入式智能手表项目实现分享

简介 这是一个基于STM32F411CUE6和FreeRTOS和LVGL的低成本的超多功能的STM32智能手表~ 推荐 如果觉得这个手表的硬件难做,又想学习相关的东西,可以试下这个新出的开发板,功能和例程demo更多!FriPi炸鸡派STM32F411开发板: 【STM32开发板】 FryPi炸鸡派 - 嘉立创EDA开源硬件平…

使用mediapip 检测pose 并作为一个服务

代码 import uvicorn from fastapi import FastAPI, HTTPException import cv2 import mediapipe as mp from pydantic import BaseModelapp FastAPI()# 创建一个模型来序列化姿态数据 class PoseData(BaseModel):landmarks: list# 初始化MediaPipe的姿态估计模型 mp_pose m…

GD32MCU最小系统构成条件

大家是否有这个疑惑&#xff1a;大学课程学习51的时候&#xff0c;老师告诉我们51的最小系统构成&#xff1f;那么进入32位单片机时代&#xff0c;gd32最小系统构成又是怎么样的呢&#xff1f; 1.供电电路 需要确保供电的电压电流稳定&#xff0c;以东方红开发版为例&#xff…

Qt WARNING: Failure to find: xxxxxx.h

重新规划了自定义文件夹后&#xff0c;编译出现错误&#xff0c;如 Qmake WARNING: Failure to find: xxxxxx.h 或者 error: XXXX.h: No such file or directory 如果文件是在windows下直接重新放置新的目录&#xff0c;那么需要修改.pro文件 老文件的可能没有注释或删除&am…