『 canvas 特效』一文教你绘制绚丽的星空背景 TS + ES6

news2025/1/31 8:03:55

介绍

很久没有写关于 canvas 效果的文章了,刚好最近又学到了一个新的特效,使用 canvas 绘制多层次动态星空背景,今天就分享给大家。首先我们依旧来看一下最终实现的效果,如图所示:

由于录制 GIF 造成失帧,因此图片可能看不出完整的动画,完整的效果及代码可以拉到文章最底部来进行观赏。

在上图中可以简单的看出有层次的星空图在不断的变换,一会儿向右移动,一会儿又向左移动,最后还会进行旋转,那么这样的效果是如何实现的呢?咱们就一起来学习一下吧!

绘制动态星空图

因为这个效果是使用 canvas 来实现的,因此咱们就需要做一些基础的准备工作,由于这次是做背景效果,因此不需要在 html 中添加默认的标签,我们会通过代码来动态插入 canvas 标签。

首先还是需要准备好相关的 css 样式代码,涉及到的样式很简单,如下所示:

*{margin: 0; padding: 0;
}
body {background: #000; overflow: hidden;
} 

简单的设置好 css 代码后,接下来就需要添加基础的 TS 准备代码了同鞋悉的童鞋们可能都知道这里为啥要使用 TS 了吧,如果还不清楚的,可以去查看我之前写的文章,这里咱们依旧使用 TS + ES6 的语法来进行编写,基础的准备代码如下所示:

class StarrySky {canvas: HTMLCanvasElement;ctx: CanvasRenderingContext2D;constructor() {this.canvas = document.createElement('canvas') as HTMLCanvasElement;this.canvas.width = innerWidth;this.canvas.height = innerHeight;this.canvas.style.zIndex = '-1';this.ctx = this.canvas.getContext('2d');document.body.appendChild(this.canvas);}
} 

在基础的准备代码中,我们通过 document.createElement 创建了一个 canvas 标签,并给它设置了相关的宽高和层级,最后通过 document.body.appendChildcanvas 插入到 body 中。有了以上准备代码后,接下来咱们还需要把动画相关的代码也编写好,相关代码如下所示:

class StarrySky {...other code constructor () {...other code }draw() {this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);}animate() {requestAnimationFrame(() => this.animate());this.draw();}
} 

其实现在 canvas 上面已经有了动画了,只是因为还没有绘制内容,因此看不到效果,接下来咱们就该添加星空效果了。

星空效果其实就是很多不同的粒子展示在 canvas 中,因此咱们需要先创建一个 Particle 粒子类,然后通过生成N个粒子,从而在 canvas 中展示,一起来看一下 Particle 粒子类的相关代码吧,如下:

class Particle {x: number;y: number;vx: number;w: number;h: number;ctx: CanvasRenderingContext2D;constructor(width: number, height: number, ctx: CanvasRenderingContext2D) {this.w = width;this.h = height;this.ctx = ctx;this.x = Math.random() * width;this.y = Math.random() * height;this.vx = Math.random();}update() {this.x += this.vx * 3;if (this.x > this.w) {this.x = 0;}}draw() {this.ctx.beginPath();this.ctx.arc(this.x, this.y, 1 + this.vx, 0, Math.PI * 2);this.ctx.fillStyle = `rgba(255, 255, 255, ${this.vx})`;this.ctx.fill();}
} 

Particle 粒子类中,我们添加了三个随机值,并在 update 方法中不断更新 x 值的数值,在前面的文章中讲过,我们要改变某个元素的位置时,只需要改变它沿 x轴y轴 的属性值即可,因此这里也是一样的;而在 draw 方法中,我们通过 ctx.arc() 方法来绘制每一个粒子圆,并通过 fillStyle 设置粒子的颜色,当 this.vx 值越小时,这个粒子的透明度越低,这样看起来就会显得非常有层次感。

有了 Particle 粒子类,接下来咱们就需要在前面的 StarrySky 类中进行使用了。首先咱们还要对 StarrySky 类中的 constructor 添加更多的属性,代码如下:

class StarrySky {canvas: HTMLCanvasElement;ctx: CanvasRenderingContext2D;particles: Particle[];count: number;actions: string[];action: number;constructor() {...other codethis.particles = [];this.count = 1000;this.animate();}
} 

可以看到我们在 constructor 中新增了一个 particles 数组,它主要用于存储生成的粒子类,然后我们需要在 StarrySky 类的 draw 方法中生成粒子,一起来看代码:

class StarrySky {...other codedraw() {this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);if (this.particles.length < this.count) {this.particles.push(new Particle(this.canvas.width, this.canvas.height, this.ctx));}for (let i in this.particles) {const p = this.particles[i];p.update();p.draw();}}
} 

当我们在 StarrySky 类的 draw 方法中动态生成粒子,并调用对应的 updatedraw 方法时,最终可以看到如下效果:

通过上图咱们可以看到一幅有层次的星空图已经绘制回来了,但是目前的星空只会向右进行移动,效果还是有些单调,因此咱们接下来通过代码实现点击任何区域让星空的移动能够变换方向,那么该如何实现呢?

变幻的星空图

在前面咱们已经通过代码实现了有层次的星空背景绘制了,但是目前可以看到星空的移动只有一个方法,那么该如何修改星空的移动方法呢?还记得在 Particle 星空类 中的 update 方法吗?在 update 方法中,我们是通过修改当前 this.x 的值从而让粒子实现移动的,因此咱们可以从 update 方法入手,代码如下:

class Particle {...other codeupdate(direction = 'right') {switch (direction) {case 'right':this.x += this.vx * 3;if (this.x > this.w) this.x = 0;break;case 'left':this.x -= this.vx * 3;if (this.x < 0) this.x = this.w;break;case 'up':this.y -= this.vx * 3;if (this.y < 0) this.y = this.h;break;case 'down':this.y += this.vx * 3;if (this.y > this.h) this.y = 0;break;}}
} 

通过修改 Particle 类的 update 方法可以看出,我们通过一个变量来判断当前移动的方向,并且设置每个方法不同的值,接下来我们还需要继续修改 StarrySky 类,这样咱们才能实现星空方法的不断变换,一起来看代码:

class StarrySky {...other codeactions: string[];action: number;constructor() {...other codethis.actions = ['right', 'left', 'up', 'down', 'around'];this.action = 0;this.animate();this.event();}event() {document.body.addEventListener('click', () => {this.action++;this.action = this.action % this.actions.length;});}draw() {...other codefor (let i in this.particles) {const p = this.particles[i];p.update(this.actions[this.action]);p.draw();}}
} 

StarrySky 类的 constructor 方法中,我们添加了一个 actions 数组,里面包含了上下左右以及旋转五个值,然后还添加了一个 action 变量,用于表示当前是第几个,并且通过 document.body.addEventListenerbody 添加了一个 click 事件,当 body 被点击时,修改当前的 action,从而改变星空的移动方向。最后在 StarrySky 类的 draw 方法中,调用 Particle 类的 update 方法时,将当前的移动方向传入即可实现改变星空方向变换的效果了。

当然,咱们还剩下最后一步,还记得上面的 actions 中咱们总共写了五个值,但是在 Particle 类的 update 方法中,咱们只添加了四个 case,因此还剩下最后一个旋转的 case 需要编写,代码如下:

class Particle {...other codeupdate(direction = 'right') {switch (direction) {...other codecase 'around':let deg = Math.atan2(this.y - this.h / 2,this.x - this.w / 2);let r = Math.sqrt(Math.pow(this.x - this.w / 2, 2) + Math.pow(this.y - this.h / 2, 2))this.x = r * Math.cos(deg + this.vx / 200) + this.w / 2;this.y = r * Math.sin(deg + this.vx / 200) + this.h / 2;break;}}
} 

casearound 时,星空需要变换为渲染的形式,咱们就需要找到旋转的角度和半径,根据勾股定理和三角函数相关的知识,最终获取到当前移动的 xy 的值。完整的代码及实现效果如下所示:

总结

总的来说,这个效果还是很不错的,并且还添加了相关的交互事件,实现的原理也很简单,有兴趣的童靴可以自己实现一下,本期的内容到此结束。

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

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

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

相关文章

实战三十三:STAMP算法实现商品推荐实战 代码+数据

1.案例知识点 推荐系统任务描述:通过用户的历史行为(比如浏览记录、购买记录等等)准确的预测出用户未来的行为;好的推荐系统不仅如此,而且能够拓展用户的视野,帮助他们发现可能感兴趣的却不容易发现的item;同时将埋没在长尾中的好商品推荐给可能感兴趣的用户。STAMP推荐…

记录Win7系统下的node版本升级过程及后续项目启动所遇问题

目录 Windows7系统如何“安装”高版本Node 后续项目运行启动时遇到的问题 Bug &#xff1a; 背景 &#xff1a; 银行内网开发&#xff0c;接到一个新的项目开发&#xff0c;但前提因为开发所使用的云桌面更换了&#xff0c;由原来的Windows10系统降到了现在的Windows7系统&am…

【自媒体必备】AI文字转语音,支持多种人声选择,在线生成一键导出【电脑永久版】

AI文字转语音&#xff0c;支持多种人声选择&#xff0c;在线生成一键导出 软件是电脑版安装版本&#xff0c;大家根据自己安软件的习惯进行安装即可&#xff0c;安装后打开软件即可&#xff0c;且完全免费。语音选择方面就有晓晓、云扬、云希等等常用选择。点击后面的播放按钮…

腾讯大佬告诉你,写Python到底用什么IDE合适

不管你是 Python 新手还是老鸟&#xff0c;肯定纠结过一个问题&#xff1a; 到底用什么编辑器写 Python 代码好&#xff1f; 为此&#xff0c;我们调查了数十位鹅厂程序猿们爱用的 Python IDE&#xff0c;从他们对每款编辑器的看法中&#xff0c;也许能给你一点启示&#xff1…

我对程序员做副业有这几点建议

疫情这几年&#xff0c;“副业”的概念非常火。各大平台上都在说“副业赚钱”、“副业是刚需”等相关的内容、视频&#xff0c;有免费的&#xff0c;还有付费观看的。 其实在2020年疫情之前&#xff0c;大家对于“副业”并没有很关心。 从百度指数上可以明显看出&#xff0c;…

【笔记】Pade Approximations

1 pade approximations是什么&#xff1f; Pad Approximations是一种对pure delay使用常规传函进行近似替换的方法&#xff0c;matlab提供了的一个库函数pade。 在传递函数中&#xff0c;用e−τse^{-\tau s}e−τs表示纯延时pure delay环节&#xff0c;它对信号的幅值不会有任…

微服务框架 SpringCloud微服务架构 微服务面试篇 54 微服务篇 54.2 Nacos的服务注册表结构是怎样的? 54.2.1 多级存储模型

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 微服务面试篇 文章目录微服务框架微服务面试篇54 微服务篇54.2 Nacos的服务注册表结构是怎样的&#xff1f;54.2.1 多级存储模型54 微服务篇…

《Redis实战篇》四、分布式锁

文章目录4.1 基本原理和实现方式对比4.2 Redis分布式锁的实现核心思路4.3 实现分布式锁版本一4.4 Redis分布式锁误删情况说明4.5 解决Redis分布式锁误删问题4.6 分布式锁的原子性问题4.7 Lua脚本解决多条命令原子性问题4.8 利用Java代码调用Lua脚本改造分布式锁4.1 基本原理和实…

Oracle时间函数

Oracle 时间函数INTERVAL间隔一年间隔一天间隔一小时间隔一分钟NUMTODSINTERVAL间隔一天间隔一小时间隔一分钟MyBatis xml 中计算时间间隔需要处理的问题INTERVAL 函数整合到 MyBatis$ 符号拼接 sql 源码追踪NUMTODSINTERVAL 函数整合到 MyBatisOracle 时间间隔函数有INTERVAL、…

%25 express 中的中间件

1、express 中的中间件 &#xff08;1&#xff09;、当客户端的请求到达 express 服务器后&#xff0c;会连续调用多个中间件&#xff0c;对请求进行预处理 &#xff08;2&#xff09;、本质是一个 function 处理函数(与普通路由函数相比&#xff0c;形参中多了 next 参数&…

深入分析广告和电商结合的逻辑,针对性推出一套广告电商方案

大家好&#xff0c;我是林工&#xff0c;本期林工给大家深入分析广告和电商结合的逻辑&#xff0c;针对性推出一套广告电商方案。 对于消费者来说&#xff0c;广告要发挥引导、刺激并满足消费者需求的作用&#xff0c;首先要使广告传播的信息引人注意&#xff0c;激发消费者购…

掌握Qt构造系统

1. 前言 ​对于日常在Qt或者Visual Studio等**集成开发环境&#xff08;即IDE&#xff09;**中编码的开发者来说&#xff0c;构造系统通常是比较容易被忽略的部分。集成开发环境配置简单&#xff0c;界面友好&#xff0c;可以提高开发效率。 ​随着对开发环境的深度使用&…

2.0、Hibernate-级联关系

2.0、Hibernate-级联关系 级联关系 中 最为常见的就是 -> 1、一对多关系 &#xff1a;一个消费者可以购买多个商品 -> 生成多个订单; 消费者是1&#xff0c;订单是多 那么在数据库中&#xff0c;1 的一方是主表&#xff0c;多的一方是从表&#xff0c;通过主外键关系去维…

C# Winform 三层架构

一、介绍 三层架构是 C# 桌面开发中比较常用的框架&#xff0c;是由 表示层&#xff08;UI&#xff09;、业务逻辑层&#xff08;BLL&#xff09;和数据访问层&#xff08;DAL&#xff09;三层架构组成&#xff0c;目的是为了 “高内聚&#xff0c;低耦合”。开发人员分工更明…

深度学习入门(六十五)循环神经网络——序列到序列学习(seq2seq)

深度学习入门&#xff08;六十五&#xff09;循环神经网络——序列到序列学习&#xff08;seq2seq&#xff09;前言循环神经网络——序列到序列学习&#xff08;seq2seq&#xff09;课件机器翻译seq2seq编码器-解码器细节训练衡量生成序列的好坏的BLEU总结教材1 编码器2 解码器…

为什么世界500强企业都要求员工必须学Python编程语言?

行业的更迭与进化并非虚无缥缈&#xff0c;它就发生在我们每天的生活中。当代最具竞争力的技能是什么&#xff1f;高盛、JP 摩根、麦肯锡等顶级公司纷纷给出了答案——编程。 今年新加入摩根大通的全部资产管理分析师必须强制学习编程语言&#xff0c;Python。 摩根大通资产管…

【Java异常】处理异常

一&#xff1a;异常的处理&#xff1a;抓抛处理 过程一&#xff1a;"抛":程序在正常执行的过程中&#xff0c;一旦出现异常&#xff0c;就会在异常代码处生成一个对应异常类的对象并将此对象抛出一旦抛出对象以后&#xff0c;其后的代码不再执行 过程二&#xff1a;&…

科班演员陶弈菱:北京电影学院的系统学习是我艺术道路上最大的底气

今天的记忆是带着海水的味道………”12月18日&#xff0c;在年末岁尾之际&#xff0c;徽风皖韵熏陶下成长的新生代演员陶奕菱再次来到海南三亚&#xff0c;受邀参加第四届海南岛国际电影节&#xff0c;迫不及待地跑向沙滩面朝大海&#xff0c;感受多姿多彩的魅力三亚。 优雅端…

在 React 中应用设计模式:策略模式

这篇文章是关于我们许多人在 React 和前端开发中遇到的一个问题&#xff08;有时甚至没有意识到这是一个问题&#xff09;&#xff1a;在不同的组件、钩子、实用程序等中实现了一段逻辑。 让我们深入了解问题的详细信息以及如何解决它。正如标题所暗示的&#xff0c;我们将使用…

DPDK-收包完整过程

本篇博客作为自己了解dpdk收包过程的一个记录。在写时发现已经有很多写DPDK收包过程的博客了&#xff0c;但还是决定自己写一遍。 DPDK收包分为两个阶段&#xff0c;首先是DMA将数据包从网卡搬运到内存&#xff0c;然后是调用dpdk提供的接口rte_eth_rx_burst去取。但是具体是怎…