我用canvas带你看一场流星雨
前言
最近总是梦见一些小时候的故事,印象最深刻的就是夏天坐在屋顶上,看着满天的繁星,一颗,两颗,三颗...不由自主地开始了数星星的过程。不经意间,一颗流星划过夜间,虽然只是转瞬即逝,但它似乎比夜空中的其它繁星更吸引着我。听老人说,看见流星的时候许愿,愿望是可以实现的,此时早已把数星星抛之脑后,开始期待着下一颗流星的出现。但是那天晚上,流星再也没有出现,这也成了自己小时候的一个遗憾。
今天,我决定用canvas为大家带来一场流星雨视觉盛宴。
如果这篇文章有帮助到你,️关注+点赞️鼓励一下作者,文章公众号首发,关注 前端南玖
第一时间获取最新文章~
需求分析
首先我们需要的元素有:夜空、满天繁星、流星雨。
满天繁星: 这个其实就是画上一个个点,然后不断的通过颜色交替,营造出一种星星闪烁的意境。
流星雨: 流星处于他自己的运动轨迹之中,当前的位置最亮,轮廓最清晰,而之前划过的地方离当前位置轨迹距离越远就越暗淡越模糊,其实它就是一个渐变的过程,恰巧canvas有方法可以创建一个沿参数坐标指定的直线的渐变。然后让它从右上向左下移动,这样就能营造一种流星雨的效果,同时实现动画的循环。
OK,需求分析结束,准备动手开干~
实现过程
1.绘制满天繁星
//创建一个星星对象
class Star {
constructor() {
this.x = windowWidth * Math.random(); //横坐标
this.y = 5000 * Math.random(); //纵坐标
this.text = "."; //文本
this.color = "white"; //颜色
}
//初始化
init() {
this.getColor();
}
//绘制
draw() {
context.fillStyle = this.color;
context.fillText(this.text, this.x, this.y);
}
}
//画星星
for (let i = 0; i < starCount; i++) {
let star = new Star();
star.init();
star.draw();
arr.push(star);
}
来看下此时的效果:
夜空中的满天繁星现在是有了,但是缺乏一点意境,我们得想办法让这些繁星都闪烁起来。
2.满天繁星闪起来
//创建一个星星对象
class Star {
constructor() {
this.x = windowWidth * Math.random(); //横坐标
this.y = 5000 * Math.random(); //纵坐标
this.text = "."; //文本
this.color = "white"; //颜色
}
// 获取随机颜色
getColor() {
let _r = Math.random();
if (_r < 0.5) {
this.color = "#333";
} else {
this.color = "white";
}
}
//初始化
init() {
this.getColor();
}
//绘制
draw() {
context.fillStyle = this.color;
context.fillText(this.text, this.x, this.y);
}
}
//画星星
for (let i = 0; i < starCount; i++) {
let star = new Star();
star.init();
star.draw();
arr.push(star);
}
//繁星闪起来
let t1
function playStars() {
for (let n = 0; n < starCount; n++) {
arr[n].getColor();
arr[n].draw();
}
t1 = requestAnimationFrame(playStars);
}
繁星闪烁的元素就在于这个getColor
方法,通过不断地切换星星的颜色,来达到星星闪烁的效果。
再来看看这时的效果:
此刻的自己就可以开始数星星了,一颗,两颗,三颗...
3.绘制流星
简单点理解,流星其实就是一条渐变的线段,当前的位置最亮,轮廓最清晰,而之前划过的地方离当前位置轨迹距离越远就越暗淡越模糊。
这里的关键API是createLinearGradient
,用于创建一个沿参数坐标指定的直线的渐变。
语法:
CanvasGradient ctx.createLinearGradient(x0, y0, x1, y1);
- x0:起点的 x 轴坐标。
- y0:起点的 y 轴坐标。
- x1:终点的 x 轴坐标。
- y1:终点的 y 轴坐标。
使用createLinearGradient()
方法初始化一个线性渐变。在这个线性渐变中添加三种颜色,达到一种渐变的效果来模拟出流星划过夜空的状态。
/**绘制流星**/
draw() {
//绘制一个流星的函数
context.save();
context.beginPath();
context.lineWidth = 1; //宽度
context.globalAlpha = this.alpha; //设置透明度
//创建横向渐变颜色,起点坐标至终点坐标
let line = context.createLinearGradient(
this.x,
this.y,
this.x + this.width,
this.y - this.height
);
//分段设置颜色
line.addColorStop(0, "white");
line.addColorStop(0.3, this.color1);
line.addColorStop(0.6, this.color2);
context.strokeStyle = line;
//起点
context.moveTo(this.x, this.y);
//终点
context.lineTo(this.x + this.width, this.y - this.height);
context.closePath();
context.stroke();
context.restore();
}
现在我们来看一看当年的那个流星:
4.流星划过夜空
流星有了,现在我们得想办法让它动起来。这里其实就是通过不断地计算位置来达到流星动起来的效果。
move() {
//清空流星像素
let x = this.x + this.width - this.offset_x;
let y = this.y - this.height;
context.clearRect(x - 3, y - 3, this.offset_x + 5, this.offset_y + 5);
//重新计算位置,往左下移动
this.countPos();
//透明度增加
this.alpha -= 0.002;
//重绘
this.draw();
}
现在,我们就可以看到当年的那颗流星了,是不是很激动。稍安勿躁,为了弥补当年的遗憾,这里决定来一场从未真实见过的流星雨。
5.流星雨
写到这里,实现流星雨其实就很简单了,我们只需要再多生成一些流星,为它们各自分配不同的坐标即可。
let t2
// 创建流星雨对象
class MeteorRain {
constructor() {
this.x = -1;
this.y = -1;
this.length = -1; //长度
this.angle = 30; //倾斜角度
this.width = -1; //宽度
this.height = -1; //高度
this.speed = 1; //速度
this.offset_x = -1; //横轴移动偏移量
this.offset_y = -1; //纵轴移动偏移量
this.alpha = 1; //透明度
this.color1 = ""; //流星的色彩
this.color2 = ""; //流星的色彩
}
init() {
//初始化
this.getPos();
this.alpha = 1; //透明度
this.getRandomColor();
//最小长度,最大长度
let x = Math.random() * 80 + 150;
this.length = Math.ceil(x);
x = Math.random() + 0.5;
this.speed = Math.ceil(x); //流星的速度
let cos = Math.cos((this.angle * 3.14) / 180);
let sin = Math.sin((this.angle * 3.14) / 180);
this.width = this.length * cos;
this.height = this.length * sin;
this.offset_x = this.speed * cos;
this.offset_y = this.speed * sin;
}
/**获取随机颜色函数**/
getRandomColor() {
let a = Math.ceil(255 - 240 * Math.random());
//中段颜色
this.color1 = "rgba(" + a + "," + a + "," + a + ",1)";
//结束颜色
this.color2 = "black";
}
/**重新计算流星坐标的函数**/
countPos() {
//
//往左下移动,x减少,y增加
this.x = this.x - this.offset_x;
this.y = this.y + this.offset_y;
}
/**获取随机坐标的函数**/
getPos() {
//
//横坐标
this.x = Math.random() * window.innerWidth; //窗口高度
//纵坐标
this.y = Math.random() * window.innerHeight; //窗口宽度
}
/**绘制流星**/
draw() {
//绘制一个流星的函数
context.save();
context.beginPath();
context.lineWidth = 1; //宽度
context.globalAlpha = this.alpha; //设置透明度
//创建横向渐变颜色,起点坐标至终点坐标
let line = context.createLinearGradient(
this.x,
this.y,
this.x + this.width,
this.y - this.height
);
//分段设置颜色
line.addColorStop(0, "white");
line.addColorStop(0.3, this.color1);
line.addColorStop(0.6, this.color2);
context.strokeStyle = line;
//起点
context.moveTo(this.x, this.y);
//终点
context.lineTo(this.x + this.width, this.y - this.height);
context.closePath();
context.stroke();
context.restore();
}
move() {
//清空流星像素
let x = this.x + this.width - this.offset_x;
let y = this.y - this.height;
context.clearRect(x - 3, y - 3, this.offset_x + 5, this.offset_y + 5);
//重新计算位置,往左下移动
this.countPos();
//透明度增加
this.alpha -= 0.002;
//重绘
this.draw();
}
}
//绘制流星
function playRains() {
for (let n = 0; n < rainCount; n++) {
// console.log(rains, "--");
let rain = rains[n];
rain.move(); //移动
if (rain.y > window.innerHeight) {
//超出界限后重来
context.clearRect(rain.x, rain.y - rain.height, rain.width, rain.height);
rains[n] = new MeteorRain();
rains[n].init();
}
}
t2 = requestAnimationFrame(playRains);
}
6.merge视觉盛宴
流星极短暂的星星是也,它不像恒星和行星那般耀眼,却用短暂的生命,划破夜空,用瞬间潇洒的弧线留住美丽的光辉。昙花和流星瞬间之美令我无法忘怀,大自然神奇的造物者给了我们许多的美,不论瞬间的还是永恒的,我们都要用真心去欣赏去品味。
通过合并前面五个步骤,我们就能够一睹这流星刹那间的交错,而后瞬间就穿透为永恒,只是一刻用生命幻化的美。
最后
今天的视觉盛宴就到这里了,想要查看源码的同学公众号回复流星雨~
我是南玖,我们下期见!!!
我用canvas带你看一场流星雨的更多相关文章
- 让Python带你看一场唯美的横飘雪!
“北国风光,千里冰封,万里雪飘”,这句诗描写了一句美丽肃静的风光图,恰逢昨天笔者这边也下了一场比较大的雪,要不今天就用Python带大家也来领略一次美丽的雪景? 开发环境 版本:Python3.6 系 ...
- 撩妹技能 get,教你用 canvas 画一场流星雨
开始 妹子都喜欢流星,如果她说不喜欢,那她一定是一个假妹子. 现在就一起来做一场流星雨,用程序员的野路子浪漫一下. 要画一场流星雨,首先,自然我们要会画一颗流星. 玩过 canvas 的同学,你画圆画 ...
- 【 全干货 】5 分钟带你看懂 Docker !
欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 作者丨唐文广:腾讯工程师,负责无线研发部地图测试. 导语:Docker,近两年才流行起来的超轻量级虚拟机,它可以让你轻松完成持续集成.自动交付 ...
- 51ak带你看MYSQL5.7源码3:修改代码实现你的第一个Mysql版本
从事DBA工作多年 MYSQL源码也是头一次接触 尝试记录下自己看MYSQL5.7源码的历程 目录: 51ak带你看MYSQL5.7源码1:main入口函数 51ak带你看MYSQL5.7源码2:编译 ...
- 51ak带你看MYSQL5.7源码2:编译现有的代码
从事DBA工作多年 MYSQL源码也是头一次接触 尝试记录下自己看MYSQL5.7源码的历程 目录: 51ak带你看MYSQL5.7源码1:main入口函数 51ak带你看MYSQL5.7源码2:编译 ...
- 51ak带你看MYSQL5.7源码1:main入口函数
从事DBA工作多年 MYSQL源码也是头一次接触 尝试记录下自己看MYSQL5.7源码的历程 目录: 51ak带你看MYSQL5.7源码1:main入口函数 51ak带你看MYSQL5.7源码2:编译 ...
- 51ak带你看MYSQL5.7源码4:实现SQL黑名单功能
博客迁移至: https://www.dboop.com/ 从事DBA工作多年 MYSQL源码也是头一次接触 尝试记录下自己看MYSQL5.7源码的历程 申明:个人Python编程很溜,但是C++还停 ...
- 一文带你看清HTTP所有概念(转)
一文带你看清HTTP所有概念 上一篇文章我们大致讲解了一下 HTTP 的基本特征和使用,大家反响很不错,那么本篇文章我们就来深究一下 HTTP 的特性.我们接着上篇文章没有说完的 HTTP 标头继 ...
- 一文带你看遍 JDK9~14 的重要新特性!
Java9 发布于 2017 年 9 月 21 日 .作为 Java8 之后 3 年半才发布的新版本,Java 9 带 来了很多重大的变化其中最重要的改动是 Java 平台模块系统的引入,其他还有诸如 ...
随机推荐
- 关于又拍云免费cdn全网加速服务的长期评测(各种踩坑)
原文转载自「刘悦的技术博客」 ( https://v3u.cn/a_id_128 ) 妇孺皆知,前端优化中最重要的优化手段之一就是cdn加速,所谓cdn加速就是采用更多的缓存服务器(CDN边缘节点), ...
- AOP实现切入
6.AOP实现切入 AOP为Aspect Oriented Programming的缩写,意为:面向切面编程 通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术 AOP是OOP的延续,也 ...
- Docker 13 Dockerfile
参考源 https://www.bilibili.com/video/BV1og4y1q7M4?spm_id_from=333.999.0.0 https://www.bilibili.com/vid ...
- JS判断两个数组的元素是否完全相等
1.使用ES6 新增的扩展运算符和Set新数据类型判断两个数组是否包含有相同的元素 var arr1 = ['green' , 'yellow' ,'blue' ,'red']; var arr2 = ...
- 深入解析Flutter下一代渲染引擎Impeller
作者 魏国梁:字节 Flutter Infra 工程师, Flutter Member,长期专注 Flutter 引擎技术 袁 欣:字节 Flutter Infra 工程师, 长期关注渲染技术发 ...
- [NOIP2018提高组] 保卫王国 (树链剖分+动态DP)
题面 题目链接-Luogu 题目链接-Loj(要加Freopen) 题解 什么是动态DP? OneInDark:你不需要知道这么多,你只需要知道是利用了广义矩阵乘法就够了! 广义矩乘 广义矩阵乘法,简 ...
- 【c语言简单算法】1-阶乘
求n的阶乘 算法要求 从键盘输入一个数,求出这个数的阶乘 代码实现 #include main() { double result=1; size_t n; scanf("%d", ...
- 第八篇:用css写一个登录界面
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- 第四十一篇:Vue生命周期(二)
好家伙,书接上回 上图:(Vue官网中Vue实例图片的下半张) 以下为解释: 5.1.1. mounted执行完后,表示整个Vue实例已经初始化完毕了; 此时,组件已经脱离了创建阶段;进入到运行阶段 ...
- 使用man手册查看内核函数
1.为避免man的一些功能缺失,先装好manpages-dev sudo apt-get install manpages-dev 2.安装mandocs的依赖包xmlto sudo apt-get ...