Home > Web Front-end > H5 Tutorial > body text

HTML5 Canvas practical code case to achieve fireworks effect

黄舟
Release: 2017-03-30 13:22:29
Original
4219 people have browsed it

1. Effect

HTML5 Canvas practical code case to achieve fireworks effect

2. Code analysis

(1) requestAnimationFrame

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);
Copy after login

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);
              };
})();
Copy after login

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);
Copy after login

Cancel redrawing can be done with cancelAnimationFrame.

window.cancelAnimationFrame(requestID);
Copy after login

Its parameter is an integer value representing the task ID returned by requestAnimationFrame.

(2) Prepare canvas

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");
Copy after login

(3) Fireworks Object (FireWork)

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 = [];
}
Copy after login

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));
        }
    }
Copy after login

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;
    }
Copy after login

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));
        }
    }
}
Copy after login

(4) The small ball object (Ball) generated by the explosion

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轴坐标}
Copy after login

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;
    }
}
Copy after login

(5) Global Variables and tool methods

var fwx = -1,                 //烟花位置X轴坐标
    fwy = -1,                 //烟花位置Y轴坐标
    currFW = null,            //烟花实例
    currBallIndex = -1,       //当前正在绘制的小球索引
    drawCount = 0,            //绘制次数
    allDrawCount = 40,        //总共需要的绘制次数
    width = canvas.width,     //画布宽度
    height = canvas.height;   //画布高度
Copy after login

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);
}
Copy after login

(6) Drawing method

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);
}
Copy after login

The color here is a random value.

(7) Start drawing

The last step is to start drawing.

currFW = new FireWork();
currFW.run();
requestAnimationFrame(drawLine);
Copy after login

(8) All codes.

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);
Copy after login

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!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template