欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

canvas制作的烟花效果

程序员文章站 2023-11-17 15:31:22
最近感觉canvas挺有意思的,在业余时间没事研究了一下,参考过网上一些思路,话不多说,开始啦。 github地址:https://github.com/aWhiteBear/fireworks 演示地址:https://awhitebear.github.io/fireworks/ 图片效果如下: ......

最近感觉canvas挺有意思的,在业余时间没事研究了一下,参考过网上一些思路,话不多说,开始啦。

github地址:https://github.com/awhitebear/fireworks

演示地址:

图片效果如下:(右上角能显示fps,是时候看看你电脑的性能了哈哈~)

canvas制作的烟花效果

 

先说一下思路吧,其实很简单,烟花分为两个部分:1.窜天猴。2.爆炸效果。  当穿天猴的竖直方向的速度为0的时候,让它爆炸。

下面是一些常量,后面类中会用到的

 1 const canvas = document.getelementbyid('canvasnode');
 2 const ctx = canvas.getcontext('2d');
 3 canvas.width = window.innerwidth;
 4 canvas.height = window.innerheight;
 5 const canvas_width = canvas.width;
 6 const canvas_height = canvas.height;
 7 const gravitational = 2.5; // 模拟重力加速度
 8 const air_resistance = 1; // 模拟空气阻力
 9 const every_firework_time = 3; // 每个烟花的持续时间,单位:秒
10 const frame = 60;
class flyingmonkey{ // 窜天猴,发射升空的,目前只能垂直发射
    constructor(x,y,speedx,speedy){
        this.x = x; // x,y 是窜天猴的位置坐标
        this.y = y;
        this.speedx = speedx;
        this.speedy = speedy;
        this.opacity = 1;
        this.count = 50; // 窜天猴和其尾巴由这50个圆绘制而成
        for(let i=0;i<this.count;i++){
            this.createcircle(i);
        }
    }
    createcircle(i) { // 创建窜天猴的圈圈尾巴
        ctx.beginpath();
        ctx.save();
        ctx.globalcompositeoperation = 'lighter';
        ctx.fillstyle = `rgba(245,123,63,${this.opacity})`;
        ctx.arc(this.x + (math.random()-0.5) * i/10 + i/this.count * this.speedx, this.y + i/this.count * this.speedy ,8 - (6 * i/this.count),0,2 * math.pi);
        ctx.fill();
        ctx.restore();
        ctx.closepath();
    }
}

上面是窜天猴类,就是最开始向上发射的烟花,下面烟花类

class firework { // 烟花,爆炸的
    constructor(x,y,speedx,speedy){
        this.x = x;
        this.y = y;
        this.speedx = speedx;
        this.speedy = speedy;
        this.opacity = 1;
        this.count = 500; // 烟花的爆炸效果由500个点组成
        this.allfireworks = [];

        this.createallfirework();
        launch.arrfirework.push(this);
    }
    createallfirework(){
        let r = math.floor(math.random()*256), g = math.floor(math.random()*256) , b =math.floor(math.random()*256);
        for(let i=0;i<this.count;i++){
            this.allfireworks.push({
                r,g,b,
                x:this.x,
                y:this.y,
                opacity:1,
                speedx:this.speedx * i/this.count*10 *(math.random()-0.5),
                speedy:this.speedy * i/this.count*10 *(math.random()-0.5)
            });
        }
        this.updateallfirework();
    }
    updateallfirework(){
        for(let i=0;i<this.allfireworks.length;i++){
            let {r,g,b,x,y,speedx,speedy,opacity} = this.allfireworks[i];
            this.allfireworks[i].y = y - speedy/frame;
            this.allfireworks[i].x = x - speedx/frame;
            this.allfireworks[i].opacity = opacity - 1/ frame / every_firework_time;
            this.allfireworks[i].speedy = speedy - gravitational;
            if(math.abs(speedx)>3/frame) { // 速度<= 3/frame 认为停止了
                this.allfireworks[i].speedx = speedx - (speedx>0?air_resistance:(air_resistance*(-1)));
            } else {
                this.allfireworks[i].speedx = 0;
            }
            ctx.beginpath();
            ctx.save();
            ctx.globalcompositeoperation = 'lighter';
            ctx.fillstyle = `rgba(${r},${g},${b},${this.allfireworks[i].opacity})`;
            ctx.arc(this.allfireworks[i].x , this.allfireworks[i].y  ,2,0,2 * math.pi);
            ctx.fill();
            ctx.restore();
            ctx.closepath();
        }
    }
}

下面是start类,用来发射窜天猴

class start{
    constructor(x,y,speedx,speedy){
        launch.arrflyingmonkey.push(this);
        this.x = x;
        this.y = y;
        this.speedx = speedx;
        this.speedy = speedy;
        this.begin();
    }
    begin(){
        this.y = this.y - this.speedy/frame; // 速度减小
        this.x = this.x - this.speedx/frame;
        this.speedy = this.speedy - gravitational;
        new flyingmonkey(this.x, this.y, this.speedx, this.speedy);
    }
}

下面是发射类,是用【requestanimationframe】来渲染的动画

class launch{ // 发射
    constructor(){
        this.fps=0;
        this.sum=0;// 帧数计数器 60帧一循环
        this.draw = this.draw.bind(this);
        this.draw();
    }
    draw(){
        ctx.clearrect(0,0,canvas_width,canvas_height);
        this.updatefps();
        launch.arrflyingmonkey.foreach((item,i)=>{
            item.begin();
            if(item.speedy < 0){
                launch.arrflyingmonkey.splice(i,1);
                new firework(item.x,item.y,7*7,5*7); // 烟花宽高比:7:5
            }
        });
        launch.arrfirework.foreach((item,i)=>{
            item.updateallfirework();
        });
        if(launch.arrfirework.length>5){ // 清理arrfirework,避免占用过多内存,其实还可以通过 every_firework_time 和 launch.timer 更及时清理。length > every_firework_time/launch.timer
            launch.arrfirework.shift();
        }
        requestanimationframe(this.draw);
    }
    updatefps(){
        if(this.sum++>=60){
            this.sum = 0;
            let nowtime = new date().gettime();
            this.fps = 60/(nowtime - launch.lasttime) *1000;
            launch.lasttime = nowtime;
        }
        ctx.save();
        ctx.fillstyle = 'red';
        ctx.font="20px arial";
        ctx.filltext(`fps: ${~~this.fps}`,canvas_width - 100,50);
        ctx.restore();
    }
}

然后添加launch静态属性

/** 添加launch静态属性*/
launch.arrflyingmonkey = [];
launch.arrfirework = [];
launch.timer = setinterval(()=>{
    new start(canvas_width * (math.random() * 0.8 + 0.1),canvas_height * 0.9,0,300 *(math.random()*0.5 + 1));
},1500);
launch.lasttime = new date().gettime();

最后在  new launch(); 就能发射出去啦。

代码还有好多可以优化的地方,在一些手机浏览器上会出现fps越来越低得到情况,画面会变卡,以后可能的话要在进行优化一下,也可以和大家讨论一下如何优化会更好,可以在评论区指导一下呀,感谢大家提出宝贵的意见~