目录
1、效果
2、代码解析
(1)requestAnimationFrame
(2)准备画版(canvas)
(3)烟花对象(FireWork)
(4)爆炸产生的小球对象(Ball)" >(4)爆炸产生的小球对象(Ball)
变量及工具方法" >(5)全局变量及工具方法
(6)绘制方法" >(6)绘制方法
(7)启动绘制
(8)全部代码。
首页 web前端 H5教程 HTML5 Canvas实战之实现烟花效果的代码案例

HTML5 Canvas实战之实现烟花效果的代码案例

Mar 30, 2017 pm 01:22 PM

1、效果

1321.png

2、代码解析

(1)requestAnimationFrame

requestAnimationFrame是浏览器用于定时循环操作的一个接口,类似于setTimeout,主要用途是按帧对网页进行重绘。

设置这个API的目的是为了让各种网页动画效果(DOM动画、Canvas动画、SVG动画、WebGL动画)能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。代码中使用这个API,就是告诉浏览器希望执行一个动画,让浏览器在下一个动画帧安排一次网页重绘。

requestAnimationFrame的优势,在于充分利用显示器的刷新机制,比较节省系统资源。显示器有固定的刷新频率(60Hz或75Hz),也就是说,每秒最多只能重绘60次或75次,requestAnimationFrame的基本思想就是与这个刷新频率保持同步,利用这个刷新频率进行页面重绘。此外,使用这个API,一旦页面不处于浏览器的当前标签,就会自动停止刷新。这就节省了CPU、GPU和电力。

不过有一点需要注意,requestAnimationFrame是在主线程上完成。这意味着,如果主线程非常繁忙,requestAnimationFrame的动画效果会大打折扣。

requestAnimationFrame使用一个回调函数作为参数。这个回调函数会在浏览器重绘之前调用。

requestID = window.requestAnimationFrame(callback);
登录后复制

目前,高版本浏览器Firefox 23 / IE 10 / Chrome / Safari)都支持这个方法。可以用下面的方法,检查浏览器是否支持这个API。如果不支持,则自行模拟部署该方法。

window.requestAnimFrame = (function(){      
return  window.requestAnimationFrame       || 
              window.webkitRequestAnimationFrame || 
              window.mozRequestAnimationFrame    || 
              window.oRequestAnimationFrame      || 
              window.msRequestAnimationFrame     || 
              function( callback ){
                window.setTimeout(callback, 1000 / 60);
              };
})();
登录后复制

上面的代码按照1秒钟60次(大约每16.7毫秒一次),来模拟requestAnimationFrame。

使用requestAnimationFrame的时候,只需反复调用它即可。

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

requestAnimationFrame(repeatOften);
登录后复制

取消重绘可以用 cancelAnimationFrame。

window.cancelAnimationFrame(requestID);
登录后复制

它的参数是requestAnimationFrame返回的一个代表任务ID的整数值。

(2)准备画版(canvas)

判断浏览器是否支持canvas,并把宽高设置为浏览器窗口大小。

var canvas = document.getElementById("myCanvas");
if (!canvas.getContext) {    return;
}
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;var ctx = canvas.getContext("2d");
登录后复制

(3)烟花对象(FireWork)

烟花效果可以简单地认为是围绕一个点,爆炸产生很多小球向边上扩散。因此需要一个烟花对象,这个对象主要是记录烟花绽放的位置和周围小球的信息。所以我们的烟花对象定义如下。

function FireWork() {    
this.x = -1;    
this.y = -1;    
this.balls = [];
}
登录后复制

那这个对象应该有哪些方法呢?

首先,要创建爆炸产生的小球。

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));
        }
    }
登录后复制

注:这里fwx为烟花位置X轴坐标,fwy为烟花位置Y轴坐标,下同。

这里小球的运行长度为 50 到200 的随机值,小球运行轨迹起点为烟花位置,终点在一个圆上随机的一点。

然后,要对烟花进行初始化,主要是确定位置,产生小球。

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;
    }
登录后复制

注:这里drawCount为绘制次数,currBallIndex为当前绘制的小球索引

整个FireWork定义如下。

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));
        }
    }
}
登录后复制

(4)爆炸产生的小球对象(Ball)

小球需要知道自己的起点和终点的位置,所以定义如下。

function Ball(bx, by, ex, ey) {    this.bx = bx;//起点X轴坐标
    this.by = by;//起点Y轴坐标
    this.ex = ex;//终点X轴坐标
    this.ey = ey;//终点Y轴坐标}
登录后复制

小球还要能根据当前绘制的次数和总绘制次数计算得到当前坐标和下一次绘制坐标,这两个坐标连接起来的直线就是本次要绘制的内容,所以定义如下。

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;
    }
}
登录后复制

(5)全局变量及工具方法

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

然后还要几个工具方法。

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);
}
登录后复制

(6)绘制方法

最后还剩一个供 requestAnimationFrame 调用的绘制方法。这个绘制方法就是根据当前的绘制次数,拿到爆炸小球的路径(包含起点和终点的一条线段),然后把上一次绘制的路径擦除。

当一个烟花的效果绘制完成后,进行下一个烟花的绘制。

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);
}
登录后复制

这里颜色取的是随机值。

(7)启动绘制

最后就是启动绘制。

currFW = new FireWork();
currFW.run();
requestAnimationFrame(drawLine);
登录后复制

(8)全部代码。

全部代码如下,共160行。

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);
登录后复制

欢迎讨论。

 

以上是HTML5 Canvas实战之实现烟花效果的代码案例的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

HTML 中的表格边框 HTML 中的表格边框 Sep 04, 2024 pm 04:49 PM

HTML 表格边框指南。在这里,我们以 HTML 中的表格边框为例,讨论定义表格边框的多种方法。

HTML 中的嵌套表 HTML 中的嵌套表 Sep 04, 2024 pm 04:49 PM

这是 HTML 中嵌套表的指南。这里我们讨论如何在表中创建表以及相应的示例。

HTML 左边距 HTML 左边距 Sep 04, 2024 pm 04:48 PM

HTML 左边距指南。在这里,我们讨论 HTML margin-left 的简要概述及其示例及其代码实现。

HTML 表格布局 HTML 表格布局 Sep 04, 2024 pm 04:54 PM

HTML 表格布局指南。在这里,我们详细讨论 HTML 表格布局的值以及示例和输出。

HTML 输入占位符 HTML 输入占位符 Sep 04, 2024 pm 04:54 PM

HTML 输入占位符指南。在这里,我们讨论 HTML 输入占位符的示例以及代码和输出。

HTML 有序列表 HTML 有序列表 Sep 04, 2024 pm 04:43 PM

HTML 有序列表指南。在这里我们还分别讨论了 HTML 有序列表和类型的介绍以及它们的示例

在 HTML 中移动文本 在 HTML 中移动文本 Sep 04, 2024 pm 04:45 PM

HTML 中的文本移动指南。在这里我们讨论一下marquee标签如何使用语法和实现示例。

HTML onclick 按钮 HTML onclick 按钮 Sep 04, 2024 pm 04:49 PM

HTML onclick 按钮指南。这里我们分别讨论它们的介绍、工作原理、示例以及各个事件中的onclick事件。

See all articles