[Take canvas to wander (7)] Draw a water polo chart

Time:2019-9-11

[Take canvas to wander (7)] Draw a water polo chart

The sample code is hosted at http://www.github.com/dashnowords/blogs

Blog Garden Address: The original blog catalogue of Dashi Living in the Big Front End

Huawei Cloud Community Address: [Guidelines for Upgrading Your Front-end Crackers]

[TOC]

I. Mission Statement

Use nativecanvasAPIDrawing a water polo map will be an interesting challenge. Water Polo is a common loading animation, which belongs to extended graphics.echartsExtension libraries need to be downloaded when they are used (as well as text cloud plug-ins and map plug-ins, the project address is https://github.com/ecomfe/echarts-liquidfill).

[Take canvas to wander (7)] Draw a water polo chart

II. Key Tips

There are several difficulties in drawing a water polo chart:

  1. Drawing of Water Wave

    In fact, the drawing of water waves is simulated by using the simple harmonic oscillation formula, that is to sayx = A*(wt +φ)Among them, the amplitudeAIt determines the ripple height and angular frequency of water waves.wIt determines the speed and phase of water waves.φBy determining the initial displacement difference, and adding some Y-axis displacement deviation and color difference, different water waves can be simulated, and then only need to be constantly changed in the frame animation.φBy redrawing the curve, we can simulate the effect of the water wave.

  2. Spherical clipping area

    The scope of the water wave cannot flow out of the spherical outline. The method here is to use it before drawing the water wave.context.clip( )The method controls the visible drawing area of water wave in the water ball. If there is a figure outside the water ball that needs to be drawn, remember to call it after each frame of drawing water wave.context.restore( )Cancel the previous clipping.

  3. Drawing of Text

    It’s easier to just draw words floating on a water polo chart, but it’s not so easy to achieve more detailed effects. What we expect to achieve is that when the text is not immersed in the water waves, it will show the blue of the water marks, and the part that is immersed in the water will show white, which will make it more vivid. But it is not easy to draw. If the text is painted blue, the submerged part will disappear in the watermarks. If the watermarks are painted white, the text will be completely invisible when the height of the watermarks is small. So how to achieve such rendering text?

3. Sample code

let options = {
    value:0,
    A:20,//amplitude
    Pos: [300,300], // Water Ball Map Location
    R: 160, // / radius of spherical chart
    Color: [' 2E5199',' 1567c8',' 1593E7',' 42B8F9']// Watermark color
};

start(options);

/**
 * Draw a water polo chart
 */
function start(options) {
    // Move the drawing coordinates to the left boundary point of the water polo chart
    context.translate(options.pos[0],options.pos[1]);
    context.font = 'bold 60px Arial';
    context.textAlign='center';
    context.textBaseLine = 'baseline';
    // Calculating the Drawing Data of Water Ball Chart
    createParams(options);
    // Open frame animation
    requestAnimationFrame(startAnim);
}

// Generated water wave drawing parameters, position coordinate formula is y = A* (wt + phi)
function createParams(options) {
    Options.w = []; // angular velocity of stored water waves
    Options. theta = []; // Store the displacement of each water wave
    for(let i = 0; i < 4; i++){
      options.w.push(Math.PI /(100 + 20*Math.random()));
      options.theta.push(20*Math.random());
    }
}

// Drawing water wave lines
function drawWaterLines(options) {
   let offset;
   Let A = options.a;//sinusoidal curve amplitude
   let y,x,w,theta;
   let r = options.r;
   // Traverse each water texture
   for(let line = 0; line < 4; line++){ 
     context.save();
     // The offset distance of water wave in each drawing
     theta = Math.random();
     offset = r + A / 2  -  (r*19/8 + A) * (options.value / 100 ) + line * r/12;
     // Obtaining the Calculating Parameters of Sinusoidal Curve
     w = options.w[line];
     theta = options.theta[line];
     context.fillStyle = options.color[line];
     context.moveTo(0,0);
     context.beginPath(); 
     // Drawing Sinusoidal Curve with 0.1 Step Sinusoidal Curve
     for(x = 0; x <= 2*r; x+=0.1){
        y = A * Math.sin(w * x + theta) + offset;
        // draw point
        context.lineTo(x,y);
     }
      // Draw a closed figure beyond the sphere of water
      context.lineTo(x,r);
      context.lineTo(x - 2 * r,r);
      context.lineTo(0, A * Math.sin(theta) - options.height);
      context.closePath();
      // Fill in the closed figure to get a water wave
      context.fill();
      // Intercept the water wave range and draw text (explained later here)
      context.clip();
      context.fillStyle = 'white';
      context.fillText(parseInt(options.value,10) + '%',options.r + 10,10);
      context.restore();
   }
}

// Drawing the lowest text
function drawText1(options) {
    context.fillStyle = options.color[0];
    context.fillText(parseInt(options.value,10) + '%',options.r + 10,10);
}

// Frame animation loop
function startAnim() {
    // Simulating Water Wave by Displacement Change
    options.theta = options.theta.map(item=>item-0.03);
    // Calculating the Height of Water Wave with Percentage Progress
    options.value += options.value > 100 ? 0:0.1;
    context.save();
    ResetClip (options);//Cut plot area
    DrawText1 (options);//Draw blue text
    drawWaterLines(options);// Drawing water wave lines
    context.restore();
    requestAnimationFrame(startAnim);
}

/ ** Setting the sphere to the clipping area
* (In this case, there is no part other than water polo that needs to be drawn. In fact, there is no need to add a frame animation loop here, just need to set it at the beginning once. )
*/
function resetClip(options) {
   let r = options.r;
   context.strokeStyle = '#2E5199';
   context.fillStyle = 'white';
   context.lineWidth = 10;
   context.beginPath();
   context.arc(r, 0, r + 10, 0, 2*Math.PI, false);
   context.closePath();
   context.fill();
   context.stroke();
   context.beginPath();
   context.arc(r, 0, r, 0, 2*Math.PI, true);
   context.clip();
}

Viewable effects in browsers:

[Take canvas to wander (7)] Draw a water polo chart

IV. Realization of the Effect of Writing Flooding

In fact, the drawing of text flooding effect is carried out according to the following ideas:

  1. Firstly, the text is drawn with the same color as the top watermarking, so that the text can be displayed in visible color before it is submerged.
  2. In the process of drawing water waves, the connection is called after completion.context.clip( )Method Cut the drawing area into all the immersed parts, then set the fill color to white, and then render the text in the same place, so that the rendered white text will not exceed the range of the watermarks, then the blue part of the text outside the watermarks will be saved on the canvas.
  3. In order to avoid truncating the white part of the text when it is drawn by the next layer of water marks, we need to repeat step 2 after each layer of water marks is drawn. Set all the ranges from the layer of water marks to the bottom of the water ball as the cutting area, and then draw the white part of the text within the layer of water marks, so that when several layers of water marks are drawn, we can finish the drawing. The flooded part of the text will be dyed white.
  4. In such rendering method, the final effect of text is equivalent to splicing the fragments drawn layer by layer. In each rendering process, the last part can be saved, only the part intersecting with the current layer’s watermarks.

If we modify the rendering color of each layer of text, it will be easier to understand the rendering process.

[Take canvas to wander (7)] Draw a water polo chart

V. About Canvas Anti-aliasing

If you look closely at the outer circle of the water polo above, you will find that the outside of the water polo chart is not very flat, and it looks like there will be a lot of jagged teeth. Most of the methods found on the Internet are canvas sizes.(canvas.height,canvas.width) Adjust to Element Size (set in CSS)canvasIt is hoped that the anti-aliasing effect can be achieved by scaling 3-4 times the size of the element, but the measured results have not been significantly improved, and the size of the canvas can be used for scaling.Resolving Image and Filling BlurThe effect is better, but the effect of anti-aliasing seems to be related to the size of the line itself, which is not an absolutely effective scheme. Another more effective scheme is to increase the number of circles drawn.2px-4pxThe dark shadows can weaken the sense of jagged teeth visually.

// Add the following code before drawing the outer circle   
   context.shadowColor = '#2E5199';
   context.shadowBlur = 2;
   context.shadowOffsetX = 0;
   context.shadowOffsetY = 2;

Six. Summary

So far, we have completed the original API drawing of all basic charts in this series. Some relatively advanced charts are not necessarily very complicated. For example, rectangular tree charts are actually rectangular squares, but they help us to observe data in a more intuitive and expressive way. For example, visual renderingwebpackPacking results. The basic task of data visualization is to make data visible, which requires us to choose the right way of expression for the data we want to observe. This is not only achieved by technology, but also requires some artistic cells and imagination. But in any case, this is an interesting direction worth studying.