(自己看)HTML5 Canvas下雨动画DEMO演示

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(自己看)HTML5 Canvas下雨动画DEMO演示相关的知识,希望对你有一定的参考价值。

1.开篇

1.1本文目标

本次的实例是html5 Canvas下雨动画DEMO演示,用到的知识主要是canvas和一个js的库dat.gui,感觉这个库老猛了,有空看一下。

技术分享

2.正文

2.1代码

首先是canvas绘画部分代码

下雨的绘制:

 1 ctx.beginPath();
 2 var rain_height = Rain.height * dpr;
 3 for (var i = rain.length - 1; i >= 0; i--) {
 4     var r = rain[i];
 5     var real_x = r.x * dpr;
 6     var real_y = r.y * dpr;
 7     ctx.moveTo(real_x, real_y);
 8     ctx.lineTo(real_x - demo.wind * r.z * dpr * 1.5, real_y -     
 9         rain_height * r.z);
10 }
11 ctx.lineWidth = Rain.width * dpr;
12 ctx.strokeStyle = demo.rain_color;
13 ctx.stroke();

雨水溅开水花的绘制:

1 for (var i = drops.length - 1; i >= 0; i--) {
2     var d = drops[i];
3     var real_x = d.x * dpr - d.radius;
4     var real_y = d.y * dpr - d.radius;
5     ctx.drawImage(d.canvas, real_x, real_y);
6 }

这里记录几个点,首先说的是绘制用到的dpr,也就是设备像素比(devicePixelRatio),从这里的代码可以看到,canvas最终绘制的宽高和位置都是要乘以dpr,那照着dpr的公式看canvas绘制的是设备像素?这里还有点不清楚,先记着。再有就是绘制溅开的水花的方法,是每一个水花的小点都是一个对象,并且存有初始化好的canvas,然后用主canvas的drawImage方法进行绘制,嗯,记着。

下面是雨水和水花对象:

雨水Rain:

 1 Rain.prototype.init = function() {
 2     this.y = Math.random() * -100;
 3     this.z = Math.random() * 0.5 + 0.5;
 4     this.splashed = false;
 5 }
 6 Rain.prototype.recycle = function() {
 7     demo.rain_pool.push(this);
 8 }
 9 // recycle rain particle and create a burst of droplets
10 Rain.prototype.splash = function() {
11     if (!this.splashed) {
12         this.splashed = true;
13         var drops = demo.drops;
14         var drop_pool = demo.drop_pool;
15 
16         for (var i=0; i<16; i++) {
17             var drop = drop_pool.pop() || new Drop();
18             drops.push(drop);
19             drop.init(this.x);
20         }
21     }
22 }

水花Drop:

 1 // Droplet definition
 2 function Drop() {
 3     this.x = 0;
 4     this.y = 0;
 5     this.radius = Math.round(Math.random() * 2 + 1) * demo.dpr;
 6     this.speed_x = 0;
 7     this.speed_y = 0;
 8     this.canvas = document.createElement(‘canvas‘);
 9     this.ctx = this.canvas.getContext(‘2d‘);
10     
11     // render once and cache
12     var diameter = this.radius * 2;
13     this.canvas.width = diameter;
14     this.canvas.height = diameter;
15 
16     var grd = this.ctx.createRadialGradient(this.radius, this.radius , 1, this.radius, this.radius, this.radius);
17     grd.addColorStop(0, demo.rain_color);
18     grd.addColorStop(1, demo.rain_color_clear);
19     this.ctx.fillStyle = grd;
20     this.ctx.fillRect(0, 0, diameter, diameter);
21 }
22 Drop.prototype.init = function(x) {
23     this.x = x;
24     this.y = demo.height;
25     var angle = Math.random() * Math.PI - (Math.PI * 0.5);
26     var speed = Math.random() * Drop.max_speed;
27     this.speed_x = Math.sin(angle) * speed;
28     this.speed_y = -Math.cos(angle) * speed;
29 }
30 Drop.prototype.recycle = function() {
31     demo.drop_pool.push(this);
32 }

首先说Rain对象,对象内部没有贴出来,贴出的是几个主要的方法,主要看它的回收机制recycle方法,只要飞出边界的雨水都会回收到rain_pool数组,而rain数组的是正在飞的雨水。再看Drop对象,就是主要看它的canvas设置了,还有就是init方法里面设置水平和垂直方向速度的方式,作者数学肯定很牛X。

下面是一个Ticker对象:

 1 var Ticker = (function(){
 2     var PUBLIC_API = {};
 3 
 4     // public
 5     // will call function reference repeatedly once registered, passing elapsed time and a lag multiplier as parameters
 6     PUBLIC_API.addListener = function addListener(fn) {
 7         if (typeof fn !== ‘function‘) throw(‘Ticker.addListener() requires a function reference passed in.‘);
 8 
 9         listeners.push(fn);
10 
11         // start frame-loop lazily
12         if (!started) {
13             started = true;
14             queueFrame();
15         }
16     };
17 
18     // private
19     var started = false;
20     var last_timestamp = 0;
21     var listeners = [];
22     // queue up a new frame (calls frameHandler)
23     function queueFrame() {
24         if (window.requestAnimationFrame) {
25             requestAnimationFrame(frameHandler);
26         } else {
27             webkitRequestAnimationFrame(frameHandler);
28         }
29     }
30     function frameHandler(timestamp) {
31         var frame_time = timestamp - last_timestamp;
32         last_timestamp = timestamp;
33         // make sure negative time isn‘t reported (first frame can be whacky)
34         if (frame_time < 0) {
35             frame_time = 17;
36         }
37         // - cap minimum framerate to 15fps[~68ms] (assuming 60fps[~17ms] as ‘normal‘)
38         else if (frame_time > 68) {
39             frame_time = 68;
40         }
41 
42         // fire custom listeners
43         for (var i = 0, len = listeners.length; i < len; i++) {
44             listeners[i].call(window, frame_time, frame_time / 16.67);
45         }
46         
47         // always queue another frame
48         queueFrame();
49     }
50 
51     return PUBLIC_API;
52 }());

主要是看requestAnimationFrame,这是一个绘制动画的方法,有时间再去看。传入的listener就是下面说的step。

 1 demo.step = function(time, lag) {
 2     // localize common references
 3     var demo = window.demo;
 4     var speed = demo.speed;
 5     var width = demo.width;
 6     var height = demo.height;
 7     var wind = demo.wind;
 8     var rain = demo.rain;
 9     var rain_pool = demo.rain_pool;
10     var drops = demo.drops;
11     var drop_pool = demo.drop_pool;
12     
13     // multiplier for physics
14     var multiplier = speed * lag;
15     
16     // spawn drops
17     demo.drop_time += time * speed;
18     while (demo.drop_time > demo.drop_delay) {
19         demo.drop_time -= demo.drop_delay;
20         var new_rain = rain_pool.pop() || new Rain();
21         new_rain.init();
22         var wind_expand = Math.abs(height / new_rain.speed * wind); // expand spawn width as wind increases
23         var spawn_x = Math.random() * (width + wind_expand);
24         if (wind > 0) spawn_x -= wind_expand;
25         new_rain.x = spawn_x;
26         rain.push(new_rain);
27     }
28     
29     // rain physics
30     for (var i = rain.length - 1; i >= 0; i--) {
31         var r = rain[i];
32         r.y += r.speed * r.z * multiplier;
33         r.x += r.z * wind * multiplier;
34         // remove rain when out of view
35         if (r.y > height) {
36             // if rain reached bottom of view, show a splash
37             r.splash();
38         }
39         // recycle rain
40         if (r.y > height + Rain.height * r.z || (wind < 0 && r.x < wind) || (wind > 0 && r.x > width + wind)) {
41             r.recycle();
42             rain.splice(i, 1);
43         }
44     }
45     
46     // splash drop physics
47     var drop_max_speed = Drop.max_speed;
48     for (var i = drops.length - 1; i >= 0; i--) {
49         var d = drops[i];
50         d.x += d.speed_x * multiplier;
51         d.y += d.speed_y * multiplier;
52         // apply gravity - magic number 0.3 represents a faked gravity constant
53         d.speed_y += 0.3 * multiplier;
54         // apply wind (but scale back the force)
55         d.speed_x += wind / 25 * multiplier;
56         if (d.speed_x < -drop_max_speed) {
57             d.speed_x = -drop_max_speed;
58         }else if (d.speed_x > drop_max_speed) {
59             d.speed_x = drop_max_speed;
60         }
61         // recycle
62         if (d.y > height + d.radius) {
63             d.recycle();
64             drops.splice(i, 1);
65         }
66     }
67     
68     demo.draw();
69 }

首先是第三段的地方,作者用了一个巧妙的数学方法增加了雨水下落的范围,主要就是根据wind风速的变化来调节范围。亲自试过,如果不添加这段代码,雨水下降的范围只有屏幕中间的一小部分。第四段是设置雨水的x、y,第五段是水花的x、y。

最后是事件:

 1 // handle interaction
 2 demo.mouseHandler = function(evt) {
 3     demo.updateCursor(evt.clientX, evt.clientY);
 4 }
 5 demo.touchHandler = function(evt) {
 6     evt.preventDefault();
 7     var touch = evt.touches[0];
 8     demo.updateCursor(touch.clientX, touch.clientY);
 9 }
10 demo.updateCursor = function(x, y) {
11     x /= demo.width;
12     y /= demo.height;
13     var y_inverse = (1 - y);
14     
15     demo.drop_delay = y_inverse*y_inverse*y_inverse * 100 + 2;
16     demo.wind = (x - 0.5) * 50;
17 }
18 
19 document.addEventListener(‘mousemove‘, demo.mouseHandler);
20 document.addEventListener(‘touchstart‘, demo.touchHandler);
21 document.addEventListener(‘touchmove‘, demo.touchHandler);

主要看的是触屏的touch事件,evt的touches对象就是对多点触屏的保存。

总结

继续努力吧!

以上是关于(自己看)HTML5 Canvas下雨动画DEMO演示的主要内容,如果未能解决你的问题,请参考以下文章

Canvas制作的下雨动画

HTML5 都有哪些让你惊艳的 demo?

html5 canvas首屏自适应背景动画循环效果怎么插入

html5 canvas首屏自适应背景动画循环效果怎么插入

HTML5 金色漩涡动画

如何用HTML5 的Canvas制作3D动画效果