requestAnimationFrame is used by the browser for timing An interface for loop operations, similar to setTimeout, its main purpose is to redraw the web page frame by frame.
The purpose of setting up this API is to allow various web page animation effects (DOM animation, Canvas animation, SVG animation, WebGL animation) to have a unified refresh mechanism, thereby saving system resources, improving system performance, and improving Visual effect. Using this API in the code is to tell the browser that you want to execute an animation and let the browser schedule a web page redraw in the next animation frame.
The advantage of requestAnimationFrame is to make full use of the refresh mechanism of the display and save system resources. The display has a fixed refresh frequency (60Hz or 75Hz), which means it can only be redrawn up to 60 or 75 times per second. The basic idea of requestAnimationFrame is to keep in sync with this refresh frequency and use this refresh frequency to redraw the page. Additionally, using this API, the page will automatically stop refreshing once it is no longer in the browser's current tab. This saves CPU, GPU and power.
But one thing to note is that requestAnimationFrame is completed on the main thread. This means that if the main thread is very busy, the animation effect of requestAnimationFrame will be greatly reduced.
requestAnimationFrame uses a callback function as a parameter. This callback function will be called before the browser redraws.
requestID = window.requestAnimationFrame(callback);
Currently, higher version browsers (Firefox 23 / IE 10 / Chrome / Safari) support this method. You can use the following method to check whether the browser supports this API. If it is not supported, simulate the deployment method yourself.
window.requestAnimFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function( callback ){ window.setTimeout(callback, 1000 / 60); }; })();
The above code simulates requestAnimationFrame 60 times per second (approximately once every 16.7 milliseconds).
When using requestAnimationFrame, just call it repeatedly.
function repeatOften() { // Do whatever requestAnimationFrame(repeatOften); } requestAnimationFrame(repeatOften);
Cancel redrawing can be done with cancelAnimationFrame.
window.cancelAnimationFrame(requestID);
Its parameter is an integer value representing the task ID returned by requestAnimationFrame.
Determine whether the browser supports canvas, and set the width and height to the browser window size.
var canvas = document.getElementById("myCanvas"); if (!canvas.getContext) { return; } canvas.width = window.innerWidth; canvas.height = window.innerHeight;var ctx = canvas.getContext("2d");
The firework effect can be simply thought of as surrounding a point, and the explosion produces many small balls that spread to the sides. Therefore, a fireworks object is needed. This object mainly records the location of the fireworks and the information about the surrounding balls. So our firework object is defined as follows.
function FireWork() { this.x = -1; this.y = -1; this.balls = []; }
What methods should this object have?
First, create the small ball produced by the explosion.
createBalls: function () { for (var i = 0; i < 300; i++) { var angle = Math.random() * Math.PI * 2, radius = getRandom(50, 200); this.balls.push(new Ball(fwx, fwy, fwx + Math.cos(angle) * radius, fwy + Math.sin(angle) * radius)); } }
Note: Here fwx is the X-axis coordinate of the fireworks position, fwy is the Y-axis coordinate of the fireworks position, the same below.
The running length of the ball here is a random value from 50 to 200. The starting point of the ball's trajectory is the position of the fireworks and the end point is a random point on a circle.
Then, the fireworks need to be initialized, mainly to determine the position and generate small balls.
init: function () { this.x = getRandom(200, width - 200); this.y = getRandom(200, height - 200); fwx = this.x; fwy = this.y; this.createBalls(); drawCount = 0; currBallIndex = 0; }
Note: here drawCount is the number of draws, and currBallIndex is the currently drawn ball index .
The entire FireWork is defined as follows.
function FireWork() { this.x = -1; this.y = -1; this.balls = []; } FireWork.prototype = { init: function () { this.x = getRandom(200, width - 200); this.y = getRandom(200, height - 200); fwx = this.x; fwy = this.y; this.createBalls(); drawCount = 0; currBallIndex = 0; }, run: function () { this.init(); }, createBalls: function () { for (var i = 0; i < 300; i++) { var angle = Math.random() * Math.PI * 2, radius = getRandom(50, 200); this.balls.push(new Ball(fwx, fwy, fwx + Math.cos(angle) * radius, fwy + Math.sin(angle) * radius)); } } }
The small ball needs to know the location of its starting point and end point, so it is defined as follows.
function Ball(bx, by, ex, ey) { this.bx = bx;//起点X轴坐标 this.by = by;//起点Y轴坐标 this.ex = ex;//终点X轴坐标 this.ey = ey;//终点Y轴坐标}
The ball must also be able to calculate the current coordinates and the next drawing coordinates based on the current number of draws and the total number of draws. The straight line connecting these two coordinates is the content to be drawn this time, so it is defined as follows .
Ball.prototype = { getSpan: function () { var xSpan = (this.ex - this.bx) / allDrawCount, ySpan = (this.ey - this.by) / allDrawCount; return { x: xSpan, y: ySpan }; }, currPosition: function () { var span = this.getSpan(), currX = -1, currY = -1; if (drawCount < allDrawCount) { currX = this.bx + span.x * (drawCount - 1); currY = this.by + span.y * (drawCount - 1); return { x: currX, y: currY }; } return null; }, nextPosition: function () { var span = this.getSpan(), currX = -1, currY = -1; if (drawCount < allDrawCount) { currX = this.bx + span.x * drawCount; currY = this.by + span.y * drawCount; return { x: currX, y: currY }; } return null; } }
var fwx = -1, //烟花位置X轴坐标 fwy = -1, //烟花位置Y轴坐标 currFW = null, //烟花实例 currBallIndex = -1, //当前正在绘制的小球索引 drawCount = 0, //绘制次数 allDrawCount = 40, //总共需要的绘制次数 width = canvas.width, //画布宽度 height = canvas.height; //画布高度
And then there are several tool methods.
function componentToHex(c) { var hex = c.toString(16); return hex.length == 1 ? "0" + hex : hex; }function rgbToHex(r, g, b) { return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); }function getRandom(minNum, maxNum) { var iChoices = maxNum - minNum + 1; return Math.floor(Math.random() * iChoices + minNum); }
There is finally one drawing method left for requestAnimationFrame to call. This drawing method is to get the path of the exploding ball (a line segment including the starting point and end point) based on the current number of drawings, and then erase the last drawn path.
When the effect of one firework is drawn, proceed to draw the next firework.
function drawLine(span) { if (currFW && currBallIndex !== -1) { if (drawCount <= allDrawCount) { ctx.save(); drawCount++; for (var i = 0, j = currFW.balls.length; i < j; i++) { var currBall = currFW.balls[i], beginPoint = currBall.currPosition(), endPoint = currBall.nextPosition(); if (beginPoint && endPoint) { console.log(currBallIndex, drawCount, currBall, beginPoint, endPoint); ctx.beginPath(); ctx.moveTo(currBall.bx, currBall.by); ctx.lineTo(beginPoint.x, beginPoint.y); ctx.strokeStyle = "#000"; ctx.stroke(); ctx.beginPath(); ctx.moveTo(beginPoint.x, beginPoint.y); ctx.lineTo(endPoint.x, endPoint.y); var r = getRandom(0, 255); var g = getRandom(0, 255); var b = getRandom(0, 255); ctx.strokeStyle = rgbToHex(r, g, b); ctx.stroke(); } else { ctx.beginPath(); ctx.moveTo(currBall.bx, currBall.by); ctx.lineTo(currBall.ex, currBall.ey); ctx.strokeStyle = "#000"; ctx.stroke(); } } currBallIndex++; currBallIndex %= currFW.balls.length; ctx.restore(); } else { ctx.clearRect(0, 0, width, height); currFW = new FireWork(); currFW.run(); } } requestAnimationFrame(drawLine); }
The color here is a random value.
The last step is to start drawing.
currFW = new FireWork(); currFW.run(); requestAnimationFrame(drawLine);
The entire code is as follows, 160 lines in total.
1 (function () { 2 var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { 3 return window.setTimeout(callback, 1000 / 60); 4 }; 5 window.requestAnimationFrame = requestAnimationFrame; 6 })(); 7 8 var canvas = document.getElementById("myCanvas"); 9 if (!canvas.getContext) { 10 return; 11 } 12 canvas.width = window.innerWidth; 13 canvas.height = window.innerHeight; 14 15 var ctx = canvas.getContext("2d"); 16 17 var fwx = -1, 18 fwy = -1, 19 currFW = null, 20 currBallIndex = -1, 21 drawCount = 0, 22 allDrawCount = 40, 23 width = canvas.width, 24 height = canvas.height; 25 26 function componentToHex(c) { 27 var hex = c.toString(16); 28 return hex.length == 1 ? "0" + hex : hex; 29 } 30 31 function rgbToHex(r, g, b) { 32 return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); 33 } 34 35 function getRandom(minNum, maxNum) { 36 var iChoices = maxNum - minNum + 1; 37 return Math.floor(Math.random() * iChoices + minNum); 38 } 39 40 function drawLine(span) { 41 if (currFW && currBallIndex !== -1) { 42 if (drawCount <= allDrawCount) { 43 ctx.save(); 44 drawCount++; 45 for (var i = 0, j = currFW.balls.length; i < j; i++) { 46 var currBall = currFW.balls[i], 47 beginPoint = currBall.currPosition(), 48 endPoint = currBall.nextPosition(); 49 if (beginPoint && endPoint) { 50 console.log(currBallIndex, drawCount, currBall, beginPoint, endPoint); 51 ctx.beginPath(); 52 ctx.moveTo(currBall.bx, currBall.by); 53 ctx.lineTo(beginPoint.x, beginPoint.y); 54 ctx.strokeStyle = "#000"; 55 ctx.stroke(); 56 ctx.beginPath(); 57 ctx.moveTo(beginPoint.x, beginPoint.y); 58 ctx.lineTo(endPoint.x, endPoint.y); 59 var r = getRandom(0, 255); 60 var g = getRandom(0, 255); 61 var b = getRandom(0, 255); 62 ctx.strokeStyle = rgbToHex(r, g, b); 63 ctx.stroke(); 64 } else { 65 ctx.beginPath(); 66 ctx.moveTo(currBall.bx, currBall.by); 67 ctx.lineTo(currBall.ex, currBall.ey); 68 ctx.strokeStyle = "#000"; 69 ctx.stroke(); 70 } 71 } 72 currBallIndex++; 73 currBallIndex %= currFW.balls.length; 74 ctx.restore(); 75 } else { 76 ctx.clearRect(0, 0, width, height); 77 currFW = new FireWork(); 78 currFW.run(); 79 } 80 } 81 requestAnimationFrame(drawLine); 82 } 83 84 function FireWork() { 85 this.x = -1; 86 this.y = -1; 87 this.balls = []; 88 } 89 90 FireWork.prototype = { 91 init: function () { 92 this.x = getRandom(200, width - 200); 93 this.y = getRandom(200, height - 200); 94 fwx = this.x; 95 fwy = this.y; 96 this.createBalls(); 97 drawCount = 0; 98 currBallIndex = 0; 99 }, 100 run: function () { 101 this.init(); 102 }, 103 createBalls: function () { 104 for (var i = 0; i < 300; i++) { 105 var angle = Math.random() * Math.PI * 2, 106 radius = getRandom(50, 200); 107 this.balls.push(new Ball(fwx, fwy, fwx + Math.cos(angle) * radius, fwy + Math.sin(angle) * radius)); 108 } 109 } 110 } 111 112 function Ball(bx, by, ex, ey) { 113 this.bx = bx; 114 this.by = by; 115 this.ex = ex; 116 this.ey = ey; 117 } 118 119 Ball.prototype = { 120 getSpan: function () { 121 var xSpan = (this.ex - this.bx) / allDrawCount, 122 ySpan = (this.ey - this.by) / allDrawCount; 123 return { 124 x: xSpan, 125 y: ySpan 126 }; 127 }, 128 currPosition: function () { 129 var span = this.getSpan(), 130 currX = -1, 131 currY = -1; 132 if (drawCount < allDrawCount) { 133 currX = this.bx + span.x * (drawCount - 1); 134 currY = this.by + span.y * (drawCount - 1); 135 return { 136 x: currX, 137 y: currY 138 }; 139 } 140 return null; 141 }, 142 nextPosition: function () { 143 var span = this.getSpan(), 144 currX = -1, 145 currY = -1; 146 if (drawCount < allDrawCount) { 147 currX = this.bx + span.x * drawCount; 148 currY = this.by + span.y * drawCount; 149 return { 150 x: currX, 151 y: currY 152 }; 153 } 154 return null; 155 } 156 } 157 158 currFW = new FireWork(); 159 currFW.run(); 160 requestAnimationFrame(drawLine);
Welcome to discuss.
The above is the detailed content of HTML5 Canvas practical code case to achieve fireworks effect. For more information, please follow other related articles on the PHP Chinese website!