Maison > interface Web > Tutoriel H5 > Cas de code pratique HTML5 Canvas pour obtenir un effet de feu d'artifice

Cas de code pratique HTML5 Canvas pour obtenir un effet de feu d'artifice

黄舟
Libérer: 2017-03-30 13:22:29
original
4254 Les gens l'ont consulté

1. Effet

Cas de code pratique HTML5 Canvas pour obtenir un effet de feu dartifice

2. Analyse du code

(1) requestAnimationFrame

requestAnimationFrame est utilisé par le navigateur pour le timing Une interface pour les opérations de bouclage, similaire à setTimeout, son objectif principal est de redessiner la page Web image par image.

Le but de la configuration de cette API est de permettre à divers effets d'animation de pages Web (animation DOM, animation Canvas, animation SVG, animation WebGL) d'avoir un mécanisme de rafraîchissement unifié, économisant ainsi les ressources système, améliorant les performances du système, et améliorer les effets visuels. Utiliser cette API dans le code consiste à indiquer au navigateur que vous souhaitez exécuter une animation et à laisser le navigateur planifier un redessinage de page Web dans l'image d'animation suivante.

L'avantage de requestAnimationFrame est d'utiliser pleinement le mécanisme de rafraîchissement de l'affichage et d'économiser les ressources système. L'affichage a une fréquence de rafraîchissement fixe (60 Hz ou 75 Hz), ce qui signifie qu'il ne peut être redessiné que jusqu'à 60 ou 75 fois par seconde. L'idée de base de requestAnimationFrame est de rester synchronisé avec cette fréquence de rafraîchissement et d'utiliser cette fréquence de rafraîchissement. pour redessiner la page. De plus, en utilisant cette API, la page cessera automatiquement de s'actualiser une fois qu'elle ne sera plus dans l'onglet actuel du navigateur. Cela permet d'économiser du CPU, du GPU et de l'énergie.

Mais une chose à noter est que requestAnimationFrame est terminé sur le thread principal. Cela signifie que si le thread principal est très occupé, l'effet d'animation de requestAnimationFrame sera considérablement réduit.

requestAnimationFrame prend une fonction de rappel comme paramètre. Cette fonction de rappel sera appelée avant que le navigateur ne soit redessiné.

requestID = window.requestAnimationFrame(callback);
Copier après la connexion

Actuellement, les navigateurs de versions supérieures (Firefox 23 / IE 10 / Chrome / Safari) prennent en charge cette méthode. Vous pouvez utiliser la méthode suivante pour vérifier si le navigateur prend en charge cette API. Si elle n'est pas prise en charge, simulez vous-même la méthode de déploiement.

window.requestAnimFrame = (function(){      
return  window.requestAnimationFrame       || 
              window.webkitRequestAnimationFrame || 
              window.mozRequestAnimationFrame    || 
              window.oRequestAnimationFrame      || 
              window.msRequestAnimationFrame     || 
              function( callback ){
                window.setTimeout(callback, 1000 / 60);
              };
})();
Copier après la connexion

Le code ci-dessus simule requestAnimationFrame 60 fois par seconde (environ une fois toutes les 16,7 millisecondes).

Lorsque vous utilisez requestAnimationFrame, appelez-le simplement à plusieurs reprises.

function repeatOften() {  // Do whatever  requestAnimationFrame(repeatOften);
}

requestAnimationFrame(repeatOften);
Copier après la connexion

Annulez le redessinage à l'aide de CancelAnimationFrame.

window.cancelAnimationFrame(requestID);
Copier après la connexion

Son paramètre est une valeur entière renvoyée par requestAnimationFrame représentant l'ID de la tâche.

(2) Préparer le canevas (canvas)

Déterminez si le navigateur prend en charge le canevas et définissez la largeur et la hauteur en fonction de la taille de la fenêtre du navigateur.

var canvas = document.getElementById("myCanvas");
if (!canvas.getContext) {    return;
}
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;var ctx = canvas.getContext("2d");
Copier après la connexion

(3) Objet de feu d'artifice (FireWork)

L'effet de feu d'artifice peut être simplement considéré comme entourant un point, et l'explosion produit de nombreuses petites boules qui se propagent sur les côtés. Par conséquent, un objet de feu d'artifice est nécessaire. Cet objet enregistre principalement l'emplacement du feu d'artifice et les informations sur les boules environnantes. Notre objet feu d'artifice est donc défini comme suit.

function FireWork() {    
this.x = -1;    
this.y = -1;    
this.balls = [];
}
Copier après la connexion

Quelles méthodes cet objet doit-il avoir ?

Tout d'abord, créez la petite boule qui explose.

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));
        }
    }
Copier après la connexion

Remarque : ici fwx est la coordonnée sur l'axe X de la position du feu d'artifice, fwy est la coordonnée sur l'axe Y de la position du feu d'artifice, la même ci-dessous.

La longueur de course de la balle est ici une valeur aléatoire de 50 à 200. Le point de départ de la trajectoire de la balle est la position du feu d'artifice et le point final est un point aléatoire sur un cercle.

Ensuite, il faut initialiser le feu d'artifice, principalement pour déterminer la position et générer des petites boules.

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;
    }
Copier après la connexion

Remarque : ici drawCount est le nombre de tirages, et currBallIndex est la balle actuellement tirée index .

L'ensemble de FireWork est défini comme suit.

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));
        }
    }
}
Copier après la connexion

(4) Objet balle (Ball) produit par explosion

La balle a besoin de connaître l'emplacement de son point de départ et de son point d'arrivée, elle est donc défini comme suit.

function Ball(bx, by, ex, ey) {    this.bx = bx;//起点X轴坐标
    this.by = by;//起点Y轴坐标
    this.ex = ex;//终点X轴坐标
    this.ey = ey;//终点Y轴坐标}
Copier après la connexion

La balle doit également être capable de calculer les coordonnées actuelles et les coordonnées du prochain dessin en fonction du nombre actuel de dessins et du nombre total de dessins. La ligne droite reliant ces deux coordonnées est le contenu de. être dessiné cette fois, donc la définition est la suivante.

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;
    }
}
Copier après la connexion

(5) Variables globales et méthodes d'outils

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

et puis il existe plusieurs méthodes d'outils.

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);
}
Copier après la connexion

(6) Méthode de dessin

Enfin, il existe une méthode de dessin appelée par requestAnimationFrame. Cette méthode de dessin consiste à obtenir le chemin de la balle qui explose (un segment de ligne comprenant le point de départ et le point final) en fonction du nombre actuel de dessins, puis à effacer le dernier chemin tracé.

Lorsque l'effet d'un feu d'artifice est dessiné, procédez au tirage du feu d'artifice suivant.

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);
}
Copier après la connexion

La couleur ici est une valeur aléatoire.

(7) Commencez à dessiner

La dernière étape consiste à commencer à dessiner.

currFW = new FireWork();
currFW.run();
requestAnimationFrame(drawLine);
Copier après la connexion

(8) Tous les codes.

L'intégralité du code est le suivant, 160 lignes au 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);
Copier après la connexion

Bienvenue pour en discuter.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal