用HTML5的<canvas>元素实现刮刮乐游戏
用HTML5的<canvas>元素实现刮刮乐游戏
用HTML5的<canvas>元素实现刮刮乐,要求:将上面的“图层”的图像可用鼠标刮去,露出下面的“图层”的图像。
示例从简单到复杂。
简单示例
准备两张图像,我这里上面的图像top_image.png,下面的图像bottom_image.png,如下图:
我这里为方便 ,经图片和源码文件放在同一个文件夹中。
先看用一个canvas元素实现刮刮乐,源码如下:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>刮刮乐(Scratch Card)</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {height: 100vh;display: flex;justify-content: center;align-items: center;background-color: #f0f0f0; /* 背景色 */}canvas {background-image: url('bottom_image.png'); /* 底层图片 */background-size: cover;}</style>
</head>
<body><canvas id="canvas" width="356" height="358"></canvas><script>var canvas = document.getElementById("canvas");var ctx = canvas.getContext("2d");// 加载上层图片(可被刮去的图层)var img = new Image();img.src = "top_image.png"; // 上层图片路径img.onload = function() {ctx.drawImage(img, 0, 0, canvas.width, canvas.height);};// 标记是否按下鼠标(开始刮卡)var isDown = false;// 鼠标按下事件canvas.addEventListener('mousedown', function() {isDown = true;// 切换到“擦除”模式ctx.globalCompositeOperation = 'destination-out';});// 鼠标松开事件canvas.addEventListener('mouseup', function() {isDown = false;});// 鼠标移动事件canvas.addEventListener('mousemove', function(event) {if (isDown) {let x = event.offsetX;let y = event.offsetY;// 绘制擦除效果ctx.beginPath();ctx.arc(x, y, 20, 0, Math.PI * 2, false); // 使用圆形笔触ctx.fill();ctx.closePath();}});</script>
</body>
</html>
下面用两个canvas元素实现刮刮乐,底层图片和上层图片各用一个canvas元素,效果和上面的一样。实现的源码如下:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>刮刮乐(Scratch Card)2</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {height: 100vh;display: flex;justify-content: center;align-items: center;background-color: #f0f0f0;}#container {position: relative;width: 356px;height: 358px;}canvas {position: absolute;top: 0;left: 0;}</style>
</head>
<body><div id="container"><canvas id="bottomCanvas" width="356" height="358"></canvas> <!-- 底层Canvas --><canvas id="topCanvas" width="356" height="358"></canvas> <!-- 上层Canvas --></div><script>document.addEventListener('DOMContentLoaded', function() {var bottomCanvas = document.getElementById('bottomCanvas');var topCanvas = document.getElementById('topCanvas');var bottomCtx = bottomCanvas.getContext('2d');var topCtx = topCanvas.getContext('2d');// 加载底层图片var bottomImage = new Image();bottomImage.src = 'bottom_image.png'; // 底层图片路径bottomImage.onload = function() {bottomCtx.drawImage(bottomImage, 0, 0, bottomCanvas.width, bottomCanvas.height);};// 加载上层图片var topImage = new Image();topImage.src = 'top_image.png'; // 上层图片路径topImage.onload = function() {topCtx.drawImage(topImage, 0, 0, topCanvas.width, topCanvas.height);};var isDown = false;// 鼠标按下事件topCanvas.addEventListener('mousedown', function() {isDown = true;topCtx.globalCompositeOperation = 'destination-out';});// 鼠标松开事件topCanvas.addEventListener('mouseup', function() {isDown = false;});// 鼠标移动事件topCanvas.addEventListener('mousemove', function(event) {if (!isDown) return;var x = event.offsetX;var y = event.offsetY;// 绘制擦除效果topCtx.beginPath();topCtx.arc(x, y, 20, 0, Math.PI * 2, false); // 使用圆形笔触topCtx.fill();topCtx.closePath();});});</script>
</body>
</html>
复杂示例
下面是改进,从列表框(下拉框)选择图片刮刮乐,增加了游戏的趣味性。
先给出效果
项目(project)的目录结构如下:
我这里游戏图片:
源码如下:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>刮刮乐(Scratch Card)3</title><style>div{ margin:20px;text-align:center;}* {margin: 0;padding: 0;box-sizing: border-box;}body {height: 100vh;display: flex;justify-content: center;align-items: center;background-color: #f0f0f0;}#container {position: relative;width: 356px;height: 358px;}canvas {position: absolute;top: 0;left: 0;}</style>
</head>
<body><div> 选择游戏图片<select id="mySelect" onchange="loadImages()"> <!-- 添加 onchange 事件 --><option value="1">1</option> <!-- 更改 value 以匹配图像名称 --><option value="2">2</option><option value="3">3</option></select><div><div id="container"><canvas id="bottomCanvas" width="356" height="358"></canvas> <!-- 底层Canvas --><canvas id="topCanvas" width="356" height="358"></canvas> <!-- 上层Canvas --></div><script>function loadImages() {var selectElement = document.getElementById('mySelect');var selectedValue = selectElement.options[selectElement.selectedIndex].value;var bottomCanvas = document.getElementById('bottomCanvas');var topCanvas = document.getElementById('topCanvas');var bottomCtx = bottomCanvas.getContext('2d');var topCtx = topCanvas.getContext('2d');// 清除画布bottomCtx.clearRect(0, 0, bottomCanvas.width, bottomCanvas.height);topCtx.clearRect(0, 0, topCanvas.width, topCanvas.height);// 加载底层图片var bottomImage = new Image();bottomImage.src = 'img/bottom' + selectedValue + '.png';bottomImage.onload = function() {bottomCtx.drawImage(bottomImage, 0, 0, bottomCanvas.width, bottomCanvas.height);};// 重新加载并绘制上层图片var topImage = new Image();topImage.src = 'img/top' + selectedValue + '.png'; // 确保这里的路径正确匹配你的图片路径和命名topImage.onload = function() {topCtx.globalCompositeOperation = 'source-over'; // 重置合成操作为默认值topCtx.drawImage(topImage, 0, 0, topCanvas.width, topCanvas.height);// 确保刮刮效果重新应用addScratchEffect(topCanvas, topCtx);};}function addScratchEffect(canvas, ctx) {var isDown = false;// 移除之前可能添加的事件监听器canvas.onmousedown = null;canvas.onmouseup = null;canvas.onmousemove = null;// 鼠标按下事件canvas.onmousedown = function() {isDown = true;ctx.globalCompositeOperation = 'destination-out'; // 设置合成操作以实现刮效果};// 鼠标松开事件canvas.onmouseup = function() {isDown = false;};// 鼠标移动事件canvas.onmousemove = function(event) {if (!isDown) return;var x = event.offsetX;var y = event.offsetY;// 绘制擦除效果ctx.beginPath();ctx.arc(x, y, 20, 0, Math.PI * 2); // 使用圆形笔触ctx.fill();};}// 页面加载完毕后初始化画布document.addEventListener('DOMContentLoaded', function() {loadImages(); // 页面加载时也加载图片});</script>
</body>
</html>
本文是对https://blog.csdn.net/cnds123/article/details/112392014 例子的补充
关于HTML5中,使用<select>元素创建一个列表框(下拉框),并使用JavaScript来操作,可参见https://blog.csdn.net/cnds123/article/details/128353007
相关文章:

用HTML5的<canvas>元素实现刮刮乐游戏
用HTML5的<canvas>元素实现刮刮乐游戏 用HTML5的<canvas>元素实现刮刮乐,要求:将上面的“图层”的图像可用鼠标刮去,露出下面的“图层”的图像。 示例从简单到复杂。 简单示例 准备两张图像,我这…...
TypeScript + react 中 TypeScript 的加入后 , 有哪些优化项目
在使用 TypeScript 结合 React 进行开发时,TypeScript 提供了许多优化和增强代码质量的方式。以下是一些关键的优化操作和最佳实践: 强类型组件属性(Props)和状态(State): 使用接口或类型别名定义组件的 pr…...
Redis学习路径(构建体系)
学习路径 掌握数据类型(分析底层数据结构)和缓存的基本使用 (理论使用) 掌握 redis 实现高性能,高可靠、高可用技术 (理论)学习redis源代码底层实现 (底层实现) 先来一个引言,比较宏观的角度…...

【README 小技巧】 展示gitee中开源项目start
【README 小技巧】 展示gitee中开源项目start <a target"_blank" hrefhttps://gitee.com/wujiawei1207537021/wu-framework-parent><img srchttps://gitee.com/wujiawei1207537021/wu-framework-parent/badge/star.svg altGitee star/></a>...
tcping实用小工具
Tcping实用小工具命令详解 一、tcping介绍 tcping:tcping命令基于tcp协议监控,可以从较低级别的协议获得简单的,可能不可靠的数据报服务。 原则上,TCP应该能够在从容硬线连接到分组交换或电路交换网络的各种通信系统之上操作。 …...
【Web】Java反序列化之CC2——commons-collections4的新链之一
目录 关于commons-collections4 一个重要的思维模型 触发Transform的关键类:TransformingComparator 反序列化的入口:PriorityQueue Exp 关于commons-collections4 commons-collections4 是 Apache Commons 组件库中的一个项目,它是对旧…...

golang使用gorm操作mysql1
1.mysql连接配置 package daoimport ("fmt""gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gorm/logger" )var DB *gorm.DB// 连接数据库,启动服务的时候,init方法就会执行 func init() {username : "roo…...
Flutter异常上报及性能监控实现
1. 页面异常监测 在Flutter中,通常用FlutterError监测Flutter框架抛出的异常,用runZonedGuarded监测应用中用户代码异常。 class AppGuarded {run(Widget app) {//1. 用FlutterError监测flutter框架抛出的异常FlutterError.onError (FlutterErrorDetail…...

基于springboot+vue的工厂车间管理系统
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 主要内容:毕业设计(Javaweb项目|小程序|Pyt…...
Java基础 - Stream 流:Stream API的终端操作
在前两篇博客中,我介绍了构建 Stream 流的多种方式,以及 Stream API 的中间操作,如果你还没有阅读,你可以点击这里和这里查看。 Java基础 - Stream 流:构建流的多种方式 Java基础 - Stream 流:Stream API…...
高级语言期末2009级A卷(计算机学院)
1.编写函数,打印下列序列0,1,1,2,3,5,8,13,21,34...(斐波那契序列)的前n项 #include <stdio.h>int main() {int x0,y1,z,n;scanf("%d",&…...
docker-compose搭建php开发环境
Docker Compose简介 Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,您可以使用 YML 文件来配置应用程序需要的所有服务。然后使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。而DockerCompose作为一种容器编排工具&…...

翻译论文:Beating Floating Point at its Own Game: Posit Arithmetic(一)
仅作记录学习使用,侵删 原文Beating Floating Point at its Own Game: Posit Arithmetic 参考翻译Posit: 替换IEE754的新方式 | SIGARCH 摘要 IEEE标准754浮点数(浮点数)的直接接点替换 Posit的优势 不需要区间算术或可变大小操作数 如…...

【数据结构-图论】并查集
并查集(Union-Find)是一种数据结构,它提供了处理一些不交集的合并及查询问题的高效方法。并查集主要支持两种操作: 查找(Find):确定某个元素属于哪个子集,这通常意味着找到该子集的…...

云计算时代的运维: 职业发展方向与岗位选择
✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua,在这里我会分享我的知识和经验。&#x…...
java锁底层概述
Java中的锁是并发编程中核心的同步机制之一,用于控制多个线程对共享资源的访问,以保证数据的一致性和完整性。Java锁的底层实现依赖于操作系统的原生线程模型和Java虚拟机(JVM)的实现。这里主要讨论两种常见的锁:synch…...

win10如何添加指纹登陆
1、首先进入设置,进入下一个设置页面 2、在下一个设置页面内,我们直接使用右上角的搜索框,输入“指纹/finger”进行搜索。回车之后进入设置指纹登陆选项 3、设置指纹登陆的前期是设置好你的密码和pin码(先要设定登录密码和pin码),这里pin和密码都可以直接登陆我们的win10,设…...

足底筋膜炎的症状及治疗
足底筋膜炎症状:足底筋膜炎通常表现为足跟部疼痛,尤其是在晨起或长时间站立、行走后加重。疼痛可能向足底前部或足弓处放射,严重时可能影响行走。此外,患者还可能出现足跟部肿胀、皮肤温度升高、局部压痛等症状。 足底筋膜炎治疗方…...
udp丢包问题研究
//发现udp 有收不到数据包现象. 一: 观察丢包 1. ifconfig enp8s0 2. netstat -s -u 二: 修改系统缓存参数. recv_buffer_size 修改系统buffer_size sysctl -w net.core.rmem_max26214400 sysctl -w net.core.rmem_default26214400 三: 应用程序考虑 av_dict_set(&m_o…...

在idea中用模板骨架初始创建maven管理的web项目时没有src有关的目录的解决方案
一.问题如下 二.解决方法 首先关闭当前项目,接着修改全局设置,重新创建项目 在VM Options中添加"-DarchetypeCataloginternal",点击ok保存 点击创建,如果创建成功没报错且有src,就ok了。 当然如果出现以下…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...

51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...

Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...

【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...