I recently made a project where there is a need to realize the animation effects of raining and light snowing, so I made a drop component here to show the common falling objects of this canvas. Before introducing the main text to you, let me show you the renderings:
展示效果图:
下雨下雪
It looks good. Compared to using the creation of dom elements to create multi-object position movement pictures, using canvas will be easier and faster, and the performance will be better.
调用代码
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Document</title><style>#canvas{width:100%;height: 100%;}</style></head><body><canvas id="canvas"></canvas><script src="canvasDrop.js"></script><script>canvasDrop.init({type: "rain", // drop type, rain or snowspeed: [0.4,2.5], //speed range size_range: [0.5,1.5],//The size and radius range hasBounce: true, //Is there any rebound effect or false, wind_direction: -105 //The angle hasGravity: true //Is there any gravity consideration});</script></body></html>OK, let’s explain the simple implementation principle. First, define some global variables we will use, such as wind direction angle, probability, object data, etc.
定义全局变量
//Define two object data//The drops drop object object object// and rebound object bounces object var drops = [], bounces = [];//The gravity acceleration is set to 0.2/One frame var gravity = 0.2;var speed_x_x, //Horizontal acceleration speed_x_y, //Longiline acceleration wind_anger; //Wind direction//The pixel width and height of the canvas var canvasWidth, canvasHeight;//The probability of creating drop var drop_chance;//Configure object var OPTS;//Default whether there is a requestAnimationFrame method, if there is, use it, if there is no, it will be about 30 frames per second window.requestAnimFrame =window.requestAnimationFrame ||window.webkitRequestAnimationFrame ||window.mozRequestAnimationFrame ||window.oRequestAnimationFrame ||window.msRequestAnimationFrame ||function(callback) {window.setTimeout(callback, 1000 / 30);};定义核心对象
Next we need to define several important objects. There are fewer objects that the organization needs to define. In total, only three core objects are defined in the entire drop component, which are as follows:
Vector velocity object, with horizontal x and vertical y velocity size units: V = displacement pixels/frames
The understanding of Vector object is also very simple and crude, which is to record the speed of the falling object drop/V
var Vector = function(x, y) {//私有属性横向速度x ,纵向速度ythis.x = x || 0;this.y = y || 0;};//公有方法- add : 速度改变函数,根据参数对速度进行增加//由于业务需求,考虑的都是下落加速的情况,故没有减速的,后期可拓展/** @param v object || string */Vector.prototype.add = function(v) {if (vx != null && vy != null) {this.x += vx;this.y += vy;} else {this.x += v;this.y += v;}return this;};//Public method- copy: Copy a vector to save the record of the previous speed node Vector.prototype.copy = function() {//Return a Vector instance with the same speed attribute return new Vector(this.x, this.y);};Drop The drop object object, that is, the raindrops and snow in the above effect, later you can expand it to a meteorite or shell. The basic definition of the Drop object is as follows//Constructor var Drop = function() {/* .... */};//Public method-update Drop.prototype.update = function() {/* .... */};//Public method-drawDrop.prototype.draw = function() {/* .... */};After reading the above three methods, have you guessed their role? Let’s understand what these three methods do.
Constructor
The constructor is mainly responsible for defining the initial information of the drop object, such as speed, initial coordinates, size, acceleration, etc.
//Constructor Dropvar Drop = function() {//Random set the initial coordinates of drop//First select the falling element from which side is the one from var randomEdge = Math.random()*2;if(randomEdge > 1){this.pos = new Vector(50 + Math.random() * canvas.width, -80);}else{this.pos = new Vector(canvas.width, Math.random() * canvas.height);}//Set the size of the falling element//Random value is taken by the radius range of the called OPTS function this.radius = (OPTS.size_range[0] + Math.random() * OPTS.size_range[1]) *DPR;//获得drop初始速度//通过调用的OPTS函数的速度范围进行随机取值this.speed = (OPTS.speed[0] + Math.random() * OPTS.speed[1]) *DPR;this.prev = this.pos;//将角度乘以0.017453293 (2PI/360)即可转换为弧度。 var eachAger = 0.017453293; //Get the angle of wind direction wind_anger = OPTS.wind_direction * eachAger;//Get the lateral acceleration speed_x = this.speed * Math.cos(wind_anger);//Get the longitudinal acceleration speed_y = - this.speed * Math.sin(wind_anger);//Bind a speed instance this.vel = new Vector(wind_x, wind_y);};Drop对象的update方法
The update method is responsible for changing the attributes of each frame drop instance, such as changing the displacement.
Drop.prototype.update = function() {this.prev = this.pos.copy();//If there is gravity, the longitudinal velocity will be increased if (OPTS.hasGravity) {this.vel.y += gravity;}//this.pos.add(this.vel);};Drop对象的draw方法
draw方法负责,每一帧drop实例的绘画
Drop.prototype.draw = function() {ctx.beginPath();ctx.moveTo(this.pos.x, this.pos.y);// Currently there are only two situations, one is rain, i.e. Bezier curve if(OPTS.type =="rain"){ctx.moveTo(this.prev.x, this.prev.y);var ax = Math.abs(this.radius * Math.cos(wind_anger));var ay = Math.abs(this.radius * Math.sin(wind_anger));ctx.bezierCurveTo(this.pos.x + ax, this.pos.y + ay, this.prev.x + ax , this.prev.y + ay, this.pos.x, this.pos.y);ctx.stroke();//The other is snow---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------bounce The object of rebounding from falling and falling, that is, the water droplets that rebound from rainwater above, you can also expand into rebounding gravel or smoke in the later stage
The definition is very simple, so I won't explain it in detail here
var Bounce = function(x, y) {var dist = Math.random() * 7;var angle = Math.PI + Math.random() * Math.PI;this.pos = new Vector(x, y);this.radius = 0.2+ Math.random()*0.8;this.vel = new Vector(Math.cos(angle) * dist,Math.sin(angle) * dist);};Bounce.prototype.update = function() {this.vel.y += gravity;this.vel.x *= 0.95;this.vel.y *= 0.95;this.pos.add(this.vel);};Bounce.prototype.draw = function() {ctx.beginPath();ctx.arc(this.pos.x, this.pos.y, this.radius*DPR, 0, Math.PI * 2);ctx.fill();};对外接口
update
That is, the start function equivalent to the entire canvas animation
function update() {var d = new Date;//Clean the drawing ctx.clearRect(0, 0, canvas.width, canvas.height);var i = drops.length;while (i--) {var drop = drops[i];drop.update();//If the drop instance drops to the bottom, you need to be clear in the drops array if (drop.pos.y >= canvas.height) {//If you need rebound, add bounce instance to the bounce array if(OPTS.hasBounce){var n = Math.round(4 + Math.random() * 4); while (n--)bounces.push(new Bounce(drop.pos.x, canvas.height));}//If the drop instance drops to the bottom, you need to be clear in the drops array of the instance object drops.splice(i, 1);}drop.draw();}//If you need to rebound if(OPTS.hasBounce){var i = bounces.length;while (i--) {var bounce = bounces[i];bounce.update();bounce.draw();if (bounce.pos.y > canvas.height) bounces.splice(i, 1);}}//If the number of generated each time is if(drops.length < OPTS.maxNum){if (Math.random() < drop_chance) {var i = 0,len = OPTS.numLevel;for(; i<len; i++){drops.push(new Drop());}}}//Continuously loop updaterequestAnimFrame(update);}init
Init interface, initialize all basic properties of the entire canvas canvas, such as obtaining the pixel ratio of the screen, and setting the pixel size of the canvas, and setting the style of the canvas
function init(opts) {OPTS = opts;canvas = document.getElementById(opts.id);ctx = canvas.getContext("2d");//// Compatible with high-definition screens, the canvas canvas pixels must also change DPR = window.devicePixelRatio;// Canvas artboard pixel size, and must be compatible with high-definition screens, so the length and width of the artboard canvas should be multiplied by DPR canvasWidth = canvas.clientWidth * DPR;canvasHeight = canvas.clientHeight * DPR;// Set the width and height of the artboard canvas.width = canvasWidth;canvas.height = canvasHeight;drop_chance = 0.4;//Set style setStyle();}function setStyle(){if(OPTS.type =="rain"){ctx.lineWidth = 1 * DPR;ctx.strokeStyle = 'rgba(223,223,223,0.6)';ctx.fillStyle = 'rgba(223,223,223,0.6)';}else{ctx.lineWidth = 2 * DPR;ctx.strokeStyle = 'rgba(254,254,254,0.8)';ctx.fillStyle = 'rgba(254,254,254,0.8)';}}Conclusion
OK, a simple drop component has been completed, of course, there are many things that are not perfect. After writing this drop component, for the animation implementation of canvas, I believe that there are many places to discover in the H5 scene.
Finally, let’s talk about the shortcomings and later work:
0. There are not enough external interfaces for this component, the adjustable range is not very large, and the abstraction is not very thorough.
1、 setStyle 设置基本样式
2. Customization of the update and draw methods of Drop and Bounce objects allows users to set more forms and style effects of falling speed and size change.
3. The interface for animation pause, acceleration and deceleration operations should be added.
The above is the relevant knowledge about JS and Canvas that I introduced to you about achieving rainy and snowing effects. I hope it will be helpful to you. If you have any questions, please leave me a message and the editor will reply to you in time. Thank you very much for your support to Wulin.com website!
This article is reproduced: http://blog.csdn.net/xllily_11/article/details/51444311