资讯专栏INFORMATION COLUMN

程序员的小浪漫----烟火

roundstones / 958人阅读

摘要:多代码,慎读预览完整项目预览预览地址属性设计烟花状态烟花应有三个状态升空等待炸裂炸裂后烟花发射点,爆炸点,升空后等待炸裂时间,炸裂后微粒个数,烟花半径烟花炸裂后微粒自身位置,自身大小,自身速度,最大烟花半径。

多代码,慎读!!!

预览

完整项目预览----预览地址;

属性设计

烟花状态:烟花应有三个状态:

升空

等待炸裂

炸裂后

烟花:发射点(x, y),爆炸点(xEnd, yEnd),升空后等待炸裂时间(wait),炸裂后微粒个数(count),烟花半径(radius)

烟花炸裂后微粒:自身位置(x, y),自身大小(size),自身速度(rate),最大烟花半径(radius)。

config:为全局变量,以及控制参数,包括画布宽高,设定烟花属性等。

设定全局变量
const config = {
    width: 360,
    height: 600,
    canvases: ["bg", "firework"],
    skyColor: "210, 60%, 5%, 0.2)",
    fireworkTime:{min:30,max:60},
    //烟花参数本身有默认值 传入undefined则使用默认参数
    fireworkOpt:{
        x: undefined,
        y: undefined,
        xEnd: undefined,
        yEnd: undefined,
        count: 300,   //炸裂后粒子数
        wait: undefined,  //消失后 => 炸裂  等待时间
    }
}
构建微粒类
class Particle{
    //默认值写法 
    constructor({x, y, size = 1, radius = 1.2} = {}){
        this.x = x;
        this.y = y;
        this.size = size;
        
        this.rate = Math.random(); //每个微粒移动的速度都是随机不同的
        this.angle = Math.PI * 2 * Math.random(); //每个微粒的偏移角度
        
        //每次微粒移动速度分解为横纵坐标的移动。
        this.vx = radius * Math.cos(this.angle) * this.rate; 
        this.vy = radius * Math.sin(this.angle) * this.rate;
    }

    go(){
        this.x += this.vx;
        this.y += this.vy; 
        this.vy += 0.02; //重力影响 y越大实际越偏下
        
        //空气阻力
        this.vx *= 0.98;
        this.vy *= 0.98;
    }
    
    //画出微粒的位置
    render(ctx){
        this.go();
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2, false);
        ctx.fill();
    }
}
构建烟花类
class Firework{
    constructor({x, y = config.height, xEnd, yEnd, count = 300, wait} = {}){
        //烟花自身属性
        this.x = x || config.width / 8 + Math.random() * config.width * 3 / 4; 
        this.y = y;
        this.xEnd = xEnd || this.x;
        this.yEnd = yEnd || config.width / 8 + Math.random() * config.width * 3 / 8;
        this.size = 2;
        this.velocity = -3;
        
        this.opacity = 0.8;
        this.color = `hsla(${360 * Math.random() | 0},80%,60%,1)`;
        this.wait = wait || 30 + Math.random() * 30;
        //微粒个数等
        this.count = count;
        this.particles = [];
        this.createParticles();
    
        this.status = 1;
    }
    //创建微粒
    createParticles(){
        for(let i = 0;i < this.count; ++i){
            this.particles.push(new Particle({x:this.xEnd, y:this.yEnd}));
        }
    }
    //升空
    rise(){
        this.y += this.velocity * 1;
        this.velocity += 0.005; //升空时产生的阻力
        //烟花升空到目标位置开始渐隐
        if(this.y - this.yEnd <= 50){
            this.opacity = (this.y - this.yEnd) / 50;
        }
        //如果到了目标位置 就开始第二个状态
        if(this.y <= this.yEnd){
            this.status = 2;
        }
    }
    
    //渲染烟花  烟花所有动作完成之后返回false
    render(ctx){
        switch(this.status){
            case 1: //升空
            ctx.save();
            ctx.beginPath();
            ctx.globalCompositeOperation = "lighter";
            ctx.globalAlpha = this.opacity;
            ctx.translate(this.x, this.y);
            ctx.scale(0.8, 2.3);
            ctx.translate(-this.x, -this.y);
            ctx.fillStyle = this.color;
            ctx.arc(this.x + Math.sin(Math.PI * 2 * Math.random()) / 1.2, this.y, this.size, 0, Math.PI * 2, false);
            ctx.fill();
            ctx.restore();
    
            this.rise();
            return true;
            break;
            case 2: //烟花消失阶段,等待炸裂
            if(--this.wait <= 0){
                this.opacity = 1;
                this.status = 3;
            }
            return true;
            break;
            case 3: //炸裂之后 渲染烟花微粒
            ctx.save();
            ctx.globalCompositeOperation = "lighter";
            ctx.globalAlpha = this.opacity;
            ctx.fillStyle = this.color;
            for(let i = 0;i < this.particles.length;++i){
            this.particles[i].render(ctx);
            }
            ctx.restore();
            this.opacity -= 0.01;
            return this.opacity > 0;
            break;
            default: 
            return false;
        }
    }
}
放烟花
const canvas = {
    init: function(){
        //一些属性的设定 可以不用管
        this.setProperty();
        this.renderBg();
        
        //循环体 **主要
        this.loop();
    },
    setProperty: function(){
        this.fireworks = [];
        this.width = config.width;
        this.height = config.height;
        this.fireworkTime = (config.fireworkTime.min + (config.fireworkTime.max - config.fireworkTime.min) * Math.random()) | 0;
    
        this.bgCtx = document.querySelector("#bg").getContext("2d");
        this.fireworkCtx = document.querySelector("#firework").getContext("2d");
    },
    renderBg(){
        this.bgCtx.fillStyle = "hsla(210, 60%, 5%, 0.9)"
        this.bgCtx.fillRect(0, 0, this.width, this.height);
    },
    
    loop(){
        requestAnimationFrame(this.loop.bind(this));
        this.fireworkCtx.clearRect(0, 0, this.width, this.height);
        
        //随机创建烟花
        if(--this.fireworkTime <= 0){
            this.fireworks.push(new Firework(config.fireworkOpt));
            //每次到点之后重新设置烟花产生时间 (|0转化为整数)
            this.fireworkTime = (config.fireworkTime.min + (config.fireworkTime.max - config.fireworkTime.min) * Math.random()) | 0;
        }
    
        for(let i = this.fireworks.length - 1; i >= 0; --i){
            //渲染烟花 (若返回值为false则移除烟花)
            !this.fireworks[i].render(this.fireworkCtx) && this.fireworks.splice(i,1);    
        }
    
    }
}
canvas.init();
完善

此时烟花是这样的,感觉少了点小尾巴。

现在我们每一帧都是清除了画布,如果要加上小尾巴其实也很简单,每一帧都不要清除画布,而是覆盖一层新的有透明度的天空上去。

//canvas.loop方法

// this.fireworkCtx.clearRect(0, 0, this.width, this.height);
this.fireworkCtx.fillStyle = config.skyColor;
this.fireworkCtx.fillRect(0,0,this.width,this.height);    

这时就变成这样了。

但是,还是缺少了在爆炸瞬间 天空变亮的场景。

那么在画烟花的时候,先会获取一下烟花的颜色以及透明度。

// *****Firework constructor
// this.color = `hsla(${360 * Math.random() | 0},80%,60%,1)`;
this.hue = 360 * Math.random() | 0;
this.color = `hsla(${this.hue},80%,60%,1)`;
// *****Firework 新增实例方法
getSkyColor(){
    const skyColor = {
        //只有炸裂阶段才返回亮度
        lightness: this.status == 3 ? this.opacity : 0 ,
        hue: this.hue
    };
    return skyColor;
}
// *****config 修改config的skyColor
// skyColor: "hsla(210, 60%, 5%, 0.2)",
skyColor: "hsla({hue}, 60%, {lightness}%, 0.2)",
// canvas.loop方法
//this.fireworkCtx.fillStyle = config.skyColor;
//每次替换色调与亮度值。
this.fireworkCtx.fillStyle = config.skyColor.replace("{lightness}", 5 + this.skyColor.lightness * 15).replace("{hue}" , this.skyColor.hue);

this.skyColor = { //新增
    lightness: 0,
    hue: 210
};
for(let i = this.fireworks.length - 1; i >= 0; --i){
    //新增 天空颜色为最亮的烟花的颜色
    this.skyColor = this.skyColor.lightness >= this.fireworks[i].getSkyColor().lightness ? this.skyColor : this.fireworks[i].getSkyColor();
    !this.fireworks[i].render(this.fireworkCtx) && this.fireworks.splice(i,1);    
}

到现在就算是大功告成了。

完整项目

github项目地址

如果觉得还不错,请star一个吧。

烟花制作参考链接

参考了codepen.io上的很多作品。

主要参考 --- fireworks seen in the countryside

fireworks seen in the countryside

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107572.html

相关文章

  • 序员小浪----文字粒子效果

    摘要:预览完整项目预览预览地址粒子效果原理在中,可以通过方法来获取像素数据。下例是通过改变像素的数据而重新写出来的文字。不过可能会造成文字部分地方缺失。烟花效果可以看一下我的上一篇,程序员的小浪漫烟火完整项目项目地址如果觉得还不错,请一个吧。 预览 showImg(https://segmentfault.com/img/remote/1460000013379709?w=476&h=361...

    xiaodao 评论0 收藏0
  • IT巨头互掐云计算:安全性和耗能存争议

    摘要:问题安全性和耗能存争议云计算已经成为全球未来信息产业发展的战略方向,随着各国都认真研究云计算将为社会和经济发展模式带来的变革,部署国家战略,中国也正在推动云从概念走向应用。   身边就是一片云  ■ 体验  白领萧潇(化名)对任何科技新知都难以表现出兴趣,云?听说过,是什么?很快她就投入到了对附近商场打折活动的热切关注中。其实,像萧潇一样,我们往往享受到了云的便利,却又对它视而不见。每一天,...

    Miracle_lihb 评论0 收藏0
  • IT巨头互掐云计算:安全性和耗能存争议

    摘要:问题安全性和耗能存争议云计算已经成为全球未来信息产业发展的战略方向,随着各国都认真研究云计算将为社会和经济发展模式带来的变革,部署国家战略,中国也正在推动云从概念走向应用。   身边就是一片云     体验   白领萧潇(化名)对任何科技新知都难以表现出兴趣,云?听说过,是什么?很快她就投入到了对附近商场打折活动的热切关注中。其实,像萧潇一样,我们往往享受到了云的便利,却又对它视而不见。每一...

    libxd 评论0 收藏0
  • 云计算将被大范围滥用 耗能存争议

    摘要:预计年全国云计算产业链规模可能达到亿至一万亿元人民币,有望占到年战略性新兴产业以上的产值规模。但也有分析人士认为,云计算可以节省大量企业的硬件成本和软件成本,而云计算服务器所耗费的能源应该不会多于大量企业节省的能源。 云在哪里?其实,云计算技术在网络服务中已经随处可见,例如云桌面、云办公、云视频、云游戏、云搜索、云识别、云U盘等。用户不必了解其背后的运行原理,也无需自备一些硬件和软件,就可以...

    Lucky_Boy 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<