利用canvas,實現一個下雪的效果,我們先預覽下效果:
我們先分析下這個效果:
1,隨機產生雪花
2,雪花的產生不是同時產生,而是有先后順序的
3,雪花怎么表示
4,怎么源源不斷的下雪
5,雪花有大有小
搞清楚上面幾個問題之后,這個效果基本上就實現了,
首先,由於這個是全屏效果,我采用動態創建canvas,把整個瀏覽器的寬與高賦值給canvas
1 var Canvas = function (w, h) { 2 this.width = w; 3 this.height = h; 4 } 5 Canvas.prototype = { 6 init: function () { 7 var oC = document.createElement("canvas"); 8 oC.setAttribute('width', this.width); 9 oC.setAttribute('height', this.height); 10 oC.setAttribute('id', 'canvas'); 11 oC.style.backgroundColor = '#000'; 12 document.body.appendChild(oC); 13 } 14 } 15 var curWinWidth = window.innerWidth, 16 curWinHeight = window.innerHeight; 17 var oCanvas = new Canvas(curWinWidth, curWinHeight); 18 oCanvas.init();
調用oCanvas對象的init方法之后,就會在body的最后面追加一個canvas,id為canvas,寬、高與瀏覽器的寬、高相同,背景為黑色,晚上下雪的效果
接下來,有了舞台,演員該上場了,怎么產生雪花呢?這里把下雪相關的操作,封裝成一個類,他的基本結構如下:
var Snow = function(){}
Snow.prototype = {
init : function(){},
draw : function( cxt ) {},
update : function(){}
}
這個類一共有三個方法( init, draw, update ).
init:初始化雪花的位置( x, y 坐標 )、速度、半徑( 雪花的大小,在這里我們把雪花用半徑不同的圓表示 )
function random(min, max) { return Math.random() * (max - min) + min; } init: function () { this.x = random(0, width); this.y = 0; this.r = random(1, 5); this.vy = random(3, 5); }
那么init 加上 這個random函數 就可以完成雪花的初始化
1,雪花出來的時候,一般是在屏幕的最上方出現的,所以雪花的y坐標都是0, 其次,雪花的x坐標是隨機的,他的范圍是從屏幕的左邊到右邊,那么就是 0 ~ width. 這個width就是canvas的寬度,也就是瀏覽器的寬度
2,雪花的半徑r, 設置為1 ~ 5之間的任意值
3,雪花下降的速度設置為3 ~ 5之間的隨機速度,這里我做的下雪是垂直方向往下飄,你可以拓展,考慮風力影響( 這個時候肯定有水平方向的速度 )
有了這些初始化的參數之后,我們完善draw方法,繪制雪花:
1 draw: function (cxt) { 2 cxt.beginPath(); 3 cxt.fillStyle = 'white'; 4 cxt.arc(this.x, this.y + this.r, this.r, 0, Math.PI * 2, false); 5 cxt.fill(); 6 cxt.closePath(); 7 this.update(cxt); 8 },
參數cxt就是canvas的上下文,這個函數很簡單,就是一個arc方法調用init中設置的值來畫圓(雪花),在該方法的最后調用了一個update方法,他是干嘛的?他是更新雪花在垂直方向的速度
update: function (cxt) { if (this.y < height - this.r) { this.y += this.vy; } else { this.init(); } }
在update方法中,我們做了邊界判斷: 雪花往下飄落的時候,肯定會消失,消失之后怎么處理?沒有到達邊界怎么處理?
canvas的高度減去雪花的半徑,這就是雪花要消失時候的邊界,所以this.y < height - this.r 如果這個條件成立,那么說明雪花一直在飄着,我們就要把雪花的y方向的位置更新,雪花看起來(‘正在下雪’),當一個雪花快要消失的時候,我們再把他移動到初始的位置,這樣看起來就是在圓圓不斷的下雪,而不需要重新繪制雪花(如果這樣做,肯定會影響性能,這個特效最后肯定會被卡死,這個小技巧很多類似的特效都會用到)。至此核心的流程已經搞定,接下來,我們就要大量的生成雪花了。
1 var snow = []; 2 for (var i = 0; i < 500; i++) { 3 setTimeout(function () { 4 var oSnow = new Snow(); 5 oSnow.init(); 6 snow.push(oSnow); 7 }, 10 * i); 8 }
生成500個雪花,不是同時生成的,然后把這些雪花保存到數組snow中.
然后,開啟定時器,讓雪花不斷的飄落吧,
關於requestAnimationFrame的使用,可以參考我的這篇文章:[js高手之路] html5新增的定時器requestAnimationFrame實戰進度條
1 (function move() { 2 oGc.clearRect(0, 0, width, height); 3 for (var i = 0; i < snow.length; i++) { 4 snow[i].draw(oGc); 5 } 6 requestAnimationFrame(move); 7 })();
完整的demo代碼:

1 <head> 2 <meta charset="UTF-8"> 3 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 4 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 5 <title>雪花效果 - by ghostwu</title> 6 <!-- <script src="lib.js"></script> --> 7 <style> 8 * { 9 margin: 0; 10 padding: 0; 11 } 12 13 body { 14 overflow: hidden; 15 } 16 </style> 17 </head> 18 19 <body> 20 <script> 21 window.onload = function () { 22 var Canvas = function (w, h) { 23 this.width = w; 24 this.height = h; 25 } 26 Canvas.prototype = { 27 init: function () { 28 var oC = document.createElement("canvas"); 29 oC.setAttribute('width', this.width); 30 oC.setAttribute('height', this.height); 31 oC.setAttribute('id', 'canvas'); 32 oC.style.backgroundColor = '#000'; 33 document.body.appendChild(oC); 34 } 35 } 36 var curWinWidth = window.innerWidth, 37 curWinHeight = window.innerHeight; 38 var oCanvas = new Canvas(curWinWidth, curWinHeight); 39 oCanvas.init(); 40 41 var oC = document.querySelector('#canvas'); 42 var width = oC.width, height = oC.height, oGc = oC.getContext('2d'); 43 44 function random(min, max) { 45 return Math.random() * (max - min) + min; 46 } 47 var Snow = function () { 48 49 } 50 Snow.prototype = { 51 init: function () { 52 this.x = random(0, width); 53 this.y = 0; 54 this.r = random(1, 5); 55 this.vy = random(3, 5); 56 }, 57 draw: function (cxt) { 58 cxt.beginPath(); 59 cxt.fillStyle = 'white'; 60 cxt.arc(this.x, this.y + this.r, this.r, 0, Math.PI * 2, false); 61 cxt.fill(); 62 cxt.closePath(); 63 this.update(cxt); 64 }, 65 update: function (cxt) { 66 if (this.y < height - this.r) { 67 this.y += this.vy; 68 } else { 69 this.init(); 70 } 71 } 72 } 73 74 var snow = []; 75 for (var i = 0; i < 500; i++) { 76 setTimeout(function () { 77 var oSnow = new Snow(); 78 oSnow.init(); 79 snow.push(oSnow); 80 }, 10 * i); 81 } 82 83 (function move() { 84 oGc.clearRect(0, 0, width, height); 85 for (var i = 0; i < snow.length; i++) { 86 snow[i].draw(oGc); 87 } 88 requestAnimationFrame(move); 89 })(); 90 } 91 </script> 92 </body>