This is the 8th day of my participation in the August Text Challenge.More challenges in August

preface

Water is the best.

– thales

introduce

When we open the website or the game, loading is unavoidable. In this boring waiting, people will be at a loss what to do, and if it takes too long, it will be a kind of torture. The only thing we can do now may be to let them calm down a little and wait for the task to complete. And just like that, the loading animation was born. What we are doing today is a kind of loading animation – wavy ball. This is very common, some with CSS3 to draw two circles to do camouflage implementation, some with SVG path animation implementation. But today we’re going to do a pure Canvas API to implement it.

This example is very simple, we will explain from the infrastructure, wave ball, wave animation. Are you ready? Here we go!!

Set out

1. Infrastructure

<canvas id="canvas"></canvas>
<script type="module" src="./app.js"></script>
Copy the code

Here we go to the canvas mode, and then use the Module mode to import JS to facilitate its module import.

* {
    padding: 0;
    margin: 0;
}
​
html.body {
    width: 100%;
    height: 100vh;
    overflow: hidden;
}
body {
    background-image: repeating-radial-gradient(circle at center center, transparent 0px, transparent 8px.rgba(255.255.255.0.05) 8px.rgba(255.255.255.0.05) 11px, transparent 11px, transparent 17px.rgba(255.255.255.0.05) 17px.rgba(255.255.255.0.05) 25px, transparent 25px, transparent 38px.rgba(255.255.255.0.05) 38px.rgba(255.255.255.0.05) 42px), repeating-radial-gradient(circle at center center, rgb(0.0.0) 0px.rgb(0.0.0) 11px.rgb(0.0.0) 11px.rgb(0.0.0) 19px.rgb(0.0.0) 19px.rgb(0.0.0) 24px.rgb(0.0.0) 24px.rgb(0.0.0) 33px.rgb(0.0.0) 33px.rgb(0.0.0) 44px.rgb(0.0.0) 44px.rgb(0.0.0) 46px);
    background-size: 60px 60px;
}
​
#canvas {
    width: 100%;
    height: 100%;
}
Copy the code

In the CSS, we used repeating-radial-gradient to draw a background image for a beautiful background, as follows:

/*app.js*/
class Application {
  constructor() {
    this.canvas = null;       / / the canvas
    this.ctx = null;          / / environment
    this.w = 0;               / / canvas width
    this.h = 0;               / / the canvas
    this.init();
  }
  init() {
    / / initialization
    this.canvas = document.getElementById("canvas");
    this.ctx = this.canvas.getContext("2d");
    window.addEventListener("resize".this.reset.bind(this));
    this.reset();
    this.render();
    this.step();
  }
  reset() {
    // Screen changes
    this.w = this.canvas.width = this.ctx.width = window.innerWidth;
    this.h = this.canvas.height = this.ctx.height = window.innerHeight;
    this.render();
  }
  render() {
    / / the main rendering
  }
  step(delta) {
    / / redraw
    const { w, h, ctx } = this;
    requestAnimationFrame(this.step.bind(this));
    ctx.clearRect(0.0, w, h); }}window.onload = new Application();
Copy the code

So let’s write down the main structure, and the main thing we do is we take the canvas, and we spread the canvas all over the screen, and that’s it.

2. Wave balls

So let’s just write a class that abstractions properties like polish ball size, position, color that’s pretty easy to think about, and if you think about it a little bit more, you’ll think about waves that have amplitude and frequency and you’ll have a progress value if you’re loading an animation.

That’s about it.

/*WaveBall.js*/
class WaveBall {
    constructor(options) {
        this.x = 0;                               // X coordinates
        this.y = 0;                               // Y coordinates
        this.size = 200;                          // Size (diameter)
        this.color = "rgba(55, 133, 207, .75)";   / / color
        this.waveWidth = 0.025;                   / / wave frequency
        this.waveHeight = 5.6;                    / / breadth
        this.progress = 0;                        / / schedule
        Object.assign(this, options);
        this.ctx = null;                         / / environment
        this.startX = 0;                         // The initial position
        this.offsetX = [0.0];                   // The offset position
        this.vx = [0.156.0.097];                // increase in x axis
        this.bgColor = this.color;               / / the background color
        return this;
    }
    render(ctx) {
        this.ctx = ctx;
        const {color,size} = this;
        this.bgColor = ctx.createLinearGradient(size / 2, size / 2, size / 2, size);
        this.bgColor.addColorStop(0, color);
        this.bgColor.addColorStop(1, color);
        this.drawBall();
        this.drawWave();
        return this;
    }
    drawBall() {}
    drawWave() {}
    update() {
        this.drawWave();
        this.drawBall(); }}export default WaveBall;
Copy the code

Now that we have the basic variables, let’s draw a circle that will act as the periphery of the sphere.

/*WaveBall.js*/
drawBall() {
    const { size, ctx, x, y, color } = this;
    ctx.save();
    ctx.lineWidth = 3;
    ctx.strokeStyle = color;
    ctx.translate(x, y)
    ctx.beginPath();
    ctx.arc(0.0, size / 2.0.2 * Math.PI);
    ctx.stroke();
    ctx.restore();
}
Copy the code

I won’t repeat it too much here, size is the diameter, so divide by 2 to get the radius and you’ll get a circle.

Here we go. Let’s draw a wave

/*WaveBall.js*/
drawWave(n = 0) {
    this.offsetX[n] += this.vx[n];
    const { startX, size, ctx, offsetX, x, y, bgColor, waveWidth, waveHeight,progress } = this;
    let height = -progress/100 * size - 5; 
    ctx.save();
    ctx.translate(x, y)
    ctx.beginPath();
    ctx.arc(0.0, size / 2.0.2 * Math.PI);
    ctx.clip();
    ctx.beginPath();
    for (let i = - size / 2; i < size / 2; i++) {
        const h = waveHeight * Math.sin((startX + i) * waveWidth + offsetX[n]);
        ctx.lineTo(i, h+size/2 + height);
    }
    ctx.lineTo(size / 2, size / 2);
    ctx.lineTo(-size / 2, size / 2);
    ctx.fillStyle = bgColor;
    ctx.fill();
    ctx.restore();
}
Copy the code

Here we need to explain in advance, because our wave ball will actually have two waves, the vX variable will affect the speed, will cause the animation to appear false peak, increase the reality, if you want to three or four waves is also a logic. So, when we update, we perform the draw wave method twice, as follows:

/*WaveBall.js*/
update() {
    this.drawWave(0);
    this.drawWave(1);
    this.drawBall();
}
Copy the code

We draw the whole is not that complicated, actually, is that we calculate the distance of the x axis to draw, and then use trigonometric function about his height, then there will be ups and downs of the picture, and then connected to form a rectangle, right here don’t forget to still use to clip method to pick out a sphere, with just drawing the size of the circle. That’s it. We’ve drawn the wave, but we can’t see it yet. We need to introduce it into the master logic.

3. Wave animation

Nonsense not to say, we first introduce, first see long what kind ~

/*app.js*/
import WaveBall from "./js/WaveBall.js";

render() {
    const { w, h, ctx } = this;
    this.waveBall = new WaveBall({
        x: w / 2.y: h / 2.size: h * 0.45.progress:50
    }).render(ctx);
}
Copy the code

For perspective, let’s set the wavy ball instantiation to start at progress 50 and center it on the screen, with its size positively related to screen height.

I still don’t see the result, because we didn’t execute his update.

Finally, we just need to update it during the Step redraw phase.

/*app.js*/
step(delta) {
    const { w, h, ctx } = this;
    requestAnimationFrame(this.step.bind(this));
    ctx.clearRect(0.0, w, h);
    this.waveBall.progress += 0.1;
    this.waveBall.progress%= 100;
    this.waveBall.update();
}
Copy the code

Don’t forget, we simulated the loading process, so add a little bit at a time, and when it hits 100 it starts from scratch

With that, our wavy ball loading animation is complete, live demo

expand

We implemented this animation using Math.sin. Math is a tool that allows us to do a lot more with ease, but there’s a lot more that can be done with this. Waves can also read audio to parse, generate visual audio files, or slide a site from a rectangle to a wave.

By the way, bezier curves can also be used to draw waves, which is also a solution.


In other words, others really tired of watching the loading ball, try to achieve it, their baby where all good, their own loading ball is never tired of seeing ~~