思路分析
想要编写一个烟花程序,首先要了解一个烟花燃放的过程。
可以看到烟花燃放时,内侧的火花较暗而外层的火花明亮。烟花在燃放时会将火药炸开,这些火药飞溅被点燃就出现了烟花,通过加入一些金属使烟花带有丰富的色彩。编写一个烟花程序就是模拟这个爆炸的过程。首先在屏幕上实现丰富色彩很简单(rgba,三元色+透明度),重要的就是模拟爆炸时的这些火药飞溅的情况。这种飞溅的火花效果可以通过一些简单的图形(圆形、方形)移动和变化来得到。分析到这些就可以开始制作了。
这是大体的思路,如果要提高烟花的逼真程度还是需要一些细节,更仔细的观察烟花爆炸的过程。下面是我自己尝试做的一个demo的图片
创建一个画布
没啥好说的在html中创建一个画布,设置背景为黑色。
代码:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta HTTP-EQUIV="pragma" CONTENT="no-cache">
<meta HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate">
<meta HTTP-EQUIV="expires" CONTENT="0">
<title>烟花</title>
</head>
<body>
<canvas style="border: 1px solid; background:rgba(0, 0, 0, 1);">
您的浏览器不支持HTML5画布!
</canvas>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
编写js代码
创建火花对象
要知道一个烟花是有很多的火花组成的,首先创建火花对象。火花对象应该有的属性:它的颜色和透明度(rgba)、它的位置和大小、它的移动速度和加速度(火花不是一直移动的用加速度改变它的速度)、它的移动方向这些都是基本的一些属性在后面编写过程中还有添加。每一个火花都是一个画布然后将火花按一定方式绘制在主画布上。
/*火花对象*/
function Fire(x, y, size, direction, rgb, a, type){
var Fire = Object();
= ("canvas");//创建画布
= ("2d");
= x; //初始位置
= y;
= rgb; //颜色属性
= a; //透明度
= type;//我写了两种火花的类型圆形和方形
= 5 + () * 5; //随机初始速度
= 1;//加速度
= true;
= false; //大小转换标志
=direction;//方向
= size; //火花大小
=10;
=10;
= + ()+")"; //rgba颜色和透明度属性
= (5, 5, 0, 5, 5, 10);
( 0, 'rgba(255,255,255,1)' );
( 0.4, );
( 1, 'rgba(0,0,0,0)' );
=;
(0, 0, , );
return Fire;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
建立烟花
一个烟花就是一系列的火花,因此建立一个火花数组即为一个烟花。
代码:
var yanhua = new Array();//烟花数组(这是多个烟花相当于多个火花数组)
/*通过创建火花实例来组建烟花*/
function load_yanhua(x, y, count){//count为火花个数,x、y为位置
var i;
var fires = new Array();//火花数组即一个烟花
var type = (());
var random = (());
if(random == 0){
var rgb = create_color();//生成随机颜色
for(i=0;i<count;i++){
direction = () * * 2;
(Fire(x, y, 15, direction, rgb, 1, type));
}
}
else{
for(i=0;i<count;i++){
var rgb = create_color();
direction = () * * 2;
(Fire(x, y, 15, direction, rgb, 1, type));
}
}
return fires;//返回一个火花数组(即一个烟花)
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
随机颜色函数代码:
/*生成颜色颜色*/
function create_color(){
var r = (() * 255);
var g = (() * 255);
var b = (() * 255);
rgb = "rgba("+ ()+ "," + () +"," + () + ",";
return rgb;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
拖影效果
有了主火花当然也要有火花后面的拖影。拖影的属性应该与火花的属性大体相同它的透明度比火花的小,它的size比火花的要小(这个我写的比较糙按照真实的模拟应该是最外层的火花最大越向内侧越小,我就简化成了外面的主火花比拖影大了)
拖影对象代码:
function sprite(x, y, direction, size, rgb, a, type){//拖尾效果
var sprite=new Object();
=x,
=y;
=a;
=rgb;
= type;
=size;
= direction;
= rgb + () +")";
return sprite;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
拖影数组:
当主火花移动后在它的后面添加拖影。
function add_sprite(){
var i, j;
for(i=0;i<;i++){//i指的是第i个烟花,j指的是第i个烟花的第j个火花
for(j=0; j<yanhua[i].length; j++){
if(yanhua[i][j].flag){//主火花移动,拖影移动
sprites[i].push(sprite(yanhua[i][j].x + 6, yanhua[i][j].y + 6, yanhua[i][j].direction,yanhua[i][j].size, yanhua[i][j].rgb, 1, yanhua[i][j].type));
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
火花移动
火花移动通过改变火花的位置然后重新在画布上绘制。烟花的火花大小是先减小然后在变大,这样就呈现出了爆炸的效果。
火花移动代码:
/*火花移动*/
function fire_move(){
var i,j;
for(i=0; i<; i++){
for(j=0; j<yanhua[i].length; j++){
if(yanhua[i][j].size < 0){ /*火花先变小后变大*/
yanhua[i][j].translate = true;//由小变大,翻转标志
}
if(yanhua[i][j].translate && yanhua[i][j].size < 5){
yanhua[i][j].size += 2.5;
}
else if(!yanhua[i][j].translate){
yanhua[i][j].size -= 2;
}
/*速度为0删除元素*/
yanhua[i][j].y += 3;
if(yanhua[i][j].v < 0){
yanhua[i][j].flag = false;
}
else{
if(yanhua[i][j].a <= 0.2){
yanhua[i][j].a = 0.2;
}
else{
yanhua[i][j].a -= 0.5;
}
yanhua[i][j].v -= yanhua[i][j].a;
var move_x = yanhua[i][j].x + yanhua[i][j].v * (yanhua[i][j].direction);//变长
var move_y = yanhua[i][j].y + yanhua[i][j].v * (yanhua[i][j].direction);
yanhua[i][j].x = move_x;
yanhua[i][j].y = move_y;
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
拖影移动和颜色变化
拖影是随着火花的移动而移动的它的位置坐标略落后与主火花。它的透明度会随着时间变化呈现出烟花外围明亮内部暗的效果。同时这部分还有清除燃放完的烟花对象的功能,从火花移动得到火花停止移动的标志然后等待如果所有的拖影透明度归为0则清理燃放过的烟花。
/*拖影移动*/
function sprite_move(){
var i,j;
for(i=0; i<; i++) {
for(j=0; j<sprites[i].length; j++){
var color = sprites[i][j].rgb + sprites[i][j].() + ")";
if(sprites[i][j].type == 0){//方形火花
draw_ctx.fillStyle = color;
draw_ctx.fillRect(sprites[i][j].x-sprites[i][j].size/2, sprites[i][j].y-sprites[i][j].size /2, sprites[i][j].size/2, sprites[i][j].size/2); //绘制拖尾*/
}
else if(sprites[i][j].type == 1){//圆形火花,使用圆头直线做的
draw_ctx.beginPath();
draw_ctx.lineWidth = sprites[i][j].size / 2;
draw_ctx.lineCap = "round";
draw_ctx.moveTo(sprites[i][j].x-sprites[i][j].size/2, sprites[i][j].y-sprites[i][j].size/2);
var line_x = sprites[i][j].x-sprites[i][j].size/2 + (sprites[i][j].direction);
var line_y = sprites[i][j].y-sprites[i][j].size/2 + (sprites[i][j].direction);
draw_ctx.lineTo( line_x, line_y);
draw_ctx.strokeStyle = color;
draw_ctx.stroke();
}
}
}
}
/*拖影颜色变淡*/
function change_color(){
var i,j, k;
for(i=0; i<; i++){
for( j=0; j<yanhua[i].length; j++){
if(yanhua[i][j].flag){//移动时
for(k=0; k<sprites[i].length; k++){//减小透明度
if(sprites[i][k].a <= 0.2){
sprites[i][k].a = 0.2;
}
else{
sprites[i][k].a -= 0.001;
}
}
}
else{//停止移动时
for(k=0; k<sprites[i].length; k++){
if(sprites[i][k].a <= 0){
sprites[i].splice(k, 1);
yanhua[i].splice(k, 1);
}else{
sprites[i][k].a -= 0.001;
}
}
}
}
if(yanhua[i].length == 0){
(i, 1);
(i, 1);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
缓存烟花数组和绘制缓存的烟花画布
function load(){//创建烟花数组
if( < 2){//屏幕上最多有两个烟花在燃放
var x = () * window_width;随机烟花位置
var y = () * window_height;
(load_yanhua(x, y, 50));//创建一个烟花,火花数量为50
(sprite_childs);//创建拖影数组
}
}
/*绘制缓存画布*/
function draw_yanhua(){
var i,j;
var draw_canvas = ("canvas");
draw_ctx = draw_canvas.getContext("2d");
draw_ctx.clearRect( 0, 0, window_width, window_height);
draw_canvas.width=window_width;
draw_canvas.height=window_height;
fire_move();
add_sprite();
sprite_move()
for(i=0; i<; i++){
for(j=0;j<yanhua[i].length;j++){
draw_ctx.drawImage(yanhua[i][j].canvas,yanhua[i][j].x, yanhua[i][j].y, yanhua[i][j].size, yanhua[i][j].size);
}
}
return draw_canvas;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
在主画布上绘制
/*向主画布上绘制*/
function run(){
var update_canvas = draw_yanhua();
( 0, 0, window_width, window_height);
(update_canvas, 0, 0, window_width, window_height);
}
- 1
- 2
- 3
- 4
- 5
- 6
加载运行函数
= function(){
mycanvas = ("mycanvas");
window_width = ;
window_height = ;
/*设置主画布大小*/
= window_width;
= window_height;
ctx = ("2d");
setInterval(load, 120);
setInterval(run, 100);
setInterval(change_color, 100);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
到这里编写就结束了。
总结
这个demo供大家参考,具体的位置、颜色、透明度、火花个数、烟花的消失延时效果等可以根据情况自行调整。
这个demo的源代码可以在****资源里下载(不要积分的哦)。