JavaScript canvas绘制流星雨
前端南玖 人气:0前言
最近总是梦见一些小时候的故事,印象最深刻的就是夏天坐在屋顶上,看着满天的繁星,一颗,两颗,三颗...不由自主地开始了数星星的过程。不经意间,一颗流星划过夜间,虽然只是转瞬即逝,但它似乎比夜空中的其它繁星更吸引着我。听老人说,看见流星的时候许愿,愿望是可以实现的,此时早已把数星星抛之脑后,开始期待着下一颗流星的出现。但是那天晚上,流星再也没有出现,这也成了自己小时候的一个遗憾。
今天,我决定用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视觉盛宴
流星极短暂的星星是也,它不像恒星和行星那般耀眼,却用短暂的生命,划破夜空,用瞬间潇洒的弧线留住美丽的光辉。昙花和流星瞬间之美令我无法忘怀,大自然神奇的造物者给了我们许多的美,不论瞬间的还是永恒的,我们都要用真心去欣赏去品味。
通过合并前面五个步骤,我们就能够一睹这流星刹那间的交错,而后瞬间就穿透为永恒,只是一刻用生命幻化的美。
加载全部内容