当前位置 博文首页 > 小丞同学:邀你看一场浪漫的烟火 -- canvas放烟花

    小丞同学:邀你看一场浪漫的烟火 -- canvas放烟花

    作者:[db:作者] 时间:2021-07-02 18:40

    漫天的烟火送给遥远的你

    🎉我裁一段星河送给你,好叫你不逊色这漫天烟火

    未命名

    漫天的烟火,在这璀璨的星空中闪耀,成就了这片星空的绚丽,更散发出了自己无限的光芒,今天就使用canvas来做一个烟花效果吧!?

    实现效果gif3

    实现的效果还是很不错的,漫天的烟火肆意绽放

    实现过程

    1. 简单的 Html 和 CSS

    简单的写点基础样式,背景黑色,定义个canvas标签

    <!DOCTYPE html>
    <html lang="en">
    
    <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">
        <title></title>
        <style>
            * {
                margin: 0;
                padding: 0;
            }
    
            body {
                background-color: black;
            }
    
            canvas {
                position: absolute;
                z-index: 0;
            }
        </style>
    </head>
    
    <body>
        <canvas>您的浏览器不支持</canvas>
        <script src="index.js"></script>
    </body>
    
    </html>
    

    2. 创建 canvas画布

    在js中先获取标签,设置画布大小,采用resize监听页面的调整,及时的改变画布的大小

    // 元素获取
    const canvas = document.querySelector("canvas")
    const ctx = canvas.getContext("2d");
    // 设定画布大小
    function resizeCanvas() {
        canvas.width = window.innerWidth
        canvas.height = window.innerHeight
    }
    resizeCanvas();
    // 页面缩放改变画布大小
    window.addEventListener("resize", resizeCanvas)
    

    3. 获取鼠标点击位置

    通过e.clientXe.clientY来获取鼠标点击的位置,用于在后面实现在鼠标点击的位置,产生烟花

    function clickSite(e) {
        // 获取当前鼠标的坐标
        let x = e.clientX;
        let y = e.clientY;
        // 绘制
        addFires(x, y);
    }
    document.addEventListener('click', clickSite);
    

    4. 实现鼠标点击产生烟花的初级形态

    我们一步一步来实现,这是实现烟花效果的第一步,通过在点击位置添加一个烟花雏形

    ,这是一个单纯的静态,在后面我们慢慢的让它动起来

    function drawFires() {
        // 初始半径,以及粒子数量
        let count = 10;
        let radius = 10;
        for (let i = 0; i < count; i++) {
            // 渲染出当前数据
            // 下面是点数学题
            // moveX,moveY是粒子开始的坐标,画个三角形,角度半径知道很容易就得出方程
            let angle = 360 / count * i;
            let radians = angle * Math.PI / 180;
            let moveX = x + Math.cos(radians) * radius
            let moveY = y + Math.sin(radians) * radius
            // 开始路径
            ctx.beginPath();
            ctx.arc(moveX, moveY, 2, Math.PI * 2, false);
            // 结束
            ctx.closePath();
            ctx.fillStyle = '#ff0000'
            ctx.fill();
        }
    }
    

    1

    当前效果

    5. 实现烟花散开

    有了上面的铺垫,每一个小圆点都是即将要散开的烟花,那么我们只需要更新画布让它的半径不断的增大即可,实现散开的效果很简单,那部分代码就不贴了(节省篇幅),对于更新画布,采用的一个html5中的新方法requestAnimationFrame官方文档

    它相比于使用定时器实现动画有什么优点呢?

    1. 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,会减少对内存的使用
    2. requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成

    动画实现代码

    // 渲染,更新粒子的信息
    function tick() {
        // 更新画布
        drawFires();
        requestAnimationFrame(tick);
    }
    tick()
    

    gif1当前实现效果

    6. 实现拖尾效果以及随机颜色

    从上面的效果图可以看出,爆炸的效果我们已经能基本实现了,但是烟花不是一个个的小球,我们需要添加拖尾的效果,并且给每个小球随机颜色,这样会更加的炫丽

    拖尾效果代码

    在绘制完一帧后,绘制下一帧之前,添加一个半透明的蒙层就能实现一个拖尾的效果

    function tick() {
        // 设置拖影
        ctx.globalCompositeOperation = 'destination-out';
        ctx.fillStyle = 'rgba(0,0,0,' + 10 / 100 + ')';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.globalCompositeOperation = 'lighter';
        // 更新画布
        drawFires();
        requestAnimationFrame(tick);
    }
    

    随机颜色代码

    采用的是hsla函数来设定颜色,因为烟花的色彩都是接近的,所以我们可以通过调节色相值,来选择烟花颜色。这里采用的是固定饱和度为100%,亮度颜色随机在一定范围内,使得颜色不会过于离谱

    hsla() 函数使用色相、饱和度、亮度、透明度来定义颜色。

    let hue = Math.random() * 360;
    let hueVariance = 60;
    function setColors(firework) {
        firework.hue = Math.floor(Math.random() * ((hue + hueVariance) - (hue - hueVariance))) + (hue - hueVariance);
        firework.brightness = Math.floor(Math.random() * 21) + 50;
        firework.alpha = (Math.floor(Math.random() * 60) + 40) / 100;
    }
    

    gif2

    当前效果

    7. 实现烟花重力下坠

    从上面的效果图,我们可以认识到我们还差两部,烟花的下坠以及烟花的消失

    我们通过重新调整烟花路径的算法,来实现烟花的下坠,在初始的代码中对于烟花的爆炸路径,采用的是普通的直线运动,我们需要在这个基础上让它的y方向加大一点,这样就会实现了一个抛物线的效果,同时,对于烟花的爆炸应当还要有个殆尽的效果,我们通过改变透明度来实现,对于透明度小于0的我们将它从数组中移除

    let moveX = Math.cos(firework.radians) * firework.radius;
    let moveY = Math.sin(firework.radians) * firework.radius + 1;
    firework.x += moveX;
    firework.y += moveY;
    // 更新数据,让圆扩散开来
    firework.radius *= 1 - firework.speed / 120
    firework.alpha -= 0.01;
    // 如果透明度小于0就删除这个粒子
    if (firework.alpha <= 0) {
        fireworks.splice(i, 1);
        // 跳过这次循环,不进行绘制
        continue;
    }
    

    改变了算法

    每次画布的更新都要让透明度降低,同时每个粒子的移动半径不断地减小,这样会形成向中间合拢的趋势,由于每个粒子都存放在数组当中,当粒子透明度小于0,从数组移除

    8. 实现自动烟花效果

    只需要通过定时器,不断的添加烟花即可

    setInterval(() => {
        // 可以多调用几次用来增加烟花的数量
        addFires(Math.random() * canvas.width, Math.random() * canvas.height)
        addFires(Math.random() * canvas.width, Math.random() * canvas.height)
    }, 500)
    

    自定义文字

    可以在页面中加一些标签,比如文字,实现的效果都是不错的噢~
    gif4
    本次的烟花效果就到这里了,喜欢的话就自己尝试的做一个吧~~

    完整代码可以私信噢!

    cs