嚯,五角星还能这么玩?快摘下来送给你的她/他/ta😁

3,123 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

前言

最近在小破站闲逛,看到了很多有意思的内容,也学到了很多有用的知识,刚好最近学到了一个非常炫酷、非常炸裂的星星闪耀效果,今天就带着大家一起来实现一下这闪瞎眼的星。老规矩还是先看一下最终实现的效果,然后再来一步步分析实现的原理,如下:

demo5.gif

这个效果是不是非常的闪耀,非常的炸裂呢?下面我们就一起来学习一下如何制作这么一个闪耀的星星吧!

五角星

这个效果是使用 canvas 来开发的,因此我们需要准备好最基础的内容,也就是在页面中准备好一个 canvas,它的样式也很简单,我们一起来看一下相关的 htmlcss,如下:

<canvas id="canvas"></canvas>

html 中只有一个 canvas 标签。再来看一下 css

*{margin: 0; padding: 0;}
body {
    background: #000;
    overflow: hidden;
}

css 也很简单,只需要将 body 的背景色设置为黑色,并且将 body 的超出部分进行隐藏即可。

我们先来实现一个静态的五角星,有了静态的五角星,然后再添加周围的发射光线就简单很多了。那么要实现一个五角星该如何做呢?

这里还是需要借助我们在前面学到的三角函数相关的知识,利用 Math.sin()Math.cos() 函数来获取两个夹角之间的弧度。因为要实现五角星,其实就是要实现五个夹角的弧度,并且将这五个夹角连接在一起,最后就能画出一个完整的五角星了,下面我们一起来看一下相关的代码,如下:

/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas');
const ctx = this.canvas.getContext('2d');
canvas.width = innerWidth;
canvas.height = innerHeight;
const points = [];
const radius1 = 100;  // 外五角星半径
const radius2 = 50;   // 内五角星半径

首先我们准备好相关的初始内容,最上面的 @type {HTMLCanvasElement} 可以告诉编辑器我们是在编写 canvas 相关的内容,这样编辑器就能给予我们相关的提示内容了,这样就不怕不记得 canvas 相关的 api 该怎么写了。

基础的内容都准备好以后,我们先将五角星的五个顶点的坐标轴获取到,这里的顶点包含了外层的五个顶点坐标以及内层的五个顶点坐标,因此我们需要将相关的数据放到 points 数组中,相关的代码如下:

// 创建五角星的五个顶点
for (let i = 0; i < 5; i++) {
    // 外层五角星坐标
    let deg = -90 + i * 72;
    let x1 = radius1 * Math.cos(deg * Math.PI / 180) + canvas.width / 2;
    let y1 = radius1 * Math.sin(deg * Math.PI / 180) + canvas.height / 2;
    points.push([x1, y1]);
    // 旋转36度,生成内顶角坐标
    deg = deg + 36;
    let x2 = radius2 * Math.cos(deg * Math.PI / 180) + canvas.width / 2;
    let y2 = radius2 * Math.sin(deg * Math.PI / 180) + canvas.height / 2;
    points.push([x2, y2]);
}

这块的内容主要还是用到了三角函数相关的知识点,如果对三角函数不了解,可能会看的比较蒙,如果看不明白,可以看一下前面的文章,里面有介绍三角函数相关的知识点,相信你看完三角函数相关的内容再来看这里就明白了。

当上述的数据都准备好后,我们就可以正式开始来绘制五角星了。有了这些数据,绘制起来也就比较简单了,一起来看一下代码,如下:

function drawStart() {
    // 清理画布
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    // 开始绘制
    ctx.beginPath();
    // 连接生成的五角星顶点
    for (let i in points) {
        // 遍历各个顶点坐标,然后绘制出来
        let p = points[i];
        ctx.lineTo(p[0], p[1]);
    }
    ctx.closePath();
    ctx.fillStyle = `hsl(${Math.random() * 360}, 80%, 60%)`;
    ctx.fill();
}

上述的代码很简单,都是 canvas 最基础的操作,最终实现的效果如下图所示:

image.png

在上面我们设置这个五角星的背景色是随机色,因此页面刷新后五角星的颜色就会随机的变换。

我们可以给这个静态的五角星添加一个动画,只需要添加简单的几行代码就可以实现,如下:

function animate() {
    requestAnimationFrame(animate);
    drawStart();
}

添加动画后,实现的效果如下所示:

demo4.gif

闪耀的效果已经出来了,由于录制的 GIF 丢帧严重,因此建议大家自行在本地实现相关的代码再来看效果,这样会更好。

目前五角星已经实现了,还剩下每个方法的放射粒子效果了,接下来我们一起来看一下如何实现放射粒子吧!

五角星粒子放射

在最前面的效果中,我们可以看到满屏的粒子是从五角星的每一条边中发射出来的,并且这些粒子在发射的过程中会逐渐的消失,因此我们首先想到的就是,这些粒子应该是存放在某个对象中,这样当对象中的粒子数达到一个临界值时,就删除最先生成的粒子,然后再不断的添加新的粒子。

有了这个思路,我们就先来实现一个粒子。还记得我们在前面一节中如何生成烟花的爆炸效果吗?这里要实现粒子的放射其实原理跟前面也是差不多的,下面我们一起来看一下相关的代码,如下:

class Particle {
    constructor(x, y, canvas, ctx) {
        // 创建粒子角度
        this.deg = Math.atan2((y - canvas.height / 2), (x - canvas.width / 2));
        // 粒子与原点之间的角度和距离
        this.radius = Math.sqrt(Math.pow((y - canvas.height / 2), 2) + Math.pow((x - canvas.width / 2), 2));
        this.age = Math.random() * canvas.width / 2;
        this.color = `hsl(${Math.random() * 360}, 80%, 60%)`;
        this.canvas = canvas;
        this.ctx = ctx;
    }

    draw() {
        let x1 = this.radius * Math.cos(this.deg) + this.canvas.width / 2;
        let y1 = this.radius * Math.sin(this.deg) + this.canvas.height / 2;
        this.ctx.fillStyle = this.color;
        this.ctx.beginPath();
        this.ctx.arc(x1, y1, 2, 0, Math.PI * 2, 0);
        this.ctx.fill();
    }

    update() {
        this.radius += 2;
        this.age--;
    }
}

我们还是通过面向对象的方式来实现粒子类,这样就可以通过循环遍历不断的生成 N多个 不同的粒子。有了上面生成粒子的类,接下来我们就需要改造一下前面的代码,让五角星能够配合粒子的放射。一起来看一下代码吧,如下:

// ...other code
const particles = [];  // 需要新增一个粒子存放的数组

// 主要修改 drawStart 方法
drawStar() {
    // 要实现拖尾效果,就不能使用 clearRect,需要改成 fillRect
    ctx.fillStyle = "rgba(0, 0, 0, 0.1)";
    // 使用 fillRect 创建拖尾效果
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    // 创建粒子数据,如果当前粒子数组中的粒子数量少于800,则添加100个
    if (particles.length < 800) {
        // 先创建 100 个粒子
        for (let i = 0; i < 100; i++) {
            // 随机生成一个从0到10的值
            let s = Math.random() * 10 | 0;
            // 根据上面生成的随机值,随机选择一个顶点
            let p1 = points[s];
            // 选择跟前一个顶点相邻的顶点,如果选择的这个顶点是最后一个,则下一个顶点是第一个
            let p2 = points[(s + 1) % 10];
            // 随机获取当前这条线的x轴的值
            let px = Math.random() * (p2[0] - p1[0]) + p1[0];
            // 获取这条线与x轴相交的坐标
            let py = (px - p1[0]) * (p2[1] - p1[1]) / (p2[0] - p1[0]) + p1[1];
            particles.push(new Particle(px, py, canvas, ctx));
        }

        // ...other code
    }

    // 渲染粒子效果
    for (let i in particles) {
        let p = particles[i];
        p.update();
        p.draw();
        if (p.age < 0) {
            particles.splice(i, 1);
        }
    }
}

只需要设置好粒子的生成点,这样当我们使用 requestAnimationFrame 让五角星动起来的时候,就会不断的生成新的粒子,并且每个粒子的生成点都是随机的,最终实现的完整代码可以在这里进行查看:

总结

canvas 能够实现很多炫酷的效果,前提是需要学习一些数学相关的知识点,因为我们需要借助这些数学中的方法来生成一些数据,只有数据获取到了,才能实现我们想要的效果。关于三角函数,这都是初中的知识点,当初没有好好学习,现在就是还债的时候,让我们一起加油吧!

最后,如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,谢谢大家

往期回顾

这个国庆,带老婆去看一场烟花雨

还记得2048怎么玩吗?快来玩会儿(摸鱼)吧!

canvas 实现七彩炫酷圆环,快来看看