不得不说,这个 canvas 的旋转半圆效果可能会闪瞎你的双眼🐶

2,663 阅读4分钟

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

前言

大家好,我是爱吃鱼的桶哥Z,在动画效果这方面来看,css 还是比 canvas 要略逊一筹,刚好今天又学到了一个闪瞎眼的效果,分享给大家一起学习一下。老规矩,先来看一下最终实现的效果,如图:

demo1.gif

不得不说的是,这个效果真的很解压。虽然一眼看上去完全不知道头绪,但实际开发这个效果很简单,下面就跟着我一起来学习如何开发这么一款炫酷的旋转半圆效果吧!

一个旋转的半圆

首先我们先分析一下这个效果,在上图中可以看到总共是 24个 半圆在旋转,其中内层有8个,外层是双层的 8个 圆,因此总共有 24个 旋转的半圆。那么我们还如何实现这个效果呢?

还记得我们前面文章中做画多个图像时怎么做的吗?我们都是先画出一个图形,然后通过循环的方式就可以实现多个图形了,这里也一样,我们可以先画出一个旋转的半圆,然后再通过循环来创建多个旋转的半圆。

因为是使用的 canvas 绘制,因此还需要相关的 html 和 css 代码,这里不做过多的赘述,可以查看前面的文章,里面都有相关的代码,也可以在文末查看完整的代码。这里我们还是采用 ES6 + TS 的写法来开发这个效果,至于为什么要使用 ES6 + TS,在前面的文章中也都介绍过,有兴趣的童鞋可以去看一下。

下面我们就先来实现一个旋转的半圆。首先我们还是先定义一个 RotateSemicircle 类,并添加相关的 constructor 函数,代码如下:

class RotateSemicircle {
    canvas: HTMLCanvasElement;
    ctx: CanvasRenderingContext2D;
    size: number;
    deg: number;
    constructor() {
        this.canvas = document.getElementById('canvas') as HTMLCanvasElement;
        this.ctx = this.canvas.getContext('2d');
        this.size = 800;
        this.canvas.width = this.size;
        this.canvas.height = this.size;
        this.deg = 0;
    }
}

初始的 constructor 函数内定义的都是我们需要的数据,内容比较简单。接下来我们就需要绘制出一个旋转的半圆,主要使用到 ctx.arc() 方法,一起来看一下代码,如下:

class RotateSemicircle {
    constructor() {
        ...other code
        
        this.draw()
    }
    draw() {
        this.ctx.clearRect(0, 0, this.size, this.size);
        this.ctx.beginPath();
        this.ctx.arc(this.size / 2, this.size / 2, this.size / 2, this.deg / 4 * Math.PI, Math.PI + this.deg / 4 * Math.PI, false);
        this.ctx.strokeStyle = `hsl(${this.deg * 50}deg, 80%, 50%)`;
        this.ctx.lineWidth = 3;
        this.ctx.stroke();
        
        this.deg += 0.04;
    }
    
    animate() {
        requestAnimationFrame(() => this.animate());
        this.draw();
    }
}

RotateSemicircle 类中添加一个 draw 方法,其中主要用到 ctx.arc() 方法来绘制圆,它有 6个 参数,分别是:绘制圆心的x轴坐标、绘制圆心的y轴坐标、绘制圆的半径、绘制圆的开始角度、绘制圆的结束角度以及最后一个可选参数,如果为 true,则逆时针绘制圆弧,反之,则顺时针绘制。

有了这6个参数后,我们只需要设置到对应的数值,并不断的更新 this.deg 的值,这样就能让这个半圆旋转起来了,当然不要忘了通过 new 关键词来实例化 RotateSemicircle 类。

通过上述代码,我们可以得到如下的效果,一起来看图:

demo2.gif

可以看到绘制出来的这个半圆一直在旋转,并且在旋转的过程中不断的变换颜色,因为我们在 draw 方法中添加了 this.ctx.strokeStyle = hsl(${this.deg * 50}deg, 80%, 50%) 这行代码,因此这个半圆在旋转的过程中就会不断的根据当前的 thid.deg 的值进行变换。

我们已经绘制出了一个圆,那么接下来我们就可以通过循环的方式来创建多个旋转的半圆了,一起来看看吧!

N个旋转的半圆

在上面我们通过 ctx.arc() 方法已经绘制出了一个旋转的半圆,那么要实现N个旋转的半圆,其实只需要通过循环来创建N个即可。在最开始已经说过我们这里外层是8个旋转的半圆,因此我们只需要循环8次即可,让我们一起来看代码,如下:

class RotateSemicircle {
    ...other code
    
    draw() {
        this.ctx.clearRect(0, 0, this.size, this.size);
        
        for (let i = 0; i < 8; i++) {
            const x1 = this.size / 4 * Math.cos(i / 4 * Math.PI + this.deg / 5) + this.size / 2;
            const y1 = this.size / 4 * Math.sin(i / 4 * Math.PI + this.deg / 5) + this.size / 2;
            this.ctx.beginPath();
            this.ctx.arc(x1, y1, this.size / 4, this.deg + i / 4 * Math.PI, Math.PI + this.deg + i / 4 * Math.PI, false);
            this.ctx.strokeStyle = `hsl(${this.deg * 50}deg, 80%, 50%)`;
            this.ctx.lineWidth = 3;
            this.ctx.stroke();
        }
    }
    
    ...other code
}

我们修改 RotateSemicircle 类中的 draw 方法,在前面我们只创建一个半圆时,只需要执行一次 ctx.arc() 即可,而这里我们要实现的效果是 8个 旋转的半圆,因此循环8次即可。计算的圆心坐标可以随意的更改数值,这里也可以参考上述的代码,最终实现的效果如下所示:

demo3.gif

可以看到通过循环8次后,创建了8个旋转的半圆,组合起来的效果还不错。但是还不够精彩,因此我们可以继续添加更多的旋转半圆,让整个 canvas 画面看起来更加的炫酷,修改 draw 方法即可,代码如下:

class RotateSemicircle {
    ...other code
    
    draw() {
        this.ctx.clearRect(0, 0, this.size, this.size);
        
        for (let i = 0; i < 8; i++) {
            ...other code
            
            const x2 = this.size / 4 * Math.cos(i / 4 * Math.PI + 0.5 - this.deg / 5) + this.size / 2;
            const y2 = this.size / 4 * Math.sin(i / 4 * Math.PI + 0.5 - this.deg / 5) + this.size / 2;
            this.ctx.beginPath();
            this.ctx.arc(x2, y2, this.size / 5, this.deg + i / 4 * Math.PI, Math.PI + this.deg + i / 4 * Math.PI, false);
            this.ctx.strokeStyle = `hsl(${this.deg * 100}deg, 80%, 50%)`;
            this.ctx.lineWidth = 2;
            this.ctx.stroke();
        }
    }
    
    ...other code
}

还是在这个循环中,我们通过三角函数相关的知识,计算出新的圆心坐标点,这样就可以让外层的8个半圆与另外的8个半圆相叠加,最终实现的效果如下:

demo4.gif

怎么样,是不是比上面看起来更加的精彩呢?我们还可以给这些旋转的半圆添加上"拖尾"的效果,修改的代码也很简单,如下:

class RotateSemicircle {
    ...other code
    
    draw() {
        this.ctx.fillStyle = `rgba(0,0,0,0.1)`;
        this.ctx.fillRect(0, 0, this.size, this.size);
    }
    
    ...other code
}

只需要将 ctx.clearRect() 方法替换为 ctx.fillRect() 即可,这样就能实现一个渐变的效果了,如下图所示:

demo5.gif

你以为到这里就结束了吗?我觉得这个效果还不是特别的完美,因此我又加了最后一层,各位童鞋也可以根据自己的喜好来随意更改代码,我最后添加的代码如下:

class RotateSemicircle {
    ...other code
    
    draw() {
        this.ctx.clearRect(0, 0, this.size, this.size);
        
        for (let i = 0; i < 8; i++) {
            ...other code
            
            const x3 = this.size / 4 * Math.cos(i / 4 * Math.PI + 0.5 - this.deg / 5) + this.size / 2;
            const y3 = this.size / 4 * Math.sin(i / 4 * Math.PI + 0.5 - this.deg / 5) + this.size / 2;
            this.ctx.beginPath();
            this.ctx.arc(x3, y3, this.size / 6, this.deg + i / 4 * Math.PI, Math.PI + this.deg + i / 4 * Math.PI, false);
            this.ctx.strokeStyle = `hsl(${this.deg * 150}deg, 70%, 90%)`;
            this.ctx.lineWidth = 1;
            this.ctx.stroke();
        }
    }
    
    ...other code
}

可以看到最后添加的一层只是在第一层的基础上修改了一些数据,童鞋们也可以参考我这个方法,自己在这个效果上进行修改,最终实现的完整效果与代码可以在这里进行查看:

总结

总的来说,有时候只需要通过很简单的 API 即可完成非常炫酷的效果,主要还是计算的方法需要多多的了解和学习,这样才能创作出更多天马行空的效果,关于这个旋转的半圆,你学会了吗?

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

往期回顾

canvas 实现燃烧的线段,原来线段也能玩的这么出彩

又实现了一个酷炫的动感激光,不来看看?

canvas 动画真好玩,快来学一下这炫酷的效果吧!

妙啊,canvas 还能实现这么酷炫的旋转六边形

为了学会更多炫酷的 canvas 效果,我熬夜复习了三角函数相关的知识点

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

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