html下雨代码,(自己看)HTML5 Canvas下雨动画DEMO演示(示例代码)

本文详细介绍了如何使用HTML5 Canvas和dat.gui库创建下雨动画DEMO。讲解了canvas绘图、水花效果以及雨滴溅开的实现,包括Rain和Drop对象的定义和动画更新方法。此外,还提到了设备像素比dpr在canvas绘制中的应用,以及通过监听鼠标和触摸事件调整下雨效果的交互方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.开篇

1.1本文目标

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

08bf1498025966524a95e03b33d60e49.bmp

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() || newDrop();18 drops.push(drop);19 drop.init(this.x);20 }21 }22 }

水花Drop:

1 //Droplet definition

2 functionDrop() {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 = functionaddListener(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 functionqueueFrame() {24 if(window.requestAnimationFrame) {25 requestAnimationFrame(frameHandler);26 } else{27 webkitRequestAnimationFrame(frameHandler);28 }29 }30 functionframeHandler(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 returnPUBLIC_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() || newRain();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对象就是对多点触屏的保存。

总结

继续努力吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值