Fork me on GitHub 盒子
盒子
文章目录
  1. 介绍
  2. Demo
  3. 实现
    1. requestAnimationFrame
    2. 初始化基本配置
    3. 核心,不断渲染雪花位置的函数
    4. 补充新鲜雪花
  4. 总结

canvas实现下雪效果

介绍

该Demo主要是requestAnimationFrame的一个简单应用,实现了粒子效果,网上也有一些其他的粒子效果的实现的根本原理也大致相同,只是运动路径算法差异。

Demo

请狠狠戳我

实现

requestAnimationFrame

首先,你需要明白requestAnimationFrame是个什么东东。

在浏览器动画程序中,我们通常使用一个定时器来循环每隔几毫秒移动目标物体一次,来让它动起来。如今有一个好消息,浏览器开发商们决定:“嗨,为什么我们不在浏览器里提供这样一个API呢,这样一来我们可以为用户优化他们的动画。”所以,这个requestAnimationFrame()函数就是针对动画效果的API,你可以把它用在DOM上的风格变化或画布动画或WebGL中。

那么使用它有什么优势呢?

浏览器可以优化并行的动画动作,更合理的重新排列动作序列,并把能够合并的动作放在一个渲染周期内完成,从而呈现出更流畅的动画效果。如果在一个浏览器标签页里运行一个动画,当这个标签页不可见时,浏览器会暂停它,这会减少CPU,内存的压力,节省电池电量。

所以,我们第一步现在代码中增加一个rAF方法

1
2
3
4
5
6
7
8
9
10
11
// 使用最优频率requestAnimationFrame代替定时器,当然如果浏览器如果不支持的话,还是使用定时器完成
window.rAF = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
setTimeout(callback, 1000 / 60);
};
})();

初始化基本配置

首先我们可以设置一些默认的值,便于后续做修改调整或者适配等。

1
2
3
4
5
6
7
8
9
// 默认配置
var defaultSetting = {
width: 900, // canvas默认宽度,后面修改为频幕宽度
height: 300, // canvas默认高度
canvas: null, // canvas对象
ctx: null, // 画布content(2x)
snowArr: [], // 雪花的数组
total: 80 // 雪花的数量
};

接下来往雪花数组中推入指定数量的雪花对象,当然,每一个雪花我们可以给它设定自己的属性,长宽,渐变等。

1
2
3
4
5
6
7
8
9
10
11
12
13
defaultSetting.width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
defaultSetting.canvas = document.getElementById("canvas");
defaultSetting.canvas.setAttribute("width",defaultSetting.width);
defaultSetting.ctx = defaultSetting.canvas.getContext("2d");
for(var i = 0; i < defaultSetting.total;i++){
defaultSetting.snowArr.push({
"left":Tools.createRandom(0, defaultSetting.width), // 工具函数createRandom是创建一个m上下n的随机数
"top":Tools.createRandom(0, defaultSetting.height),
"deg":Tools.createRandom(-6, 6),
"scale":Tools.createRandom(3, 6)
});
}

这个时候我们就得到了开始时刻雪花的数组和相关属性值

核心,不断渲染雪花位置的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function updateSnow(){
// 清除画布,重新绘制下一帧雪花的位置
defaultSetting.ctx.clearRect(0, 0, defaultSetting.width, defaultSetting.height);
defaultSetting.ctx.save();
// 循环雪花数组,分别绘制每一个雪花的位置
for(var i=0; i<defaultSetting.snowArr.length; i++){
var h = 0.5 * defaultSetting.snowArr[i].scale;
defaultSetting.snowArr[i].left = defaultSetting.snowArr[i].left + Math.tan(Tools.radian(defaultSetting.snowArr[i].deg))*h/2;
defaultSetting.snowArr[i].top = defaultSetting.snowArr[i].top + h;
// 删除画面外的雪花
if(defaultSetting.snowArr[i].left < 0 || defaultSetting.snowArr[i].left > defaultSetting.width || defaultSetting.snowArr[i].top > defaultSetting.height){
defaultSetting.snowArr.splice(i--, 1);
continue;
}
var width_i = defaultSetting.snowArr[i].scale;
// 雪花边界投影
var ra = defaultSetting.ctx.createRadialGradient(defaultSetting.snowArr[i].left, defaultSetting.snowArr[i].top, width_i/4, defaultSetting.snowArr[i].left, defaultSetting.snowArr[i].top, width_i);
ra.addColorStop(0, "rgba(255,255,255,0.8)");
ra.addColorStop(1, "rgba(255,255,255,0.1)");
defaultSetting.ctx.fillStyle = ra;
defaultSetting.ctx.beginPath();
defaultSetting.ctx.arc(defaultSetting.snowArr[i].left, defaultSetting.snowArr[i].top, width_i, 0, 2*Math.PI);
defaultSetting.ctx.fill();
}
defaultSetting.ctx.restore();
// 持续刷新雪花的位置
rAF(updateSnow);
}

这样我们就得到了一组80个不断运动的雪花,但是这样的话不会有源源不断的新雪花加入,所以,最后一步,我们还需要不断补充新鲜雪花。

补充新鲜雪花

这个时候就会发现开始把雪花数组给单独提出来是多么明智,只需要不断push到该数组就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 增加新的雪花
function createNewSnow(){
setTimeout(function(){
if(defaultSetting.snowArr.length < 200){
for(var i=0; i<20; i++){
defaultSetting.snowArr.push({
"left":Tools.createRandom(0, defaultSetting.width),
"top":0,
"deg":Tools.createRandom(-6, 6),
"scale":Tools.createRandom(3, 6)
});
}
}
createNewSnow();
}, Math.random()*200+500);
}
createNewSnow();

总结

一个简单的粒子下雪效果就是这么简单,进一步可以去试试alloy团队的粒子效果。


如果能给您带去些许帮助,鄙人不甚欢心。如有错误,恳请交流指出,谢谢!
转载请注明出处:http://mcchen.club/


支持一下
扫一扫,支持McChen