内容简介:流星是一种唯美的天文现象,我一度想用所学知识将它绘制,最近阅读MDN上的canvas教程得到启发,用一个canvas的长尾效果绘制流星……我们知道,canvas动画实现依赖于画布的重绘,通过不停的清空画布,绘制画布就能实现基本的动画效果。一般使用clearRect方法清除指定矩形区域,来实现重绘。长尾效果是使用透明的填充色代替clearRect方法来实现的。吐槽:由于录屏软件fps跟不上canvas所以这个gif图有点卡顿
流星是一种唯美的天文现象,我一度想用所学知识将它绘制,最近阅读MDN上的canvas教程得到启发,用一个canvas的长尾效果绘制流星……
什么是长尾效果?
我们知道,canvas动画实现依赖于画布的重绘,通过不停的清空画布,绘制画布就能实现基本的动画效果。一般使用clearRect方法清除指定矩形区域,来实现重绘。长尾效果是使用透明的填充色代替clearRect方法来实现的。
使用clearRect
吐槽:由于录屏软件fps跟不上canvas所以这个gif图有点卡顿
使用fillRect
1.透明度为1
可以看出透明度为1时,效果与清除效果一致,我们可以理解为在画布上又盖上了一画布 ,遮住了之前所画的内容所以和清除效果是一样的。
2.透明度为0
透明度为0的效果,则等价于没有清除画布的效果,此时运动的球体像一只尾巴不断变长的蛇,可以以此猜想,将透明度设为0~1之间,就能调整尾巴的长度,达到一个带尾巴的运动模糊效果。
3.长尾效果
因为不断盖上透明的画布,所以从绘制点到出发点就产生了一条颜色不断变浅的路径,给人视觉效果就是一个动态模糊。
流星
可以将流星解构为一个圆形和长尾效果:
肉眼效果:
实际效果:
封装页面形状
使用面向对象编程完成canvas绘制
月亮类
class Moon { constructor(x, y, ctx, r = 25) { this.x = x; this.y = y; this.ctx = ctx; this.r = r; } draw() { this.ctx.fillStyle = 'rgba(255,255,255,0.6)'; this.ctx.shadowBlur = this.r + 5; //光晕半径 this.ctx.shadowColor = "#fff"; // 光晕颜色 this.ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2); this.ctx.fill(); } } 复制代码
因为月亮是静止在页面上的,所以只有一个draw方法,月亮光晕的实现是把阴影和填充设为同一颜色,然后让阴影透明度大于填充透明度,就形成一个外发光的效果。
星星类
class Star extends Moon { constructor(x, y, ctx, r) { super(x, y, ctx, r); } draw() { this.ctx.fillStyle = 'rgba(255,255,255,0.8)'; this.ctx.beginPath(); this.ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI); this.ctx.closePath(); this.ctx.fill(); } move() { this.x += 0.08; if (this.x > meteorCanvas.width) { this.x = 0; } this.draw(); } } 复制代码
星星与月亮的唯一区别是可以移动,所以用星星类去继承月亮类,实现面向对象的继承与多态。
用星星缓慢的向右移动可以模拟地球自转带来的效果。
流星类
class Meteor extends Star { constructor(x, y, ctx, r,angle) { super(x, y, ctx, r); this.angle = angle; } draw() { this.ctx.fillStyle = '#ffffff'; this.ctx.rotate(this.angle); this.ctx.translate(100, -meteorCanvas.height / 1.5); this.ctx.beginPath(); this.ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI); this.ctx.closePath(); this.ctx.fill(); this.ctx.translate(-100, meteorCanvas.height / 1.5); this.ctx.rotate(-this.angle); } move() { this.x += 4; this.y += 1; if (this.x > meteorCanvas.width) { this.x = Math.random() * 5 this.y = -2 * meteorCanvas.height + Math.random() * meteorCanvas.height * 3; } this.draw(); } } 复制代码
同理用流星类去继承星星类。
注意的是,流星类与星星类运动的不同之处是流星划过天空有一个夹角,所以在绘制时将画布旋转了角度之后,需要回归原位。
为了让流星出现的位置不会太密集,我将流星在y轴出现的位置设置在-2倍画布高度到1倍画布高度之间,并在draw方法中将画布往上挪了画布高度的2/3(同理要将画布归位)。
绘制canvas
流星需要使用长尾效果渲染,星星需要clearRect重绘,月亮就只需要绘制一次。为了三种形状互不干扰,我分别使用了不同画布去渲染它们。
优点:互不干扰,绘制逻辑清晰,优化渲染。
源码
const meteorCanvas = document.getElementById('meteor'); const starCanvas = document.getElementById('star'); const moonCanvas = document.getElementById('moon'); const meteors = [], stars = []; meteorCanvas.width = document.body.clientWidth; meteorCanvas.height = document.body.clientHeight; starCanvas.width = document.body.clientWidth; starCanvas.height = document.body.clientHeight / 3; moonCanvas.width = document.body.clientWidth; moonCanvas.height = document.body.clientHeight / 3; const meteorCtx = meteorCanvas.getContext('2d'); const starCtx = starCanvas.getContext('2d'); const moonCtx = moonCanvas.getContext('2d'); init(); animate(); function init() { for (var i = 0; i < 4; i++) { meteors[i] = new Meteor(Math.random() * meteorCanvas.width, -2 * meteorCanvas.height + Math.random() * meteorCanvas.height * 3, meteorCtx, Math.floor(Math.random() * 2) + 1.5, Math.PI / 7); meteors[i].draw(); } for (var i = 0; i < 60; i++) { stars[i] = new Star(Math.random() * starCanvas.width, Math.random() * starCanvas.height, starCtx, Math.random()); stars[i].draw(); } moon = new Moon(moonCanvas.width - 50, 50, moonCtx) moon.draw(); } function animate() { starCtx.clearRect(0, 0, starCanvas.width, starCanvas.height); meteorCtx.fillStyle = `rgba(0, 0, 0, 0.1)`; meteorCtx.fillRect(0, 0, meteorCanvas.width, meteorCanvas.height); for (let meteor of meteors) meteor.move(); for (let star of stars) star.move(); requestAnimationFrame(animate); } function recover() { for (let meteor of meteors) meteor = null; for (let star of stars) star = null; moon = null; } window.onresize = function () { meteorCanvas.width = document.body.clientWidth; meteorCanvas.height = document.body.clientHeight; starCanvas.width = document.body.clientWidth; starCanvas.height = document.body.clientHeight / 3; moonCanvas.width = document.body.clientWidth; moonCanvas.height = document.body.clientHeight / 3; recover(); init(); } 复制代码
结语
陪你去看流星雨落在这地球上
让你的泪落在我肩膀
要你相信我的爱只肯为你勇敢……
文章随着《流星雨》的歌声,也走向了尾声。人生如流星划过,转瞬即逝,然而,流星易逝,真情永恒……
以上所述就是小编给大家介绍的《用canvas绘制流星夜空》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- ViewGroup 默认顺序绘制子 View,如何修改?什么场景需要修改绘制顺序?
- Shader 绘制基础图形
- css绘制特殊图形
- View 绘制流程分析
- CSS图形绘制总结
- Flutter绘制弯曲虚线
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
爱上Arduino
Massimo Banzi / 于欣龙、郭浩赟 / 人民邮电出版社 / 2012-10 / 38.00元
《硬件开源电子设计平台:爱上Arduino(第2版)》全面透彻地介绍了arduino的相关内容,它会给你带来许多项目的点子,并帮助你顺利地实现从开始策划直到完成安装的全过程。由于《硬件开源电子设计平台:爱上Arduino(第2版)》是arduino项目合作创始人massimobanzi所著,其中一定融入了创始人对开源硬件的独到见解。《硬件开源电子设计平台:爱上Arduino(第2版)》内容完全考虑......一起来看看 《爱上Arduino》 这本书的介绍吧!