情人节快到了,一起来看烟花吧🎆🎆

新年来临之前,业务需求要求整一个烟花特效,对canvas研究不多,翻遍GitHub,找到了一个合眼缘的烟花效果,拿到源码了咱们来根据需求进行特制改造。 此库的效果展示,甚合我心,在情人节前夕,分享给各位有心人。

1.结合cursor达成业务需求

最终效果

2.结合cursor快速理解源码

只研究烟花特效

  • 将sound去掉
  • 将state去掉
  • 只保留canvas元素

分12种烟花效果

arduino 复制代码
 const shellTypes = {
    Random: randomShell,  //随机 剩余11种里边随机挑选一种
    Crackle: crackleShell, // 裂纹
    Crossette: crossetteShell, 
    Crysanthemum: crysanthemumShell, //菊花型
    "Falling Leaves": fallingLeavesShell,
    Floral: floralShell,
    Ghost: ghostShell, //幽灵效果
    "Horse Tail": horsetailShell,
    Palm: palmShell, //棕榈型
    Ring: ringShell, //环形
    Strobe: strobeShell, //闪烁效果
    Willow: willowShell, //柳树型
  }
  //因为是白色背景的烟花,换成其他颜色,根据业务需要搭配色值
  const COLOR = {
    Pink: "#FFEAB2",
    Yellow: "#F6FF3D",
    Orange: "#FF7E3D",
  };

Crackle:

Crossette:

Crysanthemum

Falling Leaves

Floral

Ghost

Horse Tail

Palm

Ring

#### Strobe
#### Willow

烟花绽放效果源码分析

  #### Shell - 烟花主体类
kotlin 复制代码
class Shell {
    constructor(options) {
        // 将传入的所有选项复制到实例上
        Object.assign(this, options);
        // 设置星星生命周期变化范围,默认为0.125
        this.starLifeVariation = options.starLifeVariation || 0.125;
        // 设置烟花颜色,如果没有指定则随机选择
        this.color = options.color || randomColor();
        // 设置闪光颜色,默认与主体颜色相同
        this.glitterColor = options.glitterColor || this.color;
        // 如果没有指定星星数量,则根据扩散大小计算
        if (!this.starCount) {
            const density = options.starDensity || 1;
            const scaledSize = this.spreadSize / 54;
            this.starCount = Math.max(6, scaledSize * scaledSize * density);
        }
    }

}

Shell 类支持的主要配置选项包括:
1.

  ##### 基本参数

  ```javascript
  {
      spreadSize: Number,     // 爆炸扩散范围大小
      starCount: Number,      // 星星数量(可选)
      starLife: Number,       // 星星生命周期
      starLifeVariation: Number, // 生命周期变化范围
      color: String,          // 烟花颜色
      glitterColor: String    // 闪光颜色
  }
  ```
  ##### 特效参数
javascript 复制代码
{
    glitter: String,        // 闪光效果:'light','medium','heavy','streamer','willow'
    pistil: Boolean,        // 是否有中心点
    pistilColor: String,    // 中心点颜色
    streamers: Boolean,     // 是否有流光效果
    crossette: Boolean,     // 是否有十字交叉效果
    floral: Boolean,        // 是否有花朵效果
    crackle: Boolean       // 是否有爆裂声效果
}
  1. 主要方法:
ini 复制代码
 // 发射烟花
launch(position, launchHeight) {
    const width = stageW;
    const height = stageH;
    const hpad = 60;        // 屏幕边缘保留空间
    const vpad = 50;        // 顶部保留空间
    const minHeightPercent = 0.45;
    
    // 计算发射和爆炸位置
    const launchX = position * (width - hpad * 2) + hpad;
    const burstY = minHeight - launchHeight * (minHeight - vpad);
    
    // 直接在目标位置爆炸
    this.burst(launchX, burstY);
}

// 爆炸效果
burst(x, y) {
    // 计算扩散速度
    const speed = this.spreadSize / 96;
    
    // 创建爆炸效果
    createBurst(this.starCount, (angle, speedMult) => {
        // 创建星星粒子
        const star = Star.add(
            x, y,
            this.color,
            angle,
            speedMult * speed,
            this.starLife + Math.random() * this.starLife * this.starLifeVariation
        );
        
        // 添加特效
        if (this.glitter) {
            star.sparkFreq = sparkFreq;
            star.sparkSpeed = sparkSpeed;
            star.sparkLife = sparkLife;
            star.sparkLifeVariation = sparkLifeVariation;
            star.sparkColor = this.glitterColor;
            star.sparkTimer = Math.random() * star.sparkFreq;
        }
    });
}
  1. 特效效果处理
javascript 复制代码
 // 处理双色烟花
if (Array.isArray(this.color)) {
    // 创建半圆形双色效果
    if (Math.random() < 0.5) {
        const start = Math.random() * Math.PI;
        const start2 = start + Math.PI;
        const arc = Math.PI;
        // 分别创建两种颜色的半圆
        createBurst(this.starCount, starFactory, start, arc);
        createBurst(this.starCount, starFactory, start2, arc);
    } 
    // 创建混合双色效果
    else {
        createBurst(this.starCount / 2, starFactory);
        createBurst(this.starCount / 2, starFactory);
    }
}
  1. 附加效果
kotlin 复制代码
 // 中心点效果
if (this.pistil) {
    const innerShell = new Shell({
        spreadSize: this.spreadSize * 0.5,
        starLife: this.starLife * 0.6,
        color: this.pistilColor,
        glitter: "light"
    });
    innerShell.burst(x, y);
}

// 流光效果
if (this.streamers) {
    const innerShell = new Shell({
        spreadSize: this.spreadSize * 0.9,
        starLife: this.starLife * 0.8,
        color: COLOR.Pink,
        glitter: "streamer"
    });
    innerShell.burst(x, y);
}
  1. Star - 烟花粒子类

ini 复制代码
const Star = {
    // 视觉属性
    drawWidth: 3,          // 绘制宽度
    airDrag: 0.98,         // 标准空气阻力
    airDragHeavy: 0.992,   // 重型粒子空气阻力

    // 按颜色存储活动的粒子
    active: createParticleCollection(),  // 创建粒子集合
    _pool: [], // 对象池

    // 创建新实例
    _new() {
        return {};
    },

    // 添加新粒子
    add(x, y, color, angle, speed, life, speedOffX, speedOffY) {
        // 从对象池获取或创建新实例
        const instance = this._pool.pop() || this._new();

        // 基础属性
        instance.visible = true;     // 是否可见
        instance.heavy = false;      // 是否重型粒子
        instance.x = x;              // X坐标
        instance.y = y;              // Y坐标
        instance.prevX = x;          // 前一帧X坐标
        instance.prevY = y;          // 前一帧Y坐标
        instance.color = color;      // 颜色
        
        // 速度分量
        instance.speedX = Math.sin(angle) * speed + (speedOffX || 0);
        instance.speedY = Math.cos(angle) * speed + (speedOffY || 0);
        
        // 生命周期
        instance.life = life;        // 当前生命值
        instance.fullLife = life;    // 总生命值

        // 自旋相关属性
        instance.spinAngle = Math.random() * PI_2;  // 自旋角度
        instance.spinSpeed = 0.8;                   // 自旋速度
        instance.spinRadius = 0;                    // 自旋半径

        // 火花效果属性
        instance.sparkFreq = 0;      // 火花发射频率
        instance.sparkSpeed = 1;     // 火花速度
        instance.sparkTimer = 0;     // 火花计时器
        instance.sparkColor = color; // 火花颜色
        instance.sparkLife = 750;    // 火花生命周期
        instance.sparkLifeVariation = 0.25;  // 火花生命变化范围
        
        // 特殊效果
        instance.strobe = false;     // 是否闪烁
        instance.alpha = 1;          // 透明度

        // 将粒子添加到对应颜色的活动集合中
        this.active[color].push(instance);
        return instance;
    },

    // 回收粒子实例到对象池
    returnInstance(instance) {
        // 触发死亡回调
        instance.onDeath && instance.onDeath(instance);

        // 重置属性
        instance.onDeath = null;
        instance.secondColor = null;
        instance.transitionTime = 0;
        instance.colorChanged = false;

        // 放回对象池
        this._pool.push(instance);
    }
};
  ##### **对象池模式**
javascript 复制代码
_pool: [],
_new() {
    return {};
},
  • 使用对象池模式来减少内存分配和垃圾回收

  • 复用对象而不是频繁创建新对象

  ##### **粒子属性系统**
arduino 复制代码
// 位置和运动
x, y            // 当前位置
prevX, prevY    // 上一帧位置
speedX, speedY  // 速度分量

// 生命周期
life            // 当前生命值
fullLife        // 总生命值

// 自旋系统
spinAngle       // 自旋角度
spinSpeed       // 自旋速度
spinRadius      // 自旋半径
  1. 火花效果系统
arduino 复制代码
// 火花属性
sparkFreq           // 发射频率
sparkSpeed          // 火花速度
sparkTimer          // 计时器
sparkColor          // 火花颜色
sparkLife           // 生命周期
sparkLifeVariation  // 生命变化范围
  1. 性能优化
kotlin 复制代码
// 按颜色分组存储
active: createParticleCollection(),

// 使用对象池
const instance = this._pool.pop() || this._new();
  1. 特效支持
arduino 复制代码
// 支持多种特效
strobe          // 闪烁效果
alpha           // 透明度渐变
secondColor     // 颜色过渡
transitionTime  // 过渡时间
  1. 使用示例:
csharp 复制代码
// 创建一个新的星星粒子
const star = Star.add(
    100,                    // x位置
    100,                    // y位置
    COLOR.Yellow,          // 颜色
    Math.PI / 4,           // 角度
    2,                     // 速度
    1000,                  // 生命周期
    0,                     // X方向速度偏移
    0                      // Y方向速度偏移
);

// 添加火花效果
star.sparkFreq = 100;      // 每100ms发射一次火花
star.sparkSpeed = 0.5;     // 火花速度
star.sparkLife = 500;      // 火花持续500ms

// 添加闪烁效果
star.strobe = true;
  1. 更新机制:
markdown 复制代码
    粒子的更新在主循环中进行,包括:

*   位置更新
*   速度衰减(空气阻力)
*   生命值递减
*   特效处理(火花、闪烁等)
*   自旋运动计算
*   透明度变化

    这个类的设计非常精巧,通过组合不同的属性可以创造出各种绚丽的烟花效果。同时通过对象池和颜色分组等优化手段,保证了在大量粒子同时存在的情况下依然能保持良好的性能。
  #### Spark - 火花效果类
ini 复制代码
const Spark = {
    // 基础视觉属性
    drawWidth: 0,      // 绘制宽度
    airDrag: 0.9,      // 空气阻力系数

    // 存储系统
    active: createParticleCollection(),  // 按颜色分组存储活动的火花
    _pool: [],         // 对象池

    // 创建新实例
    _new() {
        return {};
    },

    // 添加新火花
    add(x, y, color, angle, speed, life) {
        // 从对象池获取或创建新实例
        const instance = this._pool.pop() || this._new();

        // 设置位置属性
        instance.x = x;          // 当前X坐标
        instance.y = y;          // 当前Y坐标
        instance.prevX = x;      // 前一帧X坐标
        instance.prevY = y;      // 前一帧Y坐标
        
        // 设置基本属性
        instance.color = color;  // 火花颜色
        
        // 设置运动属性
        instance.speedX = Math.sin(angle) * speed;  // X方向速度
        instance.speedY = Math.cos(angle) * speed;  // Y方向速度
        instance.life = life;    // 生命周期
        instance.alpha = 1;      // 透明度

        // 将火花添加到对应颜色的活动集合中
        this.active[color].push(instance);
        return instance;
    },

    // 回收火花实例到对象池
    returnInstance(instance) {
        this._pool.push(instance);
    }
};
  ##### **简化设计**
arduino 复制代码
drawWidth: 0,    // 极小的绘制宽度
airDrag: 0.9,    // 较大的空气阻力,使火花快速减速
  1. 对象池管理
javascript 复制代码
_pool: [],
_new() {
    return {};
},
  • 相比Star类,Spark类设计更简单

  • 火花通常比星星小且生命周期更短

  ##### **对象池管理**
javascript 复制代码
_pool: [],
_new() {
    return {};
},
  • 使用对象池模式优化内存使用

  • 减少垃圾回收压力

  ##### **分组存储**
css 复制代码
active: createParticleCollection(),
  • 按颜色分组存储活动的火花
  ##### 火花实例属性
javascript 复制代码
{
    // 位置属性
    x: Number,        // 当前X坐标
    y: Number,        // 当前Y坐标
    prevX: Number,    // 上一帧X坐标
    prevY: Number,    // 上一帧Y坐标
    
    // 运动属性
    speedX: Number,   // X方向速度
    speedY: Number,   // Y方向速度
    
    // 视觉属性
    color: String,    // 颜色
    alpha: Number,    // 透明度
    
    // 生命周期
    life: Number      // 剩余生命值
}
  1. 使用示例
csharp 复制代码
// 创建一个新的火花
const spark = Spark.add(
    100,                // x位置
    100,                // y位置
    COLOR.Yellow,       // 颜色
    Math.random() * PI_2, // 随机角度
    1.5,                // 速度
    300                 // 生命周期(300ms)
);
  1. 更新机制

在主循环中的更新逻辑:

ini 复制代码
// 火花更新逻辑
COLOR_CODES.forEach(color => {
    const sparks = Spark.active[color];
    for (let i = sparks.length - 1; i >= 0; i = i - 1) {
        const spark = sparks[i];
        
        // 更新生命值
        spark.life -= timeStep;
        
        // 检查是否存活
        if (spark.life <= 0) {
            // 移除死亡的火花
            sparks.splice(i, 1);
            Spark.returnInstance(spark);
        } else {
            // 更新位置
            spark.prevX = spark.x;
            spark.prevY = spark.y;
            spark.x += spark.speedX * speed;
            spark.y += spark.speedY * speed;
            
            // 应用空气阻力
            spark.speedX *= sparkDrag;
            spark.speedY *= sparkDrag;
            
            // 应用重力
            spark.speedY += gAcc;
        }
    }
});
  1. 渲染实现
ini 复制代码
// 在渲染循环中的绘制逻辑
trailsCtx.lineWidth = Spark.drawWidth;
trailsCtx.lineCap = "butt";

COLOR_CODES.forEach(color => {
    const sparks = Spark.active[color];
    trailsCtx.strokeStyle = color;
    trailsCtx.beginPath();
    
    sparks.forEach(spark => {
        // 设置透明度
        trailsCtx.globalAlpha = globalAlpha * 0.8;
        // 绘制火花轨迹
        trailsCtx.moveTo(spark.x, spark.y);
        trailsCtx.lineTo(spark.prevX, spark.prevY);
    });
    
    trailsCtx.stroke();
});
  1. 与Star类的主要区别
  • 简化属性

    • 没有自旋系统
    • 没有火花发射系统
    • 没有特效系统(如闪烁)
  • 物理特性

    • 更大的空气阻力(0.9 vs 0.98)
    • 更小的绘制宽度(0 vs 3)
    • 更短的生命周期
  • 视觉效果

    • 更细小的粒子
    • 更快的消失速度
    • 更简单的运动轨迹

Spark类主要用于创建烟花爆炸时的小型火花效果,它们数量多但生命周期短,通过大量这样的小火花可以创造出绚丽的视觉效果。其简化的设计也保证了在大量火花同时存在的情况下,仍能保持良好的性能。
4.

  #### BurstFlash - 爆炸闪光效果类
javascript 复制代码
const BurstFlash = {
    // 存储系统
    active: [],     // 活动的闪光效果数组
    _pool: [],      // 对象池

    // 创建新实例
    _new() {
        return {};
    },

    // 添加新的爆炸闪光
    add(x, y, radius) {
        // 从对象池获取或创建新实例
        const instance = this._pool.pop() || this._new();

        // 设置基本属性
        instance.x = x;          // 闪光中心X坐标
        instance.y = y;          // 闪光中心Y坐标
        instance.radius = radius; // 闪光半径

        // 将实例添加到活动数组
        this.active.push(instance);
        return instance;
    },

    // 回收实例到对象池
    returnInstance(instance) {
        this._pool.push(instance);
    }
};
简单结构
javascript 复制代码
{
    x: Number,      // 位置X
    y: Number,      // 位置Y
    radius: Number  // 闪光半径
}
  • 相比Star和Spark类,结构更简单
  • 只需要位置和半径信息
对象池管理
javascript 复制代码
_pool: [],
_new() {
    return {};
}
  • 使用对象池模式优化内存使用
  • 减少垃圾回收压力
2使用场景
csharp 复制代码
// 在烟花爆炸时创建闪光效果
BurstFlash.add(
    shell.x,           // 爆炸位置X
    shell.y,           // 爆炸位置Y
    shell.spreadSize / 4  // 闪光半径为扩散范围的1/4
);
  1. 渲染实现
arduino 复制代码
// 在渲染循环中的绘制逻辑
while (BurstFlash.active.length) {
    // 获取并移除一个闪光实例
    const bf = BurstFlash.active.pop();

    // 创建径向渐变
    const burstGradient = trailsCtx.createRadialGradient(
        bf.x, bf.y, 0,    // 内圆心和半径
        bf.x, bf.y, bf.radius  // 外圆心和半径
    );

    // 设置渐变颜色停止点
    burstGradient.addColorStop(0.024, 'rgba(255, 255, 255, 1)');     // 中心纯白
    burstGradient.addColorStop(0.125, 'rgba(255, 160, 20, 0.2)');    // 过渡区域
    burstGradient.addColorStop(0.32, 'rgba(255, 140, 20, 0.11)');    // 过渡区域
    burstGradient.addColorStop(1, 'rgba(255, 120, 20, 0)');          // 边缘透明

    // 应用渐变并绘制
    trailsCtx.fillStyle = burstGradient;
    trailsCtx.fillRect(
        bf.x - bf.radius,
        bf.y - bf.radius,
        bf.radius * 2,
        bf.radius * 2
    );

    // 回收实例
    BurstFlash.returnInstance(bf);
}
  1. 特点分析
  • 即时渲染

    • 闪光效果是即时的,不需要动画过渡
    • 一旦渲染完成就立即回收实例
  • 渐变效果

    rust 复制代码
        // 渐变颜色设置
        0.024 -> 纯白色 (1.0)     // 中心最亮
        0.125 -> 橙色 (0.2)      // 快速衰减
        0.32  -> 橙色 (0.11)     // 继续衰减
        1.0   -> 橙色 (0.0)      // 完全透明
    • 使用径向渐变创造真实的爆炸光效
    • 从中心向外逐渐衰减
性能优化
scss 复制代码
// 使用矩形绘制而不是圆形
trailsCtx.fillRect(
    bf.x - bf.radius,
    bf.y - bf.radius,
    bf.radius * 2,
    bf.radius * 2
);
  • 使用fillRect代替arc+fill提高性能
  • 径向渐变确保视觉效果是圆形的
应用示例
javascript 复制代码
// 在Shell类的burst方法中使用
burst(x, y) {
    // ... 其他爆炸效果代码 ...

    // 创建爆炸闪光
    BurstFlash.add(x, y, this.spreadSize / 4);

    // ... 其他效果代码 ...
}
  1. 与其他效果类的关系
  • 与Star类的配合

    • 在烟花爆炸时提供初始闪光效果
    • 增强爆炸瞬间的视觉冲击
  • 与Spark类的配合

    • 为火花提供初始照明效果
    • 增强整体爆炸效果的真实感
  • 特点对比

    • Star: 持续运动的粒子效果
    • Spark: 短暂的火花效果
    • BurstFlash: 瞬时的闪光效果

BurstFlash类虽然结构简单,但在整个烟花效果中起到了重要的视觉增强作用。它通过模拟真实烟花爆炸时的瞬间强光,使整个效果更加逼真和震撼。同时,其优化的实现方式也确保了在频繁的烟花爆炸中保持良好的性能。

相关推荐
偷光1 小时前
深度剖析 React 的 useReducer Hook:从基础到高级用法
前端·javascript·react.js
The_era_achievs_hero2 小时前
动态表格html
前端·javascript·html
2201_756942643 小时前
react入门笔记
javascript·笔记·react.js
傻小胖3 小时前
ES6 Proxy 用法总结以及 Object.defineProperty用法区别
前端·javascript·es6
横冲直撞de4 小时前
高版本electron使用iohook失败(使用uiohook-napi替代)
前端·javascript·electron
_Eden_4 小时前
认识Electron 开启新的探索世界一
前端·javascript·electron
~怎么回事啊~4 小时前
electron中调用C++
前端·javascript·electron
海上彼尚4 小时前
electron-vite 构建后路由失效问题
前端·javascript·electron
HsuYang4 小时前
Vite源码学习(十一)——热更新(中)
前端·javascript·架构
不怕麻烦的鹿丸4 小时前
web前端录制canvas视频和video的声音,并合并成一个文件进行下载
前端·javascript·音视频·canvas