Canvas实现数字电子时钟(带粒子掉落效果)

前置知识

Canvas实现简易数字电子时钟

效果

在这里插入图片描述

逻辑代码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>粒子时钟</title><style>body {margin: 0;overflow: hidden}</style>
</head><body><canvas id="canvas"></canvas><script>const [width, height] = [window.innerWidth, window.innerHeight];const canvas = document.getElementById('canvas');canvas.width = width;canvas.height = height;const ctx = canvas.getContext('2d');/*2.声明钟表的基本数据*///钟表数字const numDt = [//0[1, 1, 1, 1,1, 0, 0, 1,1, 0, 0, 1,1, 0, 0, 1,1, 0, 0, 1,1, 0, 0, 1,1, 1, 1, 1],//1[0, 0, 0, 1,0, 0, 0, 1,0, 0, 0, 1,0, 0, 0, 1,0, 0, 0, 1,0, 0, 0, 1,0, 0, 0, 1],//2[1, 1, 1, 1,0, 0, 0, 1,0, 0, 0, 1,1, 1, 1, 1,1, 0, 0, 0,1, 0, 0, 0,1, 1, 1, 1],//3[1, 1, 1, 1,0, 0, 0, 1,0, 0, 0, 1,1, 1, 1, 1,0, 0, 0, 1,0, 0, 0, 1,1, 1, 1, 1],//4[1, 0, 0, 1,1, 0, 0, 1,1, 0, 0, 1,1, 1, 1, 1,0, 0, 0, 1,0, 0, 0, 1,0, 0, 0, 1],//5[1, 1, 1, 1,1, 0, 0, 0,1, 0, 0, 0,1, 1, 1, 1,0, 0, 0, 1,0, 0, 0, 1,1, 1, 1, 1],//6[1, 1, 1, 1,1, 0, 0, 0,1, 0, 0, 0,1, 1, 1, 1,1, 0, 0, 1,1, 0, 0, 1,1, 1, 1, 1],//7[1, 1, 1, 1,0, 0, 0, 1,0, 0, 0, 1,0, 0, 0, 1,0, 0, 0, 1,0, 0, 0, 1,0, 0, 0, 1],//8[1, 1, 1, 1,1, 0, 0, 1,1, 0, 0, 1,1, 1, 1, 1,1, 0, 0, 1,1, 0, 0, 1,1, 1, 1, 1],//9[1, 1, 1, 1,1, 0, 0, 1,1, 0, 0, 1,1, 1, 1, 1,0, 0, 0, 1,0, 0, 0, 1,1, 1, 1, 1],//:[0, 0, 0, 0,0, 0, 0, 0,0, 0, 1, 0,0, 0, 0, 0,0, 0, 1, 0,0, 0, 0, 0,0, 0, 0, 0],];//项目数量const itemNum = 8;//项目间隙的数量const itemSpNum = itemNum - 1;//项目间隙的宽度const itemSpace = 24;//粒子尺寸const partSize = 24;//项目的列数,行数const [itemColNum, itemRowNum] = [4, 7];//项目宽度const itemWidth = partSize * itemColNum;//时钟宽度const clockWidth = itemWidth * itemNum + itemSpace * itemSpNum;//时钟的高度const clockHeight = partSize * itemRowNum;//时钟的位置,放在屏幕中间const clockPos = {x: (width - clockWidth) / 2,y: (height - clockHeight) / 5};//所有粒子的边界const edge = { left: 0, right: width, bottom: height - 50 };/*粒子对象*/class Particle {constructor(width, height) {//尺寸this.width = width;this.height = height;//位置this.x = 0;this.y = 0;//1:新生,0:坠落this.state = 1;//父级this.parent = null;//速度this.vx = this.getVx();this.vy = 0.002;//重力this.gravity = 0.03;//弹力this.bounce = 0.85;}//获取x 轴的速度,避免直上直下的弹动//vx 取值范围是[-0.5,0.5],但不能在[-0.15,0.15] 之间getVx() {let vx = Math.random() - 0.5;if (Math.abs(vx) < 0.15) {return this.getVx();} else {return vx;}}//更新数据update(diff) {//解构状态、尺寸和位置const { state, width, height, parent } = this;//解构边界const { left, right, bottom } = edge;if (!state) {//如果粒子的状态为坠落状态this.vy += this.gravity;//设置粒子位置this.y += this.vy * diff;this.x += this.vx * diff;//底部碰撞检测if (this.y + height > bottom) {//将粒子位置调整到底部边界之上this.y = bottom - height;//y 值反弹this.vy *= -this.bounce;}//左右碰撞检测if (this.x + width < left || this.x > right) {//将此元素从父对象的数组中删除parent.remove(this);}}}//绘图方法draw(ctx) {//解构位置、尺寸、缩放const { x, y, width, height } = this;ctx.save();ctx.fillRect(x, y, width, height);ctx.restore();}}/*粒子粒子发射器*/class Gun {constructor(width, height) {//尺寸this.width = width;this.height = height;//位置this.x = 0;this.y = 0;//状态 1:粒子发射器的枪膛中有粒子;0:粒子发射器的枪膛中没有粒子。默认没有粒子。this._state = 0;//粒子库this.children = [];}get state() {return this._state;}set state(val) {//在为state 赋值时,做简单的diff 判断if (this._state !== val) {if (val) {//制造一个粒子this.createParticle();} else {//粒子仓库中的第一个粒子发射this.children[0].state = 0;}this._state = val;}}/*新建粒子*/createParticle() {const { x, y, width, height, img, children } = this;//实例化粒子对象const part = new Particle(width, height, img);//粒子位置part.x = x;part.y = y;//粒子父级part.parent = this;//粒子状态 1:粒子发射器中要有粒子part.state = 1;//将粒子以前置的方式添加到粒子仓库里children.unshift(part);}/*删除粒子* ele 粒子对象* */remove(ele) {const { children } = this;const index = children.indexOf(ele);if (index !== -1) {children.splice(index, 1);}}/*更新* diff:以毫秒为单位的时间差*/update(diff) {//遍历粒子仓库中的所有粒子this.children.forEach((ele) => {//更新粒子ele.update(diff);})}//绘制辅助线drawStroke(ctx) {const { x, y, width, height } = this;ctx.save();ctx.strokeStyle = '#aaa';ctx.strokeRect(x, y, width, height);ctx.restore();}}/*items 项目集合* items=[项目,项目,项目,项目,项目,项目,项目,项目],其中有8个项目* 项目=[4*7=28个粒子发射器]* 1个粒子发射器中有0个或多个粒子* */const items = [];//建立八个项目for (let i = 0; i < itemNum; i++) {//建立项目const item = [];//当前项目x 位置let curItemX = (itemWidth + itemSpace) * i + clockPos.x;//每个项目都是7列,4行。所以遍历列和行,建立粒子发射器。for (let r = 0; r < itemRowNum; r++) {//发射器的y 位置const partY = partSize * r + clockPos.y;//遍历列for (let c = 0; c < itemColNum; c++) {//粒子发射器的x 位置const partX = partSize * c + curItemX;//实例粒子发射器对象const gun = new Gun(partSize, partSize);//设置粒子发射器的位置gun.x = partX;gun.y = partY;//将粒子发射器添加到项目中item.push(gun);}}//将项目添加到项目集合里items.push(item);}/*先示配代表了冒号的两个项目*/fitNum(10, 2);fitNum(10, 5);/*基于时钟文字,修改项目中每个粒子发射器的状态的方法* numInd 数字在numDt数据集合中的索引* itemInd 项目在项目集合中的索引位置* */function fitNum(numInd, itemInd) {const item = items[itemInd];numDt[numInd].forEach((ele, ind) => {item[ind].state = ele;})}/*计时器*/let time = new Date();/*updateTime() 获取时间差和时、分、秒的方法* 此处的时、分、秒是由两个数字组成的数组,如1点=[0,1],10点=[1,0]* */function updateTime() {//获取时间差const now = new Date();const diff = now - time;time = now;//返回时间差和时、分、秒return {diff,hour: parseTime(time.getHours()),minute: parseTime(time.getMinutes()),second: parseTime(time.getSeconds())};}//解析时间,如30分钟,解析为[3,0]function parseTime(t) {let arr;if (t < 10) {arr = [0, t];} else {const str = t.toString();arr = [parseInt(str[0]), parseInt(str[1])]}return arr;}/*更新时钟的时间:基于数组类型的时、分、秒更新时钟*/function updateClock(hour, minute, second) {//匹配小时fitNum(hour[0], 0);fitNum(hour[1], 1);//匹配分钟fitNum(minute[0], 3);fitNum(minute[1], 4);//匹配秒fitNum(second[0], 6);fitNum(second[1], 7);}//渲染!(function render() {//获取时间差和时分秒const { diff, hour, minute, second } = updateTime();//更新时钟的时间updateClock(hour, minute, second);//清理画布ctx.clearRect(0, 0, width, height);//坠落中的粒子,要最后渲染,避免其被新生的和成年的粒子遮挡const dropParts = [];//先渲染state 状态不为0 的元素items.flat().forEach((part) => {part.update(diff);part.children.forEach((ele) => {if (ele.state) {ele.draw(ctx);} else {dropParts.push(ele);}});//画辅助线part.drawStroke(ctx);});//后渲染状态为0 的元素dropParts.forEach((ele) => {ele.draw(ctx);});//请求动画帧requestAnimationFrame(render)})()</script>
</body></html>

文章参考开课吧课程《webGL工程师之webgl之最短教程》,想详细了解的小伙伴可以去看看

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

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

相关文章

Redis开源协议调整,我们怎么办?

2024年3月20日, Redis官方宣布&#xff0c;从 Redis 7.4版本开始&#xff0c;Redis将获得源可用许可证 ( RSALv2 ) 和服务器端公共许可证 ( SSPLv1 ) 的双重许可&#xff0c;时间点恰逢刚刚完成最新一轮融资&#xff0c;宣布的时机耐人寻味。 Redis协议调整&#xff0c;对云计算…

Nginx三大常用功能“反向代理,负载均衡,动静分离”

注意&#xff1a;以下案例在Windows系统计算机作为宿主机&#xff0c;Linux CentOS 作为虚拟机的环境中实现 一&#xff0c;Nginx配置实例-反向代理 1.反向代理 案例一 实现效果&#xff1a;使用nginx反向代理&#xff0c;访问 www.123.com 直接跳转到127.0.0.1:8080 准备工…

Golang Context是什么

一、这篇文章我们简要讨论Golang的Context有什么用 1、首先说一下Context的基本作用&#xff0c;然后在讨论他的实现 (1)数据传递&#xff0c;子Context只能看到自己的和父Context的数据&#xff0c;子Context是不能看到孙Context添加的数据。 (2)父子协程的协同&#xff0c;比…

vue两个特性和什么是MVVM

一、什么是vue 1.构建用户界面 用vue往html页面中填充数据&#xff0c;非常的方便 2.框架 框架是一套线成的解决方案 vue的指令、组件&#xff08;是对ui结构的复用&#xff09;、路由、vuex 二、vue的特性 1.数据驱动视图 2.双向数据绑定 1.数据驱动视图 数据的变化会驱动…

面试题 之 vue

1.vue里怎样实现双向数据绑定&#xff1f; Viewmodel 中的Domlisteners 工具会帮我们检测页面上Dom元素的变化&#xff0c;如果有变化&#xff0c;则更改Model中的数据&#xff0c;更新model中的数据时&#xff0c;数据事件绑定工具会帮我们更新页面中的Dom元素 2.Vue的响应式原…

idea快速找到maven中冲突的依赖,解决依赖冲突

红色实线&#xff1a;冲突&#xff0c;红色虚线&#xff1a;依赖于同一个包的多版本 选择包&#xff0c;右键Excluede&#xff0c;排除 问题原因: 一个项目中需要jar包A和jar包B,而jar包A和jar包B都需要依赖jar包C,但A需要1.2.16版本的C,B需要1.2.17版本的C,这时候就可能会产…

升降梯人数识别摄像机

升降梯人数识别摄像机是一种智能监测设备&#xff0c;主要用于实时识别和计算升降梯内乘客的数量。通过搭载先进的图像识别技术和人工智能算法&#xff0c;该设备可以准确监测乘客进出数量&#xff0c;提供重要数据支持和信息反馈&#xff0c;帮助管理人员有效管理升降梯运行&a…

Java基础知识总结(第八篇):集合:Collection(List、Set)、Map、Collections 工具类

声明: 1. 本文根据韩顺平老师教学视频自行整理&#xff0c;以便记忆 2. 若有错误不当之处, 请指出 系列文章目录 Java基础知识总结&#xff08;第一篇&#xff09;&#xff1a;基础语法 Java基础知识总结&#xff08;第二篇&#xff09;&#x…

外汇110:交易中,是否真的存在确定性?

我们看问题的角度不同&#xff0c;得到的结果必然也是不一样的。我们不能否认任何一种可能性&#xff0c;但一切需要从逻辑出发。交易中&#xff0c;最大的确定性就是市场是不确定的&#xff0c;什么样的行情都可能发生。当然&#xff0c;绝对的确定性是不存在的&#xff0c;但…

九州未来深度参与元宇宙标准会议周

近日&#xff0c;元宇宙标准化工作组成立大会暨第一次全体委员会会议在浙江青田成功举办。本次会议由元宇宙标准化工作组主办&#xff0c;中国电子技术标准化研究院、中共青田县委 青田县人民政府承办&#xff0c;涵盖了《元宇宙参考架构》国家标准编制会、《工业元宇宙参考架构…

基于SpringBoot和Vue的房产销售系统的设计与实现

今天要和大家聊的是一款基于SpringBoot和Vue的房产销售系统的设计与实现 &#xff01;&#xff01;&#xff01; 有需要的小伙伴可以通过文章末尾名片咨询我哦&#xff01;&#xff01;&#xff01; &#x1f495;&#x1f495;作者&#xff1a;李同学 &#x1f495;&#x1f…

家庭影院触摸屏中应用的电容式触摸芯片

家庭影院的主要组成部分包括显示设备、音响设备、信号源和接线设备等。其中&#xff0c;显示设备通常采用高清电视或投影仪&#xff0c;音响设备包括功放、音箱、低音炮等&#xff0c;信号源可以是蓝光光盘、游戏机、有线电视、网络电视等多种媒体设备。 家庭影院的影像信号通…

SQL语句学习+牛客基础39SQL

什么是SQL&#xff1f; SQL (Structured Query Language:结构化查询语言) 是用于管理关系数据库管理系统&#xff08;RDBMS&#xff09;。 SQL 的范围包括数据插入、查询、更新和删除&#xff0c;数据库模式创建和修改&#xff0c;以及数据访问控制。 SQL语法 数据库表 一个…

QA测试开发工程师面试题满分问答6: 如何判断接口功能正常?从QA的角度设计测试用例

判断接口功能是否正常的方法之一是设计并执行相关的测试用例。下面是从测试QA的角度设计接口测试用例的一些建议&#xff0c;包括功能、边界、异常、链路、上下游和并发等方面&#xff1a; 通过综合考虑这些测试维度&#xff0c;并设计相应的测试用例&#xff0c;可以更全面地评…

使用pytorch构建带梯度惩罚的Wasserstein GAN(WGAN-GP)网络模型

本文为此系列的第三篇WGAN-GP&#xff0c;上一篇为DCGAN。文中仍然不会过多详细的讲解之前写过的&#xff0c;只会写WGAN-GP相对于之前版本的改进点&#xff0c;若有不懂的可以重点看第一篇比较详细。 原理 具有梯度惩罚的 Wasserstein GAN (WGAN-GP)可以解决 GAN 的一些稳定性…

OpenHarmony error: signature verification failed due to not trusted app source

问题&#xff1a;error: signature verification failed due to not trusted app source 今天在做OpenHarmony App开发&#xff0c;之前一直用的设备A在测试开效果&#xff0c;今天换成了设备B&#xff0c;通过DevEco Studio安装应用程序的时候&#xff0c;就出现错误&#xf…

蓝桥杯刷题-四平方和

四平方和 代码&#xff1a; from copy import deepcopy n int(input()) maxn int(5e6) 10 dic dict() for a in range(maxn):if a * a > n:breakfor b in range(a,maxn):if a * a b * b > n:breakif dic.get(a*ab*b) is None:dic[a*ab*b] (a,b) ans [maxn for _ …

基于springboot+vue+Mysql的教学视频点播系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

网络安全 | 什么是DDoS攻击?

关注WX&#xff1a;CodingTechWork DDoS-介绍 DoS&#xff1a;Denial of Service&#xff0c;拒绝服务。DDoS是通过大规模的网络流量使得正常流量不能访问受害者目标&#xff0c;是一种压垮性的网络攻击&#xff0c;而不是一种入侵手段。NTP网络时间协议&#xff0c;设备需要…

c++ 插值搜索-迭代与递归(Interpolation Search)

给定一个由 n 个均匀分布值 arr[] 组成的排序数组&#xff0c;编写一个函数来搜索数组中的特定元素 x。 线性搜索需要 O(n) 时间找到元素&#xff0c;跳转搜索需要 O(? n) 时间&#xff0c;二分搜索需要 O(log n) 时间。 插值搜索是对实例二分搜索的改进&#xff0c;…