🎇canvas 专场:烟花秀🎇

3,346 阅读3分钟

PK创意闹新春,我正在参加「春节创意投稿大赛」,详情请看:春节创意投稿大赛

介绍

canvas 是 html 中的一个标签,画布的含义,功能上和 window 系统自带的 画图 工具类似,唯一不同的是需要我们使用 js 来渲染我们需要的效果

入门三板斧

  1. 在 DOM 上定义一个带有长宽属性的 canvas 标签
<canvas id="canvas" width="300px" height="300px" style="background:#000"></canvas>

为了直观的看到效果,给定一个背景色

image.png

  1. 通过 js 获取到对应的 canvas 上的 “画笔” 引用,后续我们在 canvas 所有的绘制操作,基本上都是需要通过这个 “画笔”
let cx = document.getElementById('canvas').getContext('2d')

如代码,我们拿到了 2D 的画笔

  1. 使用 “画笔” 绘制我们需要的图形,比如基础的线、圆、矩阵等
// 画圆
function drawArc(x, y, r) {
    cx.beginPath();
    cx.arc(x, y, r, 0, 2 * Math.PI);
    cx.fill();
}
// 画线
function drawLine(startX, startY, endX, endY) {
    cx.beginPath();
    cx.moveTo(startX, startY);
    cx.lineTo(endX, endY);
    cx.stroke();
    cx.closePath()
}
// 画笔自带的矩阵绘制函数 cx.fillRect(x, y, width, height)
drawArc(50,50,10);
drawLine(50, 70, 50, 200);
cx.fillRect(100,100,50,100)

image.png

注意:在画布中的坐标系,已在上图中用红色线条标记出来。以画布的左上角为原点,往右为 x,往下为 y

因为画布的特性是一张纸,我们在上面的所有绘制操作,都会影响到上一次绘制的结果,即为覆盖绘制。举个例子,就如 PS 的图层,我们的每次操作,都是绘制在新建的,最顶部的图层之上;但不如 PS 的是,没法对之前的图层做修改。

好了,现在我们对 canvas 有了一定的了解,开始动手做点小东西

绘制烟花

我们熟知的烟花效果分 2 个阶段:① 是烟花快速上升 ② 是烟花上升到某个点后,炸开,形成的火花四散然后消失

烟花有一个最大的效果,就是尾焰拖拽。这里有两种实现的思路:

① 每次的轨迹绘制,前半部分,使用实线,后半部分,使用渐变的透明线;

② 利用画布的覆盖绘制的特性,每帧绘制之前,全图层覆盖一个有有透明度的背景色,每次覆盖后,画布上的图形都会变淡。

本次示例考虑采用方案 ②,定时为整个画布叠加透明背景色,剩下的只需要考虑点的位置绘制

绘制上升阶段的动画

获取到页面的可视高度,设置为烟花的起点,获取到随机的上升高度,设置一个每次上升的速度(即每帧上升几个像素点)

let hz = 1000 / 75;
fireUp(200, 2);
function fireUp(x, r) {
    let y = window.innerHeight;
    ramdomTop = parseInt(Math.random() * 500) + 100;
    let cleanT = setInterval(() => clean(), hz*1.2);
    let time = setInterval(() => {
        if (y < ramdomTop) {
            // clearInterval(time);
            y = window.innerHeight;
        }
        y -= 4;
        drawPoint(new Point(x, y, r))
    }, hz);
}

1.gif

绘制烟花四散的效果

核心就是使用 Math.sin(), Math.cos() 计算每个角度的 x, y 坐标,然后修改中心点,就得到了指定坐标四周各个角度的坐标关系,我们根据这个关系均等份的计算出各角度的匀速运动轨迹

// 计算四周的点位数据
function caculatePoints(px, py, step, maxR) {
    /**
     * [p1-x]
     * [p2-x]
     */
    let arrs = [];
    for (let i = step; i <= 360; i += step) {
        arrs.push(new Point(Math.cos(Math.PI / 180 * i).toFixed(4),
            Math.sin(Math.PI / 180 * i).toFixed(4),
            2));
    }
    let arrs2 = [];
    for (let i = 1; i < maxR; i++) {
        arrs2.push(arrs.map(n => new Point(n.x * i + px, n.y * i + py, n.r, n.color)));
    }
    return arrs2;
}

烟花绽放的 function 里插入这个函数的调用

image.png

效果如图示:

2.gif

五颜六色的绽放轨迹

效果实现了,全白的烟花一下子就看腻了,给烟花的绽放效果添加一个随机的颜色

因为绽放后,每个轨迹上的效果,一般是同色,或者是相近的颜色,才会符合实际,这个功能适合添加在四周点生成的函数内

修改使用 Point 对象,绑定 color。再定义一个生成随机 RGB 的函数

function Point(x, y, r = 2, color = '#fff') {
    return {
        x: x,
        y: y,
        r: r,
        color: color
    }
}
let rgb = function() {
    let str = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
    return {
        ramdon() {
            let color = '#';
            for (let i = 0; i < 6; i++) {
                color += str[parseInt(Math.floor(Math.random() * 16))]
            }
            return color;
        }
    }
}();

caculatePoints() 在创建 Point 实例的时候传入随机 RGB,改动如下

image.png

改动后的效果如下:

3.gif

重头戏

费这么大劲做个 DOME 自己看多难受,这不得搬上掘金当背景?

4.gif

加上随机的放烟花,以及限定最大的烟花数量,首页美化后的样式:

5.gif

油猴链接

新年烟花背景(掘金适配)

脚本做的简简单单,安装就能实现同款效果

image.png

改进

为烟花的效果增加更多的随机值,比如上升的速率,炸开后的运动半径

6.gif

待改进

绽放的效果还是有些僵硬,继续研究重力的加速度,安排上去之后应该会更加漂亮


原创文章,未经允许,禁止转载

create by:安逸的咸鱼