preface

I am Fly, canvas is really a powerful thing, and I can’t get rid of it every day. I can play games and manipulate pictures. I will share an article with you later. But that’s not the point of this article. What can you learn from today’s article

  1. Canvas is a simple drawing tool
  2. Canvas draws smooth curves, which is the focus of this article

Then someone asked me she…? , I don’t have her in my heart, I only have you coder, now let’s learn together, I expect to read for 5 minutes.

CANVAS implements a drawing widget

Because it is also relatively simple, LET me outline the idea:

  1. First of all, I established to monitor three events on the canvas canvas, respectively is mouseMove, mouseDown, mouseUp three events, at the same time created isDown this variable, to identify the current drawing is open
  2. So when we press the mouse which is the mouseDown event to start the brush, we have an initial point, we set isDown to true, and then we start moving, we can determine the end of the line, and then we can set the end of the line to be the starting point of the next line, Over and over again, mousueUp sets the isDown variable to false and clears the start and end points
  3. Through mouseMove events, coordinate points passed by the mouse are continuously collected. When and only when isDown is true (that is, in writing state), the current point is connected and drawn with the previous point through the LineTo method of canvas.

The code is as follows:

class board { constructor() { this.canvas = document.getElementById('canvas') this.canvas.addEventListener('mousemove', this.move.bind(this)) this.canvas.addEventListener('mousedown', this.down.bind(this)) this.canvas.addEventListener('mouseup', this.up.bind(this)) this.ctx = this.canvas.getContext('2d') this.startP = null this.endP = null this.isDown = false this.setLineStyle() } setLineStyle() { this.ctx.strokeStyle = 'red' this.ctx.lineWidth = 1 this.ctx.lineJoin = 'round' this.ctx.lineCap = 'round' } move(e) { if (! this.isDown) { return } this.endP = this.getPot(e) this.drawLine() this.startP = this.endP } down(e) { this.isDown = true this.startP = this.getPot(e) } getPot(e) { return new Point2d(e.offsetX, e.offsetY) } drawLine() { if (! this.startP || ! this.endP) { return } this.ctx.beginPath() this.ctx.moveTo(this.startP.x, this.startP.y) this.ctx.lineTo(this.endP.x, this.endP.y) this.ctx.stroke() this.ctx.closePath() } up(e) { this.startP = null this.endP = null this.isDown = false } } new board()Copy the code

Point2d is a class for a 2D point that I wrote myself. If you don’t know, I won’t repeat it here. Let’s take a look at the GIF:

Careful students may find that the lines drawn have a strong sense of broken lines. The reason for this is that the line drawn is actually a polyline with multiple segments, and the line connecting two points is a straight line

How do I draw a smooth curve

When I think of curves, I have to mention Bezier curves. In my previous articles, I systematically introduced Bezier curves and the derivation process of bezier curve equations — portals

Canvas is definitely the bezier curve supporting quadraticCurveTo(cp1x, cp1y, x, y), which is mainly a starting point, an end point, and a control point. There is a clever algorithm that can be used to solve this problem.

Algorithm for obtaining information of second-order Bessel curve

Suppose we have A point A, B, C, D, E, F, G during mouse movement. To draw a smooth curve, we take B1, halfway between B and C, as the end point of the first Bezier curve, and B as the control point. As shown in figure:

Next, calculate the midpoint c1 of CD with B1 as the starting point, C as the control point and C1 as the ending point. Draw the following figure:

And then I’m going to keep doing this, and I’m going to get a smooth curve. Theoretical basis we understand, we transform the above drawing line method:

Achieve a smooth curve

Divide the x and y coordinates by 2:

getMid(p1, p2) {
  const x = (p1.x + p2.x) / 2
  const y = (p1.y + p2.y) / 2
  return new Point2d(x, y)
}
Copy the code

We need at least three points to draw a second-order Bezier curve, so we need an array to store all of the points that we’re moving.

Let me first implement the method of drawing bezier curves:

drawCurve(controlP, endP) {
  this.ctx.beginPath()
  this.ctx.moveTo(this.startP.x, this.startP.y)
  this.ctx.quadraticCurveTo(controlP.x, controlP.y, endP.x, endP.y)
  this.ctx.stroke()
  this.ctx.closePath()
}
Copy the code

Then modify the events in move

move(e) { if (! this.isDown) { return } this.endP = this.getPot(e) this.points.push(this.endP) if (this.points.length >= 3) { const [controlP, endP] = this.points.slice(-2) const middle = this.getMid(controlP, endP) this.drawCurve(controlP, middle) this.startP = middle } }Copy the code

So what I’m doing here is I’m always going to take the last two points, and then I’m going to draw the Bezier curve and then I’m going to set the bezier end as the starting point for next time. This is a guarantee of a continuous Bezier curve.

Let’s take a look at the GIF:

conclusion

So far this article is also finished, if you have a better idea welcome to communicate with me, I am just a rough expression. Canvas drawing continuous smooth curve focus – or how to find control points this point is very important ha! Next article: Off-screen rendering of Canvas and use of Webworker.

Study and communication

All the code in this article is welcome to fork and Stark on github. Finally, follow the public account [Front-end Graphics] and add group to the visual communication group. Let’s learn about data visualization together!