(自己看)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演示的主要内容,如果未能解决你的问题,请参考以下文章