canvas实现雪花效果

1,158 阅读3分钟

思路

将雪花看成一个个对象,有x轴和y轴的速度,有不同的缩放比例和不同的旋转角度和旋转速度。使用一个for循环和定时器生成一定数量的雪花对象,然后根据速度进行运动。考虑画板的宽和高,运用模运算使这些对象一直在画板中。

步骤:

1. 雪花构造函数

function Snow(x, y, scale, rotate, speedX, speedY, speedR) {
  this.x = x;
  this.y = y;
  this.scale = scale;
  this.rotate = rotate;
  this.speedX = speedX;
  this.speedY = speedY;
  this.speedR = speedR;
}

2.绘制雪花

使用在原型链上编程的方法,给雪花添加绘制的方法。

Snow.prototype.render = function () {
  context.save(); //- 保存画布原来的位置
  context.beginPath(); //- 开启路径
  context.translate(this.x, this.y) //- 移动画布  x y 
  context.scale(this.scale, this.scale) //- 缩放比例
  context.rotate(this.rotate * Math.PI / 180);
  context.moveTo(-20, 0); //- 线的开始位置
  context.lineTo(20, 0) //-  线结束的位置
  context.strokeStyle = '#fff'; //- 颜色
  context.lineWidth = 10; //- 宽度
  context.lineCap = 'round'; //- 圆角
  context.stroke(); //- 开始进行绘画
  //- 角度转弧度的计算
  var disX = Math.sin(30 * Math.PI / 180) * 20;
  var disY = Math.sin(60 * Math.PI / 180) * 20;
  /* 第二条线 */
  context.moveTo(-disX, disY);
  context.lineTo(disX, -disY);
  //- 第三条线
  context.moveTo(-disX, -disY);
  context.lineTo(disX, disY);
  context.stroke(); //- 开始进行重复绘画
  context.restore(); //- 绘画完成恢复原来的位置
  context.closePath(); //- 关闭绘画路径
}

3.程序入口

生成雪花、让雪花开始运动。

使用for循环,生成100个雪花。

不能让这100个雪花一起生成,因此使用setTimeout定时器,让这些雪花在8秒内生成。

注意: 要使用闭包!

var init = function () {
  for (var i = 0; i < 100; i++) {
    var x = Math.random() * canvas.width;
    var scale = Math.random() + 0.5; //- 缩放比例欸
    var rotate = Math.random() * 60; //- 旋转的角度
    var speedX = Math.random() + 1; //- x轴移动的速度
    var speedY = Math.random() + 5; //- 下降的速度
    var speedR = Math.random() * 4 + 2; //- 旋转的速度

    (function (x, y, scale, rotate, speedX, speedY, speedR) {

      setTimeout(function () {
        var snow = new Snow(x, y, scale, rotate, speedX, speedY, speedR);
        snow.render();
        snowArr.push(snow);
      }, Math.random() * 8000)

    })(x, 0, scale, rotate, speedX, speedY, speedR)
  }
  snowing();
}

4.雪花运动函数

清除画布内容,重新绘制。

注意画布的宽高,使用模运算使雪花一直在画布中。

function snowing() {
  //- 收集生成的雪花对象,进行重置操作,改变生成位置,
  setInterval(function () {
    //- 之前的信息进行清除操作
    context.clearRect(0, 0, canvas.width, canvas.height);
    for (var i = 0; i < snowArr.length; i++) {
      snowArr[i].x = (snowArr[i].x + snowArr[i].speedX) % canvas.width;
      snowArr[i].y = (snowArr[i].y + snowArr[i].speedY) % canvas.height
      snowArr[i].rotate = (snowArr[i].rotate + snowArr[i].speedR) % 60;
      snowArr[i].render();
    }
  }, 30);
}

完整代码

var canvas = document.getElementById('snow');
var context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;


// function snow () {
//   context.save();  //- 保存画布原来的位置
//   context.beginPath(); //- 开启路径
//   context.translate(100, 100) //- 移动画布  x y 
//   context.moveTo(-20, 0); //- 线的开始位置
//   context.lineTo(20, 0) //-  线结束的位置
//   context.strokeStyle = '#fff';  //- 颜色
//   context.lineWidth = 10;    //- 宽度
//   context.lineCap = 'round';   //- 圆角
//   context.stroke(); //- 开始进行绘画

//   //- 角度转弧度的计算
//   var disX = Math.sin(30 * Math.PI / 180) * 20;
//   var disY = Math.sin(60 * Math.PI / 180) * 20;

//   /* 第二条线 */
//   context.moveTo(-disX, disY);
//   context.lineTo(disX, -disY);
//   //- 第三条线
//   context.moveTo(-disX, -disY);
//   context.lineTo(disX, disY);
//   context.stroke();   //- 开始进行重复绘画
//   context.restore();  //- 绘画完成恢复原来的位置
//   context.closePath();   //- 关闭绘画路径
// }

// snow();


function Snow(x, y, scale, rotate, speedX, speedY, speedR) {
  this.x = x;
  this.y = y;
  this.scale = scale;
  this.rotate = rotate;
  this.speedX = speedX;
  this.speedY = speedY;
  this.speedR = speedR;
}

Snow.prototype.render = function () {
  context.save(); //- 保存画布原来的位置
  context.beginPath(); //- 开启路径
  context.translate(this.x, this.y) //- 移动画布  x y 
  context.scale(this.scale, this.scale) //- 缩放比例
  context.rotate(this.rotate * Math.PI / 180);
  context.moveTo(-20, 0); //- 线的开始位置
  context.lineTo(20, 0) //-  线结束的位置
  context.strokeStyle = '#fff'; //- 颜色
  context.lineWidth = 10; //- 宽度
  context.lineCap = 'round'; //- 圆角
  context.stroke(); //- 开始进行绘画
  //- 角度转弧度的计算
  var disX = Math.sin(30 * Math.PI / 180) * 20;
  var disY = Math.sin(60 * Math.PI / 180) * 20;
  /* 第二条线 */
  context.moveTo(-disX, disY);
  context.lineTo(disX, -disY);
  //- 第三条线
  context.moveTo(-disX, -disY);
  context.lineTo(disX, disY);
  context.stroke(); //- 开始进行重复绘画
  context.restore(); //- 绘画完成恢复原来的位置
  context.closePath(); //- 关闭绘画路径
}
// var s = new Snow(50,50,1,10);
// s.render();
var snowArr = [];

var init = function () {
  for (var i = 0; i < 100; i++) {
    var x = Math.random() * canvas.width;
    var scale = Math.random() + 0.5; //- 缩放比例欸
    var rotate = Math.random() * 60; //- 旋转的角度
    var speedX = Math.random() + 1; //- x轴移动的速度
    var speedY = Math.random() + 5; //- 下降的速度
    var speedR = Math.random() * 4 + 2; //- 旋转的速度

    (function (x, y, scale, rotate, speedX, speedY, speedR) {

      setTimeout(function () {
        var snow = new Snow(x, y, scale, rotate, speedX, speedY, speedR);
        snow.render();
        snowArr.push(snow);
      }, Math.random() * 8000)

    })(x, 0, scale, rotate, speedX, speedY, speedR)

    // setTimeout(function () {
    //   var snow = new Snow(x, 0, scale, rotate, speedX, speedY, speedR);
    //   snow.render();
    //   snowArr.push(snow);
    // }, Math.random() * 8000)
  }
  // snowing();
}

function snowing() {
  //- 收集生成的雪花对象,进行重置操作,改变生成位置,
  setInterval(function () {
    //- 之前的信息进行清除操作
    context.clearRect(0, 0, canvas.width, canvas.height);
    for (var i = 0; i < snowArr.length; i++) {
      snowArr[i].x = (snowArr[i].x + snowArr[i].speedX) % canvas.width;
      snowArr[i].y = (snowArr[i].y + snowArr[i].speedY) % canvas.height
      snowArr[i].rotate = (snowArr[i].rotate + snowArr[i].speedR) % 60;
      snowArr[i].render();
    }
  }, 30);
}


init();

image.png