likes
comments
collection
share

又用 canvas 实现了一个旋转的伪3D球体,来瞧瞧?

作者站长头像
站长
· 阅读数 15

前言

前面一篇文章中,我们通过 canvas 实现了一个多彩的圆环数字时钟,刚好最近又学到了一个关于 canvas 粒子的伪3D效果,今天就分享给大家。老规矩,我们还是先来看一下最终实现的效果,如下图所示:

又用 canvas 实现了一个旋转的伪3D球体,来瞧瞧?

通过上图可以看到,在初始化的时候会生成很多粒子,这些粒子会按照一定的轨迹形成一个椭圆,最后围着这个椭圆旋转,从而实现我们看到的伪3D效果。

那这个效果是如何实现的呢?接下来我们就一起来看一下吧!

旋转的圆

首先我们先来实现中间的一个圆,只要找到中件的居中线,再向上下方向扩展,就能实现这个伪3D效果了。

基础的 htmlcss 代码这里就不在列出来了,如果还有不了解的童鞋,可以查看前面的文章,里面都有相关的代码。这里我们依旧采用 TS + ES6 的语法来开发这个效果,首先我们还是需要先定义一个 Sphere 类,并且完成初始的 constructor 函数,让我们一起来看一下代码,如下:

class Sphere {
    canvas: HTMLCanvasElement;
    ctx: CanvasRenderingContext2D;
    particles: Particle[];
    radius: number;
    constructor() {
        this.canvas = document.getElementById('canvas') as HTMLCanvasElement;
        this.ctx = this.canvas.getContext('2d');
        this.canvas.width = innerWidth;
        this.canvas.height = innerHeight;
        this.particles = [];
        this.radius = 280;
    }
}

初始化的 constructor 函数准备好后,我们还需要创建一个 Particle 类,它主要用于生成粒子,让我们一起来看一下 Particle 类的相关代码,如下:

class Particle {
    x: number;
    y: number;
    r: number;
    d: number;
    op: number;
    vx: number;
    vy: number;
    ctx: CanvasRenderingContext2D;
    constructor(d: number, x: number, y: number, r: number, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) {
        // d: 椭圆的角度
        // x, y: 圆心
        // r: 半径
        this.x = Math.random() * canvas.width;
        this.y = Math.random() * canvas.height;
        this.r = r;
        this.d = d;
        this.op = 1;
        // 椭圆圆心坐标
        this.vx = x;
        this.vy = y;
        this.ctx = ctx;
    }
    update() {
        // 弧度
        this.d += Math.PI / 360;
        // 完整的弧度值是 Math.PI * 2,相当于每次旋转圆周的 1/720
        this.x += (this.vx - this.x) / 30;
        this.y += (this.vy - this.y) / 30;
    }
    draw() {
        let x = this.r * Math.cos(this.d) + this.x;
        let y = this.r / 4 * Math.sin(this.d) + this.y;
        this.ctx.beginPath();
        this.ctx.arc(x, y, 2, 0, Math.PI * 2);
        this.ctx.fillStyle = `rgba(0, 0, 0, ${this.op})`;
        this.ctx.fill();
    }
}

Particle 类中,我们需要从外部传入6个参数,其中前面四个分别是要生成的椭圆的角度、圆心的x坐标y坐标,以及要生成的椭圆的半径,后面两个参数则是 canvasctx 对象,通过设置不同的x坐标y坐标,就能创建不同位置的粒子效果。

有了 Particle 类后,我们需要在 Sphere 类中调用它,并生成一个选择的圆,让我们一起来看一下 Sphere 代码的改造,如下:

class Sphere {
    ...other code
    constructor() {
        ...other code

        this.init();
        this.animate();
    }
    
    init() {
        // 一次生成36个粒子,等角度排列
        for (let j = 0; j < Math.PI * 2; j += Math.PI / 18) {
            this.particles.push(new Particle(j, this.canvas.width / 2, this.canvas.height / 2, this.radius, this.canvas, this.ctx));
        }
    }
    
    draw() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        for (const n in this.particles) {
            const p = this.particles[n];
            p.update();
            p.draw();
        }
    }
    
    animate() {
        requestAnimationFrame(() => this.animate());
        this.draw();
    }
}

new Sphere();

Sphere 类中,我们添加一个 init 方法,并格式化要生成粒子的数据,然后添加一个 draw 方法,用于将前面准备的数据转化为页面中的粒子效果,最后不要忘了通过 new 关键词来实例化 Sphere

通过上述修改的代码,最终我们可以得到一个旋转的圆环,如下图所示:

又用 canvas 实现了一个旋转的伪3D球体,来瞧瞧?

我们已经实现了一个圆环的旋转,那么要实现最初的那个伪3D的效果,该如何做呢?

伪3D旋转球体

在前面我们说过,要实现完整的旋转球体,就要找到中间的旋转线,然后再分别向上和向下扩展对应的圆环即可实现这个伪3D的旋转效果了。

让我们一起来修改一下 Sphere 类,只需要再添加一个新的循环,即可获取到更多的点,相关的修改代码如下:

class Sphere {
    ...other code

    init() {
        for (let i = 0; i < 5; i++) {
            const x = this.radius * Math.cos(-Math.PI / 10 * i);
            const y = this.radius * Math.sin(-Math.PI / 10 * i);
            // 一次生成36个粒子,等角度排列
            for (let j = 0; j < Math.PI * 2; j += Math.PI / 18) {
                this.particles.push(new Particle(j, this.canvas.width / 2, this.canvas.height / 2 + y, Math.abs(x), this.canvas, this.ctx));
            }
        }
    }

    ...other code
}

通过上述代码可以看到,我们只是在 init 方法中添加了一层新的循环,并且根据三角函数相关的计算原理,得到了新的圆环的生成点,最终实现的效果如下图所示:

又用 canvas 实现了一个旋转的伪3D球体,来瞧瞧?

可以看到当我们在外面又添加了一层循环后,生成了5个旋转的圆环,接下来只需要再向下添加对应的圆环即可,修改 init 方法,代码如下:

class Sphere {
    ...other code

    init() {
        for (let i = 0; i < 5; i++) {
            const x = this.radius * Math.cos(-Math.PI / 10 * i);
            const y = this.radius * Math.sin(-Math.PI / 10 * i);
            // 一次生成36个粒子,等角度排列
            for (let j = 0; j < Math.PI * 2; j += Math.PI / 18) {
                this.particles.push(new Particle(j, this.canvas.width / 2, this.canvas.height / 2 + y, Math.abs(x), this.canvas, this.ctx));
                this.particles.push(new Particle(j, this.canvas.width / 2, this.canvas.height / 2, this.radius, this.canvas, this.ctx));
                if (i !== 0) {
                    this.particles.push(new Particle(j, this.canvas.width / 2, this.canvas.height / 2 - y, Math.abs(x), this.canvas, this.ctx));
                }
            }
        }
    }

    ...other code
}

可以看到在第二层的循环中,通过传入 Particle 类中不同的参数,从而生成两个方向的旋转圆环,最后我们再修改一下 Sphere 类中的 draw 方法,让整个旋转的3D圆环有一个拖尾的效果,相信大家对这块的内容已经很熟悉了,一起来看一下修改后代码,如下:

class Sphere {
    ...other code

    draw() {
        this.ctx.fillStyle = `rgba(255, 255, 255, 0.3)`;
        this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
        ...other code
    }
}

修改后的旋转球体就会带上拖尾的效果,最终的完整代码及实现效果可以在这里进行查看:

总结

总的来说,只要掌握了 canvas 中粒子效果的生成以及三角函数的相关玩法,那么就可以实现更多更有趣的效果。

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

往期回顾

canvas 实现多彩的圆环数字时钟

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

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

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

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

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

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

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

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