Canvas生成动画---显示一组彩色气泡

 一、JS版本

<!--* @Author: LYM* @Date: 2024-07-26 13:51:47* @LastEditors: LYM* @LastEditTime: 2024-07-26 16:14:40* @Description: Please set Description
-->
<!DOCTYPE html>
<html>
<head><title>canvas动态气泡</title><style>body {margin: 0;overflow: hidden;}#jellyCanvas {display: block;width: 100%;height: 100%;}.container {width: 780px;height: 780px;margin: 0 auto;border-radius: 99px;filter: blur(64px);opacity: 1;}</style>
</head>
<body><div class="container"><canvas id="jellyCanvas"></canvas></div><script>// 获取Canvas元素和2D绘图上下文const canvas = document.getElementById('jellyCanvas');const ctx = canvas.getContext('2d');// 定义泡泡数组const bubbles = [];// 定义泡泡数量const numBubbles = 5;// 定义泡泡最大半径和最小半径const maxRadius = 50;const minRadius = 20;// 定义泡泡颜色const colors = ['#45abb5', '#abb3ff', '#73d4c7', '#abb3ff', '#455ed4'];// 定义一个函数来生成随机数function random(min, max) {return Math.random() * (max - min) + min;}// 设置2秒完成该动画const animationDuration = 2000;let startTime;// 定义一个构造函数来创建泡泡对象function Bubble(x, y, radius, color) {this.x = x;this.y = y;this.radius = radius;this.color = color;this.dx = random(-2, 2);this.dy = random(-2, 2);// 绘制泡泡this.draw = function () {ctx.beginPath();ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);// ctx.shadowColor = this.color;// ctx.shadowBlur = 20;ctx.fillStyle = this.color;ctx.fill();ctx.closePath();};// 更新泡泡位置this.update = function () {this.x += this.dx;this.y += this.dy;// 碰撞检测if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {this.dx = -this.dx;}if (this.y + this.radius > canvas.height || this.y - this.radius < 0) {this.dy = -this.dy;}};}// 创建泡泡并添加到数组中for (let i = 0; i < numBubbles; i++) {const x = random(maxRadius, canvas.width - maxRadius);const y = random(maxRadius, canvas.height - maxRadius);const radius = random(minRadius, maxRadius);const color = colors[Math.floor(random(0, colors.length))];bubbles.push(new Bubble(x, y, radius, color));}// 动画循环function animate() {// requestAnimationFrame(animate);  // 动画速度太快 但是性能比较好setInterval(function(){ctx.clearRect(0, 0, canvas.width, canvas.height);// 绘制和更新每个泡泡for (let i = 0; i < numBubbles; i++) {bubbles[i].draw();bubbles[i].update();}}, 100)}// 启动动画animate();</script>
</body>
</html>
<!DOCTYPE html>  
<html lang="en">  
<head>  
<meta charset="UTF-8">  
<title>彩色气泡动画</title>  
<style>  canvas {  border: 1px solid black;  }.container {width: 800px;height: 800px;margin: 0 auto;border-radius: 50%;/* filter: blur(64px); */opacity: 1;}
</style>  
</head>  
<body><div class="container"><canvas id="bubbleCanvas" width="800" height="800"></canvas>  </div><script>class Bubble {  constructor(x, y, radius, color, speedX, speedY) {  this.x = x;  this.y = y;  this.radius = radius;  this.color = color;  this.speedX = speedX;  this.speedY = speedY;  }  update() {  this.x += this.speedX;  this.y += this.speedY;  // 气泡碰到边界时反弹  if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {  this.speedX = -this.speedX;  }  if (this.y + this.radius > canvas.height || this.y - this.radius < 0) {  this.speedY = -this. Speedy;  }  }  draw() {  ctx.beginPath();  ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);  ctx.fillStyle = this.color;  ctx.fill();  ctx.closePath();  }  
}  const canvas = document.getElementById('bubbleCanvas');  
const ctx = canvas.getContext('2d');  const bubbles = [];  // 初始化气泡  
function initBubbles() {  for (let i = 0; i < 5; i++) {  let radius = Math.random() * 50 + 40; // 气泡大小  let x = Math.random() * (canvas.width - radius * 2) + radius;  let y = Math.random() * (canvas.height - radius * 2) + radius;  let color = `rgba(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, 0.8)`;  let speedX = (Math.random() - 0.5) * 2;  let speedY = (Math.random() - 0.5) * 2;  bubbles.push(new Bubble(x, y, radius, color, speedX, speedY));  }  
}  initBubbles();  function animate() {  ctx.clearRect(0, 0, canvas.width, canvas.height);  bubbles.forEach(bubble => {  bubble.update();  bubble.draw();  });  requestAnimationFrame(animate);  
}  animate();
</script>  
</body>  
</html>

 

二、TS版本 

/** @Author: LYM* @Date: 2024-07-26 15:08:43* @LastEditors: LYM* @LastEditTime: 2024-07-26 17:18:06* @Description: 生成动画显示一组彩色气泡*/
/*** 生成动画显示一组彩色气泡* @param params 参数对象,用于控制气泡的生成和显示* @param params.el HTML canvas元素* @param params.colors 气泡的颜色数组,默认为粉色系颜色* @param params.numBubbles 生成的气泡数量,默认为5* @param params.maxRadius 气泡的最大半径,默认为50* @param params.minRadius 气泡的最小半径,默认为20*/
export const generatedBubbles = (params: {el: anycolors: Array<string>numBubbles: numbermaxRadius: numberminRadius: number
}) => {const {el,colors = ['#45abb5', '#47c2b5', '#73d4c7', '#a3f7ed', '#455ed4'],numBubbles = 5,maxRadius = 50,minRadius = 20,} = params ?? {}// 获取Canvas元素和2D绘图上下文const canvas = el as anyconst ctx = canvas.getContext('2d')// 定义泡泡数组const bubbles = [] as Array<any>// 定义一个函数来生成随机数const random = (min: number, max: number) => {return Math.random() * (max - min) + min}// 定义一个构造函数来创建泡泡类class Bubble {x: numbery: numberradius: numbercolor: stringconstructor(x: number, y: number, radius: number, color: string) {this.x = xthis.y = ythis.radius = radiusthis.color = color}dx = random(-2, 2)dy = random(-2, 2)// 绘制泡泡draw() {ctx.beginPath()ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2)// ctx.shadowColor = this.color;// ctx.shadowBlur = 20;ctx.fillStyle = this.colorctx.fill()ctx.closePath()}// 更新泡泡位置update() {this.x += this.dxthis.y += this.dy// 碰撞检测if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {this.dx = -this.dx}if (this.y + this.radius > canvas.height || this.y - this.radius < 0) {this.dy = -this.dy}}}// 创建泡泡并添加到数组中for (let i = 0; i < numBubbles; i++) {const x = random(maxRadius, canvas.width - maxRadius)const y = random(maxRadius, canvas.height - maxRadius)const radius = random(minRadius, maxRadius)const color = colors[Math.floor(random(0, colors.length))]bubbles.push(new Bubble(x, y, radius, color))}// 动画循环const animate = () => {// requestAnimationFrame(animate);  // 动画速度太快 但是性能比较好setInterval(() => {ctx.clearRect(0, 0, canvas.width, canvas.height)// 绘制和更新每个泡泡for (let i = 0; i < numBubbles; i++) {bubbles[i].draw()bubbles[i].update()}}, 90)}// 启动动画animate()
}
/** @Author: LYM* @Date: 2024-07-26 18:36:17* @LastEditors: LYM* @LastEditTime: 2024-07-26 18:59:04* @Description: 生成气泡动画*/
/*** 生成气泡动画** @param params 生成气泡动画的参数* @param params.el 绑定的canvas元素* @param params.colors 气泡颜色数组,默认为['#45abb5', '#abb3ff', '#47c2b5', '#5e73ff', '#8e59d7']* @param params.numBubbles 气泡数量,默认为5* @param params.baseRadius 基础半径,默认为10* @param params.speed 气泡移动速度,默认为1*/
export const generatedBubbles = (params: {el: anycolors: Array<string>numBubbles: numberbaseRadius: numberspeed: number
}) => {const {el,colors = ['#45abb5', '#abb3ff', '#47c2b5', '#5e73ff', '#8e59d7'],numBubbles = 5,baseRadius = 10,speed = 1,} = params ?? {}class Bubble {x: numbery: numberradius: numbercolor: stringspeedX: numberspeedY: numberconstructor(x: number,y: number,radius: number,color: string,speedX: number,speedY: number) {this.x = xthis.y = ythis.radius = radiusthis.color = colorthis.speedX = speedXthis.speedY = speedY}update() {this.x += this.speedXthis.y += this.speedY// 气泡碰到边界时反弹if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {this.speedX = -this.speedX}if (this.y + this.radius > canvas.height || this.y - this.radius < 0) {this.speedY = -this.speedY}}draw() {ctx.beginPath()ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false)ctx.fillStyle = this.colorctx.fill()ctx.closePath()}}// 获取Canvas元素和2D绘图上下文const canvas = el as anyconst ctx = canvas.getContext('2d')// 定义泡泡数组const bubbles = [] as Array<any>// 初始化气泡for (let i = 0; i < numBubbles; i++) {const radius = Math.random() * baseRadius + 20 // 气泡大小const x = Math.random() * (canvas.width - radius * 2) + radiusconst y = Math.random() * (canvas.height - radius * 2) + radiusconst color = `rgba(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, 0.8)`const speedX = (Math.random() - 0.5) * speedconst speedY = (Math.random() - 0.5) * speedbubbles.push(new Bubble(x, y, radius, colors[i] ?? color, speedX, speedY))}const animate = () => {ctx.clearRect(0, 0, canvas.width, canvas.height)bubbles.forEach(bubble => {bubble.update()bubble.draw()})requestAnimationFrame(animate)}animate()
}

三、效果

四、解释

  1. Bubble 类:定义了气泡的属性(位置、半径、颜色、速度)和方法(更新位置、绘制)。
  2. initBubbles 函数:初始化一组气泡,随机设置它们的位置、大小、颜色和速度。
  3. animate 函数:清除画布,更新每个气泡的位置,绘制所有气泡,并使用requestAnimationFrame递归调用自身以创建动画效果。

这个简单的例子展示了如何在<canvas>上创建和动画化一组彩色气泡。你可以根据需要调整气泡的数量、大小、颜色、速度等属性,以及边界反弹的行为。

 

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

JMeter接口测试:测试中奖概率!

介绍 Apache JMeter 是 Apache 组织基于 Java 开发的压力测试工具&#xff0c;用于对软件做压力测试。JMeter 最初被设计用于 Web 应用测试&#xff0c;但后来扩展到了其他测试领域&#xff0c;可用于测试静态和动态资源&#xff0c;如静态文件、Java 小服务程序、CGI 脚本、J…

学习日志:JVM垃圾回收

文章目录 前言一、堆空间的基本结构二、内存分配和回收原则对象优先在 Eden 区分配大对象直接进入老年代长期存活的对象将进入老年代主要进行 gc 的区域空间分配担保 三、死亡对象判断方法引用计数法可达性分析算法引用类型总结1&#xff0e;强引用&#xff08;StrongReference…

联想教育电脑硬盘保护同传EDU系统使用简明教程

目录 一、原理概述 二、简明使用方法 1、软件下载 2、开机引导 3、开始安装 4、使用 &#xff08;1&#xff09;进入底层 &#xff08;2&#xff09;进行分区设置 &#xff08;3&#xff09;系统设置 &#xff08;4&#xff09;安装硬盘保护驱动 &#xff08;5&…

VUE3学习第三篇:报错记录

1、在我整理好前端代码框架后&#xff0c;而且也启动好了对应的后台服务&#xff0c;访问页面&#xff0c;正常。 2、报错ReferenceError: defineModel is not defined 学到这里报错了 在vue网站的演练场&#xff0c;使用没问题 但是在我自己的代码里就出问题了 3、watchEffec…

【YOLOv5/v7改进系列】引入中心化特征金字塔的EVC模块

一、导言 现有的特征金字塔方法过于关注层间特征交互而忽视了层内特征的调控。尽管有些方法尝试通过注意力机制或视觉变换器来学习紧凑的层内特征表示&#xff0c;但这些方法往往忽略了对密集预测任务非常重要的被忽视的角落区域。 为了解决这个问题&#xff0c;作者提出了CF…

算法与算法分析

目录 一.前言 二.算法的特性和要求 三.分析算法--时间效率 四. 分析算法--空间效率 一.前言 算法就是对特定问题求解方法和步骤的一种描述&#xff0c;它是指令的有限序列。其中&#xff0c;每个指令表示一个或多个操作。总而言之&#xff0c;我们数据结构就是通过算法实现操…

微信小程序之调查问卷

一、设计思路 1、界面 调查问卷又称调查表&#xff0c;是以问题的形式系统地记载调查内容的一种形式。微信小程序制作的调查问卷&#xff0c;可以在短时间内快速收集反馈信息。具体效果如下所示&#xff1a; 2、思路 此调查问卷采用服务器客户端的方式进行设计&#xff0c;服…

力扣141环形链表问题|快慢指针算法详细推理,判断链表是否有环|龟兔赛跑算法

做题链接 目录 前言&#xff1a; 一、算法推导&#xff1a; 1.假设有环并且一定会相遇&#xff0c;那么一定是在环内相遇&#xff0c;且是快指针追上慢指针。 2.有环就一定会相遇吗&#xff1f;快指针是每次跳两步&#xff0c;有没有可能把慢指针跳过去&#xff1f; 3.那一定…

大模型算法备案流程最详细说明【流程+附件】

文章目录 一、语料安全评估 二、黑盒测试 三、模型安全措施评估 四、性能评估 五、性能评估 六、安全性评估 七、可解释性评估 八、法律和合规性评估 九、应急管理措施 十、材料准备 十一、【线下流程】大模型备案线下详细步骤说明 十二、【线上流程】算法备案填报…

算法-BFS搜索

题目一 解题思路 比较标准的暴力搜索空间换时间的策略 二维数组map表示具体地图&#xff0c;far表示遍历过程中某点到起点的距离。 队列 q 表示在遍历过程中当前距离的所以节点坐标。 每次的节点寻找其上下左右四个方向可以继续前进的点&#xff08;这里在过程中会发生两个…

pyqt designer使用spliter

1、在designer界面需要使用spliter需要父界面不使用布局&#xff0c;减需要分割两个模块选中&#xff0c;再点击spliter分割 2、在分割后&#xff0c;再对父界面进行布局设置 3、对于两边需要不等比列放置的&#xff0c;需要套一层 group box在最外层进行分割

cesium获取模型的数据包含b3dm和cmpt

getreadyPromise()方法在模型加载完成后调用 url为模型地址 // tileset模型 function tilesetM(url) {tileset viewer.scene.primitives.add(new Cesium.Cesium3DTileset({// url: ../../public/asd/tileset.json,url: url,// type: "3dtiles",maximumScreenSpace…

构建稳固与安全的网络环境:从微软蓝屏事件看软件更新流程与应急响应

“微软蓝屏”事件暴露了网络安全哪些问题&#xff1f; 近日&#xff0c;由微软视窗系统软件更新引发的全球性“微软蓝屏”事件&#xff0c;不仅让科技领域为之震动&#xff0c;更是一次对全球IT基础设施韧性与安全性的深刻检验。这次事件源于美国电脑安全技术公司“众击”的一…

浅谈Mike11中常见的错误及解决方法

前言&#xff1a; 小编对MIKE11比较熟悉&#xff0c;今天为大家总结了mike11中常见的一些错误及解决方法分享给大家。 一&#xff1a;could not open license file 当你打开MIKE11出现这种情况是一般是试用版的License没安装成功&#xff0c;或者安装杀毒软件导致License被当…

SEO与数据中心代理IP的结合能带来哪些便利?

本文将探讨将SEO与数据中心代理IP结合所带来的好处&#xff0c;以及如何利用这种组合来提升网站在搜索引擎中的排名和可见性。 1. 数据中心代理IP的作用和优势 数据中心代理IP指的是由数据中心提供的IP地址&#xff0c;用于隐藏真实服务器的位置和身份。与其他类型的代理IP相…

网络安全常见错误及解决办法(更新中)

# 开启代理&#xff0c;无法连接网络 把代理关掉 # 上一秒还在安装tree&#xff0c;下一秒xshell就连接不上了 —》sshd服务的key这个文件权限过高&#xff0c;跟装tree没有关系&#xff0c;装一个epel 源&#xff0c;epel-release​ 部分命令&#xff1a;chmod 600 /etc/ssh…

可见性::

目录 定义&#xff1a; 解决方法&#xff1a; ①使用synchronized实现缓存和内存的同步 修改一&#xff1a; 加入语句&#xff1a; 代码&#xff1a; 修改2&#xff1a; 在代码块中加入&#xff1a; 代码&#xff1a; 执行结果&#xff1a; 原因&#xff1a; ②使用…

hot100-双指针

283移动零 11盛最多水的容器 暴力解法&#xff08;超时了&#xff09;、双指针法 15三数之和 42接雨水

如何关闭页面报错的遮罩层

问题&#xff1a;如何关闭页面报错的遮罩层 解决方法&#xff1a; 在vue.config.js中添加如下配置&#xff0c;重启项目即可 module.exports defineConfig({devServer: {client: {overlay: false,},}})

Android P Input设备变化监听 Storage设备变化监听

InputManager.java中实现了InputDeviceListener接口&#xff0c;只需要新建一个类 implements InputDeviceListener &#xff0c;并且将类实例化注册给InputManager.getInstance().registerInputDeviceListener即可。 StorageManager同理 StorageManager中会调用StorageEventL…