我正在参与「码上挑战赛」概略请看:码上挑战赛来了!

前语

最近在逛 B站 的时分看到一个用 canvas 完结的效果,特别的炫酷,刚好学习了一下 canvas 相关的知识点,今天就一同来学习一下这个炫酷的效果吧!首要我们仍是先来看一下具体完结的效果,如图:

canvas 完结七彩炫酷圆环,快来看看

在这个效果中,有一个旋转的圆,会随从鼠标的移动而移动,当鼠标不动时,它会盘绕这个圆的圆心进行旋转,具体的效果现已知晓,下面就一同来看看怎么完结吧!

基础准备工作

我们先将基础东西都准备好,后续直接讲解相关的思路,这样更利于我们学习和了解。先将基础的 html 代码写好,如下:

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

基础的 html 中只包含一个 canvas 标签,其它什么都没有,相关的样式如下:

 body {
    margin: 0;
    overflow: hidden;
    background: black;
}
#canvas {
    background: rgba(0, 0, 0, 1);
    position: relative;
    z-index: 1;
}

基础的准备工作完结了,接下来就该讲解一下完结的相关思路了。

完结思路

canvas 中,一个小球假如要顺着某个点进行旋转,我们需求凭仗 Math.sin()Math.cos() ,为什么需求凭仗这个两个方法呢?我们可以一同看一下 sincos 相关的图,如下所示:

canvas 完结七彩炫酷圆环,快来看看

canvas 完结七彩炫酷圆环,快来看看

我们完结一个最简略的圆,然后让它进行旋转,相关代码如下:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = innerWidth;  // window.innerwidth 可以省掉 window
canvas.height = innerHeight; // 同上
let theta = 0;
let speed = 0.1;
function animate() {
    requestAnimationFrame(animate);
    theta += speed;
    const x = canvas.width / 2 + Math.cos(theta) * 50;
    const y = canvas.height / 2 +  Math.sin(theta) * 50;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.beginPath();
    ctx.fillStyle = 'red';
    ctx.arc(x, y, 5, 0, Math.PI * 2);
    ctx.fill();
    ctx.closePath();
}
animate();

我们借用 Math.cos() 函数批改小球的 x轴 方向,借用 Math.sin() 函数批改小球的 y轴 方向,通过 requestAnimationFrame 方法来履行动画,然后不断的批改小球的运动轨迹,毕竟完结的效果如下图所示:

canvas 完结七彩炫酷圆环,快来看看

一个小球的运动轨迹完结了,那么要完结多个小球运动,该怎样做呢?只需求多创建一些小球,让它们按上面的方法进行运动即可,通过循环遍历创建多个小球,这儿我们运用面向目标的方法来定义小球,相关代码如下:

class Ball {
    constructor(obj) {
        this.x = obj.x;
        this.y = obj.y;
        this.radius = obj.radius;
        this.color = obj.color;
        this.canvas = obj.canvas;
        this.ctx = obj.ctx;
        this.mouse = obj.mouse;
        this.theta = Utils.randomDoubleFromRange(0, Math.PI * 2);
        this.speed = 0.05;
        this.dragSpeed = 0.05;
        this.distance = Utils.randomDoubleFromRange(70, 100);
        this.lastMouse = { x: obj.x, y: obj.y };
    }
    draw() {
        this.ctx.beginPath();
        this.ctx.fillStyle = this.color;
        this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
        this.ctx.fill();
        this.ctx.closePath();
    }
     update() {
        this.x = this.canvas.width / 2 + Math.cos(this.theta) * this.distance;
        this.y = this.canvas.height / 2 + Math.sin(this.theta) * this.distance;
        this.theta += this.speed;
        this.draw();
    }
}

通过面向目标的方法来定义小球,在调用的地方只需求通过 new 关键词实例化目标即可,我们通过循环来创建小球,相关代码如下:

const balls = [];
const colorArray = ["#97A7F8", "#C957CA", "#76E2FE"];
function init() {
    for (let i = 0; i < 50; i++) {
        // 随机创建小球颜色
        let color = Utils.randomColors(colorArray);
        balls.push(
            new Ball({
                x: canvas.width / 2,
                y: canvas.height / 2,
                radius: 3,
                color,
                canvas: anvas,
                ctx: ctx
            })
        );
    }
}

通过循环动态创建 50个 小球,毕竟完结的效果如下所示:

canvas 完结七彩炫酷圆环,快来看看

小球有了,但是还记得我们前面完结的效果吗?是一个拖尾的效果,其实要完结这个拖尾效果很简略,只需求批改 animate 中的 clearRect 方法即可,相关代码如下:

animate() {
    requestAnimationFrame(() => this.animate());
    this.ctx.fillStyle = "rgba(255, 255, 255, 0.1)";
    this.ctx.fillRect(
        0,
        0,
        this.canvas.width,
        this.canvas.height
    );
    for (let ball of this.balls) {
        ball.update();
    }
}

clearRect 方法替换为 fillRect,并且增加 fillStyle 特色,设置相关的背景色为白色透明度为0.1的值,完结的效果如下所示:

canvas 完结七彩炫酷圆环,快来看看

虽然现已完结了旋转的效果,但是这儿用 ctx.arc() 的小球线条看起来太粗了,因而我们继续对代码进行改造,相关代码如下:

draw(lastPosition) {
    this.ctx.beginPath();
    this.ctx.strokeStyle = this.color;
    this.ctx.lineWidth = this.radius;
    this.ctx.moveTo(lastPosition.x, lastPosition.y);
    this.ctx.lineTo(this.x, this.y);
    this.ctx.stroke();
    this.ctx.closePath();
}
update() {
    let lastPosition = { x: this.x, y: this.y };
    this.x = this.canvas.width / 2 + Math.cos(this.theta) * this.distance;
    this.y = this.canvas.height / 2 + Math.sin(this.theta) * this.distance;
    this.theta += this.speed;
    this.draw(lastPosition);
}

我们将 ctx.arc() 替换为 ctx.lineWidth() ,运用线条的方法来烘托圆环,毕竟完结的效果如下图所示:

canvas 完结七彩炫酷圆环,快来看看

到这儿,我们的效果现已完结了一大半了,当然目前还剩下当鼠标在页面中移动时圆环要跟着一同移动的效果了。

圆环随从鼠标

要完结圆环随从鼠标移动而移动,其实也不难,首要我们需求给 window 增加 mousemove 事情,相关代码如下:

eventFn() {
    addEventListener("mousemove", (event) => {
        this.mouse.x = event.clientX;
        this.mouse.y = event.clientY;
    });
    addEventListener("resize", () => {
        this.canvas.width = innerWidth;
        this.canvas.height = innerHeight;
    });
}

因为 addEventListener 默认是绑定在 window 上面的,因而不需求在前面增加 window。鼠标事情有了,接下来就该完结移动鼠标时相关的逻辑了,看一下相关代码:

update() {
    let lastPosition = {
        x: this.x,
        y: this.y,
    };
    this.lastMouse.x += (this.mouse.x - this.lastMouse.x) * this.dragSpeed;
    this.lastMouse.y += (this.mouse.y - this.lastMouse.y) * this.dragSpeed;
    this.x = this.lastMouse.x + Math.cos(this.theta) * this.distance;
    this.y = this.lastMouse.y + Math.sin(this.theta) * this.distance;
    this.theta += this.speed;
    this.draw(lastPosition);
}

鼠标移动的时分,我们可以获取到其时的位置,通过 event.clientXevent.clientY,并把这两个值保存在 this.mouse 中,这样当鼠标移动的时分,调用 update 方法,然后让挑选的小球随从鼠标移动而移动。毕竟的完结效果可以在这儿进行检查:

总结

通过学习 canvas 的基础 api,完结了一个炫酷的圆环效果,其中需求注意的主要有 Math.sin()Math.cos() 的调用,以及怎么通过 canvasctx 特色来烘托不同的图形,其实 canvas 相关的 api 只要运用熟练了,就可以完结各种炫酷的效果。

最后,假如这篇文章有帮忙到你,❤️重视+点赞❤️鼓舞一下作者,谢谢我们

往期回忆

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