Canvas draws water balloon

To draw a water balloon map by using canvasApi, we continue to be familiar with the basic use of Canvas and cubic Bezier curve, and at the same time introduce new canvas clipping function and anti-aliasing skills. The renderings are as follows: Gitee source code, Github source code

Analysis of preparation

Analysis of the whole water sphere diagram, waveform drawing is the biggest difficulty of the whole drawing process. Drawing waveform, the first difficulty is the shape of ripple, in fact, you can choose two methods to draw, the first one, to adopt the form of arc splicing; The second method is to use cubic Bezier curve to draw the curve. In order to get familiar with the drawing process of the cubic Bezier curve again, method two is adopted. The second difficulty of waveform drawing is how to draw waveform in a circle, which requires the use of clip, Save and restore functions. The details can be found in MDN documents.

The second difficulty of drawing water balloon map is how to draw the effect of water diffuse text. We observed the effect of the water diffuse text, not the water diffuse part is the top color, otherwise it is white. Therefore, we can consider drawing a text with a background color first, and then using the cropping effect to draw the white part of the water.

  • First draw a text with a background color

  • clipAfter clipping, draw an identical white text in the same position

We drew a total of three layers of water ripples, in each layer of water ripples need to draw the white part, otherwise someone behind the waves will cover the text. But text with a background color only needs to be drawn when the lowest ripple is drawn.

Finally, in the water polo after the completion of the drawing, we will find that the circle of the lateral is not smooth, into a jagged, at this point, we need to use the canvas anti-aliasing skills, anti-aliasing actually very simple, just like everyone in the pool is very close to the wall, you may find that metope is bumpy, but stand far, metope can feel very smooth. Because we can’t see details from a distance, our brain automatically thinks that the wall is flat. The same principle can be used for anti-aliasing. As long as you can’t see the real edge, the edge will look smoother. Therefore, it is necessary to use the shadow to blur the edge, and use the attributes of shadowColor shadowBlur shadowOffsetX shadowOffsetY.

Code sample

See the source repository posted at the beginning of the article for complete source code

function WaterCahrt(id, data) {
  let canvas = document.getElementById(id)
  let ctx = canvas.getContext('2d')
  let canvasWidth = canvas.width > canvas.height ? canvas.height : canvas.width
  let r = canvasWidth * 0.8 / 2
  let offset = [0.20.40]
  this.canvas = canvas
  this.ctx = ctx
  this.data = data
  this.r = r / / circle radius
  this.center = canvasWidth / 2 // Center x (y)
  this.intervalId = null
  Object.defineProperty(this.'offset', { // Define the offset of the water chart, automatically trigger the update when the offset changes
    enumerable: false.configurable: false.get() {
      return offset
    },
    set(val) {
      offset = val
      this.refresh()
    }
  })
}
Copy the code

The offset is set as an array so that the three waves move at different speeds.

/** * draw the water wave map *@param {number} The initial x value of the BASex ripple *@param {number} The starting point of the Basey ripple y value *@param {strign} Color The color of the ripple *@param {boolean} Flag whether to draw text with the same color as the ripple. In theory, only the lowest ripple needs to draw */
WaterCahrt.prototype.drawWater = function(basex, basey, color, flag) {
  let bezier = this.getBezierPoints(basex, basey) // Generate a cubic Bezier curve
  this.ctx.save() // Store the brush state
  // Draw the waveform
  this.ctx.beginPath()
  this.ctx.moveTo(this.center - this.r, this.center + this.r)
  this.ctx.lineTo(bezier.x, bezier.y)
  bezier.points.forEach((item) = > {
    this.ctx.bezierCurveTo(item.cp1x, item.cp1y, item.cp2x, item.cp2y, item.x, item.y)
  })
  this.ctx.lineTo(this.center + this.r, this.center + this.r)
  this.ctx.closePath()
  this.ctx.fillStyle = color
  this.ctx.shadowColor = color / / anti-aliasing
  this.ctx.shadowBlur = 2
  this.ctx.fill()
  this.ctx.shadowBlur = 0
  // Draw text with a background color
  this.ctx.font = `normal The ${this.r * 0.2}px blod`
  this.ctx.textAlign = "center"
  if(flag)
    this.ctx.fillText(`The ${(this.data * 100).toFixed(1)}% `.this.center, this.center)
  // Draw the white part of the text
  this.ctx.clip() // Crop according to the path drawn
  this.ctx.fillStyle = '#ffffff'
  this.ctx.fillText(`The ${(this.data * 100).toFixed(1)}% `.this.center, this.center)
  this.ctx.restore() // Restore the brush state, i.e., to the unclipped state
  return this
}
Copy the code

The generation of points of cubic Bezier curves is not described here, and the detailed generation principle can be referred to the article “Method for Determining Control Points of Bezier Curves”.

conclusion

The drawing of the water balloon map is mainly to try the methods related to clip, save and restore, and also introduces a small skill of anti-aliasing. There is not much computation in overall drawing, and there is no interactive effect. There is only an animation effect, which can be realized by drawing animation frames with setInterval interval. It’s not too difficult overall.