关于前端:我用canvas带你看一场流星雨

2次阅读

共计 5014 个字符,预计需要花费 13 分钟才能阅读完成。

前言

最近总是梦见一些小时候的故事,印象最粗浅的就是夏天坐在屋顶上,看着满天的繁星,一颗,两颗,三颗 … 情不自禁地开始了数星星的过程。不经意间,一颗流星划过夜间,尽管只是转瞬即逝,但它仿佛比夜空中的其它繁星更吸引着我。听老人说,看见流星的时候许诺,欲望是能够实现的,此时早已把数星星抛之脑后,开始期待着下一颗流星的呈现。然而那天早晨,流星再也没有呈现,这也成了本人小时候的一个遗憾。

明天,我决定用 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 视觉盛宴

流星极短暂的星星是也,它不像恒星和行星那般夺目,却用短暂的生命,划破夜空,用霎时洒脱的弧线留住漂亮的光芒。昙花和流星霎时之美令我无奈忘怀,大自然神奇的造物者给了咱们许多的美,不管霎时的还是永恒的,咱们都要用真心去观赏去品尝。

通过合并后面五个步骤,咱们就可能一睹这流星刹那间的交织,而后霎时就穿透为永恒,只是一刻用生命幻化的美。

最初

明天的视觉盛宴就到这里了,想要查看源码的同学快来公众号回复 流星雨 吧~

正文完
 0