“Weekly canvas animation” — magical use of difference function

Time:2021-7-9

Weekly point canvas animation code file

It seems that the last update was 11 years ago. It’s been more than half a month. Now I’m not embarrassed to update. This time we don’t cover the content of canvas 3D, but mainly share a more dazzling animation effect, which can be regarded as the last article“Weekly point canvas animation” — 3D point line and water wave animationEnhanced version of. Animation effects fromcodePen。 In this article, we will analyze how to achieve this effect. If you are confused about the source code, I believe you will suddenly realize it after reading the analysis. First, the rendering:

1. Principle analysis

Compared with the effect of the simple water wave animation in the previous article, the animation effect of this paper can not only interact with the mouse, but also the formation of waves is more natural and more in line with the laws of physics. The whole animation process is just as shown in the motion picture. Click the mouse at the position of the liquid level, and there will be a big fluctuation of the liquid level. Then the vibration will spread to both sides. With the attenuation of energy, the vibration amplitude will be lower and lower, and the last energy will decay to zero, and the page tends to be calm. Doesn’t it sound very mysterious? It feels very profound! Chairman Mao told us not to be confused by the surface phenomena of objects (who knows who said that O (^ ▽ ^) o). Let’s analyze the principle step by step.

First of all, in the static state, we can see that the whole liquid surface is equivalent to a rectangle. When we click the position of the liquid surface, the rectangle changes accordingly. But it’s not the whole rectangle that’s changed, it’s notIt’s just that the top of the rectangle has changed。 So how do you just change the top of the rectangle? The secret is at the top of the rectangle, not simply from the left pointlineTo()Go to the point on the right. It’s a lot of dotslineTo()form. This may not be easy to understand

In the upper part, we set a lot of points. The ordinates of these points are the same, but they are separated by a certain distance in the horizontal direction. In this way, in a static state, we can see that it is the same as an ordinary rectangle. When we change the position of these points, we can change the shape of the rectangle at the same time, thus forming different effects.

2. Difference equation

When it comes to the difference equation, maybe many people will have a headache, but there’s no way. If it hurts, it will hurt! This knowledge point is in the section of differential equation in higher mathematics. If you don’t understand it, forget it! Remember the following usage is also good, but in order to force the grid, we still briefly introduce.

In mathematics, recurrence relation, namely difference equation, is an equation that recursively defines a sequence: each item of the sequence is a function defined as the previous item. Some simple definitions of recurrence relations may show very complex (chaotic) properties. They belong to the field of nonlinear analysis in mathematics.

Remember that,Each term of a sequence is a function defined as the previous termThis is the principle we use. If his image is drawn with MATALAB, it is as follows:

Just pay attention to the original function. The red curve is OK. Is it especially like water wave. What we need to do is to arrange the points according to this waveform.

3. Code implementation

1. Preparation

Here’s your favorite code time. First of all, we create a vertex class, which is used to define and update the heap of points. The code isvertex.jsAs follows:

function Vertex(x,y,baseY){
        this.baseY = baseY;         // baseline
        this.x = x;                 // Coordinates of points
        this.y = y;            
        this.vy = 0;                // Vertical velocity
        this.targetY = 0;           // Target location
        this.friction = 0.15;       // friction
        this.deceleration = 0.95;   // Slow down
    }
//Y coordinate update
Vertex.prototype.updateY = function(diffVal){
        this.targetY = diffVal + this.baseY;   // Change target location
        this.vy += (this.targetY - this.y);       // speed
        this.vy *= this.deceleration;
        this.y += this.vy * this.friction;     // Change the vertical position of the coordinates
    }

We’re going to use this function to create that pile of points. Go back to our main file index. JS. Let’s initialize something to use first

var canvas = document.getElementById('canvas'),
    ctx = canvas.getContext('2d'),
    W = window.innerWidth;
    H = window.innerHeight;

    canvas.width = W;
    canvas.height = H;

Var Color1 = "#6ca0f6" // the color of rectangle 1
    color2 = "#367aec";   // Color of rectangle 2
    
Var vertices = [], // vertex coordinates
    Vernum = 250, // number of vertices
    Diffpt = [], // difference value

Then, create the point and add itpushentervertexesAt the same time, create a corresponding number of difference values, and put them in thediffPtArray, so that each point has a corresponding difference value.

for(var i=0; i<verNum; i++){
    vertexes[i] = new Vertex(W/(verNum-1)*i, H/2, H/2);
    diffPt[i] = 0;                                         // The initial value is 0
}

As a result, theyThe coordinates are all here(H/2)Take a point at a certain interval in the horizontal coordinate. In this case, take a point every 4.5 pixels, which is related to the width of your canvas and the number of points. So we have created the point. Let’s draw it to see the effect.

The code is as follows:

function draw(){
        
        //Rectangle 1
        ctx.save()
        ctx.fillStyle = color1;
        ctx.beginPath();
        ctx.moveTo(0, H);
        ctx.lineTo(vertexes[0].x, vertexes[0].y);
        for(var i=1; i<vertexes.length; i++){
            ctx.lineTo(vertexes[i].x, vertexes[i].y);
        }
        ctx.lineTo(W,H);
        ctx.lineTo(0,H);
        ctx.fill();
        ctx.restore();
        
        //Rectangle 2
        ctx.save();
        ctx.fillStyle = color2;
        ctx.beginPath();
        ctx.moveTo(0, H);
        ctx.lineTo(vertexes[0].x, vertexes[0].y+5);
        for(var i=1; i<vertexes.length; i++){
            ctx.lineTo(vertexes[i].x, vertexes[i].y+5);
        }
        ctx.lineTo(W, H);
        ctx.lineTo(0, H);
        ctx.fill();
        ctx.restore();
}

As you can see, our liquid level is completely static (because there is no update point). The reason why you want to draw two rectangles is that you can see from the renderings. Just for better looking, you can draw the third layer and the fourth layer. Now let’s update the coordinates of these points.

2. Core code

We put the point update in theupdateFunction. First, we set up an initialShock point,Buffer variableandInitial difference value

var vPos = 125;  // Shock point
var dd = 15;     // buffer
var autoDiff = 1000;  // Initial difference value

The shock point here is our starting point, which meansvertexesNo.1 in125When the earthquake starts at point, its corresponding difference value isautoDiff。 Its change will cause other points to change, so as to achieve the effect of updating other difference values.

function update(){
        autoDiff -= autoDiff*0.9;        //1
        diffPt[vPos] = autoDiff;         

        //Left
        for(var i=vPos-1; i>0; i--){     //2
            var d = vPos-i;
            if(d > dd){
                d=dd;
            }
            diffPt[i]-=(diffPt[i] - diffPt[i+1])*(1-0.01*d);
        }
        //Right side
        for(var i=vPos+1; i<verNum; i++){   //3
            var d = i-vPos;
            if(d>dd){
                d=dd;
            }
            diffPt[i] -= (diffPt[i] - diffPt[i-1])*(1-0.01*d);
        }

        //Update y coordinate
        for(var i=0; i<vertexes.length; i++){  //4
            vertexes[i].updateY(diffPt[i]);
        }
    }

Now let’s explain the above part in detail
Code 1: we set the differential offset of the starting position asautoDiff=100, attentionautoDiff -= autoDiff*0.9;That is to say, its value changes every frame.

Code 2: to the left of the starting position, focus ondiffPt[i]-=(diffPt[i] - diffPt[i+1])*(1-0.01*d);This is my line.iThe starting position of is124, the default difference value is0。 A little simple calculation, you will find that after the update, the124The difference value of point is99In the same way123be entitled as97.02。 And so on, we can get the difference values of all the points in the first frame. The same is true on the right.

Code 4: after getting the difference value of the first frame, it’s time to call the update function of each point, and pass in the calculated difference value. The effect is shown in the figure below

to glance atupdateYFunction, we take the target locationtargetYSet to differential valuediffValAnd baselinebaseYThe sum of the two. Then, calculate the speed of the movement through the distancevyFinally, the velocity is applied to the ordinate of the point. Is this section very similar to the elastic animation slow motion animation section?

Under the effect of buffer coefficient DD, the waves on both sides will become smaller and smaller in the process of diffusion, and finally close to 0We also use this variable to control the viscosity coefficient of the liquidTo achieve the effect that the object with high viscosity diffuses more slowly and fluctuates less, and the object with low viscosity diffuses rapidly but fluctuates greatly.

Later, becauseautoDiffThe decline of the,The superposition of different amplitude waveforms forms the wave effect, and finally attenuates to 0. The liquid surface tends to be calm

Now, let’s put update () and draw () into the animation loop, and you’ll see the water waves rise and fall and then calm down.

(function drawframe(){
        ctx.clearRect(0, 0, W, H);
        window.requestAnimationFrame(drawframe, canvas);
        update()
        draw();
    })()

3. Mouse interaction

The above code has achieved the effect of wave animation, but after the shock is completed, it will be calm, and the shock effect will not occur again. In this step, we will achieve the effect of “which shock”. The idea is very simpleThe reason why the water wave region is quiet is that the difference value of the starting position is constantly decreasing. We only need to reset it at the mouse click positionautoDiffThat’s it. In addition, the location of the starting point should also be changed into the location of the mouse click.The code is as follows:

canvas.addEventListener('mousedown', function(e){
        var mouse = {x:null, y:null};

        if(e.pageX||e.pageY){
            mouse.x = e.pageX;
            mouse.y = e.pageY;
        }else{
            mouse.x = e.clientX + document.body.scrollLeft +document.documentElement.scrollLeft;
            mouse.y = e.clientY + document.body.scrollTop +document.documentElement.scrollTop;
        }

        //Reset difference
        if(mouse.y>(H/2-50) && mouse.y<(H/2 +50)){
            autoDiff = 1000;
            vPos = 1 + Math.floor((verNum - 2) * mouse.x / W);
            diffPt[vPos] = autoDiff;
        }

        console.log(mouse.x, mouse.y)

    }, false)

It should be noted that we have not subtracted the offset of canvas when getting the mouse position. This is because canvas is set in full screen. So, if your canvas is not full screen size, we recommend that you use oursutils.jsMethod in filecaptureMouseTo get the coordinates of the mouse.

In addition, to determine whether the mouse clicks on the liquid surface, we set a relatively wide range, up and down a total of 100px. The purpose of this is to make it easy for users to trigger this event, not only on the only value of the page. I believe you have done this before. For smaller objects, we will mask a larger transparent object, and then trigger events on the object to facilitate user operation.

Other small functions such as color change will not be introduced too much, see you!!!