preface
Hello, glad you saw Aaron’s first article. Do front-end for a period of time, recently decided to sort out the knowledge of visualization, interested friends can have a look.
Make a bitter emoticon similar to wechat
This issue will introduce the usage of canvas to realize an expression through canvas. Here are the faces of the morning’s jerks, so let’s get started.
What is a canvas
Canvas is a new tag element of HTML5, which is usually used to make some interesting animations or composite images. The most common application in the market is H5 active page, and most of them are implemented with Canvas.
canvas
The canvas
Canvas is also a DOM element, but it should be noted that the width and height of the canvas are controlled by the attributes on the label. If the width and height of CSS are smaller than the attributes on the label, they will be stretched and deformed.
<canvas id="canvas" width="800" height="800"> Sorry, you don't deserve canvas </canvas>Copy the code
The coordinate system
The default coordinate origin of canvas is at the upper left corner of canvas element. The right is the extension direction of X axis, and the downward is the extension direction of Y axis.Similar to the CSS, the control coordinates can be translated, rotate, Scale, and Transform.
- Translate: translate the coordinate system. It is equivalent to shifting the origin of the coordinates from 0,0 to dx, dy.
- Rotate (Angle) : rotates the coordinate system. This method controls the clockwise rotation of the coordinate system Angle radian.
- Scale (sx, SY) : Scale coordinate system. The method controls the horizontal scaling of the coordinate system sx and the vertical scaling of the coordinate system SY.
- Transform (A, B, C, D, E, F) : allows scaling, rotating, moving, and tilting the current environment coordinate system, where A: horizontal scaling drawing, B: horizontal tilting drawing, C: vertical tilting drawing, D: vertical scaling drawing, E: horizontal moving drawing, F: vertical moving drawing.
- SetTransform (a, B, C, D,e,f): This method resets the current transform matrix to the identity matrix and then runs the transform with the same parameters.
Common drawing API
- The circular arc
arc(x, y, r,startAngle, endAngle, // Draw an arc from startAngle to endAngle from (x,y) with radius r as the center of a circle. ArcTo (x1,y1, x2, y2, radius) // Draw arcs according to two control points (x1,y1) and (x2, y2) and radius.Copy the code
- The path
BeginPath () creates a new path moveTo(x, y) moves the brush to the position (x, y) closePath() closes the path Stroke () draws the path stroke fill() draws the closed area fillCopy the code
- rectangular
FillRect (x,y, width, height) // Void stokeRect(x,y, width, height) ClearRect (x,y, width, height) // Clear the rectangle (x,y) with width and height as transparentCopy the code
- Bessel curve
Canvas is composed of arcs, paths, rectangles and Bezier curves.
QuadraticCurveTo (CP1x, CP1Y,x,y) // (CP1x, CP1Y) control point (x,y) end point bezierCurveTo(CP1x, CP1Y, CP2X, CP2Y,x, Y)// control point 1 (CP2x,cp2y) Control point 2 (x,y) end pointCopy the code
- gradient
let gradient = ctx.createLinearGradient( x1 ,y1 ,x2 ,y2); // Let gradient = CTX. CreateRadialGradient (x1,y1, R1,x2,y2, R2); AddColorStop (position, color)// Position: between 0 and 1 color: the color of positionCopy the code
- The text
StrokeText (text,x, y, maxWidth)) Draws a text border at the position (x,y). FillText (text,x, y, maxWidth) draws a text border at the position (x,y)Copy the code
State save & Restore
As the methods of moving the coordinate system mentioned above translate and fill color fill have recording effects and may affect the next drawing, canvas provides save and restore methods to isolate the possible effects of each drawing.
Save () // Translate or draw restore()Copy the code
The emoji Case
Round face contour
Function face(CTX, options) {const {x, y, r} = this.options ctx.save() ctx.translate(x, y) ctx.arc(0,0, r, 0, Math.PI * 2); ctx.strokeStyle = this.options.color; const radialGradient = ctx.createRadialGradient( -1 * r / 3, - 1 * r / 3, 4 * r / 5 , -1 * r / 3, -1 * r / 3 , R) radialGradient. AddColorStop (0, 'RGB (255255, 0)) radialGradient. AddColorStop (1, 'RGB (255,215,0)') ctx.fillstyle = radialGradient ctx.fill() ctx.stroke(); ctx.restore(); } face(ctx, { x: 100, y: 100, r: 50 })Copy the code
eyes
The realization of the eyes is divided into two steps, first draw the whites of the eyes, and use the quadratic Bezier curve to realize the curve of the corners. The common Canvas2D library also uses this technique to realize the graph with rounded corners. Finally, a black dot in the center completes the eye
function eye(ctx, options) { const radius = options.radius || 4 const width = options.width || 20 const height = options.height || 12 const x = options.x || 0 const y = options.y || 0 ctx.save() ctx.translate(this.options.x ,this.options.y) ctx.strokeStyle = this.options.color ctx.fillStyle = '#fff' ctx.beginPath(); ctx.moveTo(0 + radius, 0) ctx.lineTo(width - radius, 0) ctx.quadraticCurveTo(width ,0, width, 0 + radius) ctx.lineTo(width, height -radius) ctx.quadraticCurveTo(width, height, width - radius, height) ctx.lineTo(0 + radius, height) ctx.quadraticCurveTo(0, height, 0, height -radius) ctx.lineTo(0, 0 + radius) ctx.quadraticCurveTo(0, 0, 0 + radius, 0) ctx.closePath() ctx.fill() ctx.stroke(); ctx.restore() ctx.save() ctx.fillStyle = 'black' ctx.translate(x, y) ctx.beginPath() ctx.arc(width / 2, height / 2, 3, PI * 2) ctx.stroke() ctx.fill() ctx.restore()} eye(CTX, {// left eye x: 50, y: 80}) eye(CTX, {// right eye x: 90, y: 80})Copy the code
The mouth
function mouth(ctx, options) {
const width = options.size || 40
const height = options.height || 10
const x = options.x || 40
const y = options.y || 10
ctx.save()
ctx.translate(x, y)
ctx.beginPath()
ctx.moveTo(0, 0)
ctx.quadraticCurveTo(width / 2, height, size, 0)
ctx.stroke()
ctx.restore()
}
mouth(ctx, {
x: 60,
y: 118
})
Copy the code
tears
function tear(ctx, options) { const width = options.width || 6 const height = options.height || 20 const radius = options.radius || 4 const x = options.x || 0 const y = options.y || 0 ctx.save() const gradient = ctx.createLinearGradient(0, 0 , width, Height) gradient. AddColorStop (0, 'rgba (0191255, 1)) gradient. AddColorStop (1, 'rgba(135,206,250,1)') ctx.fillstyle = gradient ctx.translate(this.options.x, this.options.y) ctx.beginPath() ctx.moveTo(0, 0) ctx.lineTo(width, 0) ctx.lineTo(width, height - radius) ctx.quadraticCurveTo(width, height, width - radius, height) ctx.lineTo(0 + radius, height) ctx.quadraticCurveTo(0, height, 0, height -radius) ctx.lineTo(0, 0) CTX. ClosePath (CTX). The fill (CTX), stroke (CTX). The restore ()} eye (CTX, {/ / left tears x: 90, y: 80}) eye (CTX, {/ / right tears x: 90, y: 80 })Copy the code
At this point, a simple expression is achieved.
Simplicity – Abstract the code
In common canvas libraries such as pixi.js and create.js, graphics usually have a base class of display, which is used to control functions such as display and hiding, zooming and moving, and graphic events. Each graph has the draw method, which renders each graph in turn in the main class. Here’s a brief abstraction of the business code above.
Display
// Write some pseudo-code, Class Display {options = {} constructor(options) {object.assign (this.options,) Show (isShow) {this.options.show = isShow} // Graphical event on(eventType, callBack) {}}Copy the code
Face the class
class Face extends Display { constructor(options) { super(options) } draw() { const { x, y, r } = this.options ctx.save() ctx.translate(this.options.x ,this.options.y) ctx.arc(x, y, r, 0, Math.PI * 2); ctx.strokeStyle = this.options.color; const radialGradient = ctx.createRadialGradient(x -1 * r / 3, y - 1 * r / 3, 40, x -1 * r / 3, y -1 * r / 3 , R) radialGradient. AddColorStop (0, 'RGB (255255, 0)) radialGradient. AddColorStop (1, 'RGB (255,215,0)') ctx.fillstyle = radialGradient ctx.fill() ctx.stroke(); ctx.restore(); }}Copy the code
Eye class
class Eye extends Display {
constructor(options) {
super(options)
}
draw() {
const radius = 4
const width = 20
const height = 12
ctx.save()
ctx.translate(this.options.x ,this.options.y)
ctx.strokeStyle = this.options.color
ctx.fillStyle = '#fff'
ctx.beginPath();
ctx.moveTo(0 + radius, 0)
ctx.lineTo(width - radius, 0)
ctx.quadraticCurveTo(width ,0, width, 0 + radius)
ctx.lineTo(width, height -radius)
ctx.quadraticCurveTo(width, height, width - radius, height)
ctx.lineTo(0 + radius, height)
ctx.quadraticCurveTo(0, height, 0, height -radius)
ctx.lineTo(0, 0 + radius)
ctx.quadraticCurveTo(0, 0, 0 + radius, 0)
ctx.closePath()
ctx.fill()
ctx.stroke();
ctx.restore()
ctx.save()
ctx.fillStyle = 'black'
ctx.translate(this.options.x ,this.options.y)
ctx.beginPath()
ctx.arc(width / 2, height / 2, 3, 0, Math.PI * 2)
ctx.stroke()
ctx.fill()
ctx.restore()
}
}
Copy the code
Mouth
class Mouth extends Display {
constructor(options) {
super(options)
}
draw() {
const size = 40
const height = 10
ctx.save()
ctx.translate(this.options.x, this.options.y)
ctx.beginPath()
ctx.moveTo(0, 0)
ctx.quadraticCurveTo(size / 2, height, size, 0)
ctx.stroke()
ctx.restore()
}
}
Copy the code
Tear class
class Tear extends Display{ constructor(options) { super(options) } draw() { const { width , height, radius } = this.options ctx.save() const gradient = ctx.createLinearGradient(0, 0 , width, Height) gradient. AddColorStop (0, 'rgba (0191255, 1)) gradient. AddColorStop (1, 'rgba(135,206,250,1)') ctx.fillstyle = gradient ctx.translate(this.options.x, this.options.y) ctx.beginPath() ctx.moveTo(0, 0) ctx.lineTo(width, 0) ctx.lineTo(width, height - radius) ctx.quadraticCurveTo(width, height, width - radius, height) ctx.lineTo(0 + radius, height) ctx.quadraticCurveTo(0, height, 0, height -radius) ctx.lineTo(0, 0) ctx.closePath() ctx.fill() ctx.stroke() ctx.restore() } }Copy the code
Main render code
Let face = new face ({x: 100, y: 100, r: 50, color: 'rgba(0,0,0,0.2)'}) const eye1 = new Eye({x: 50, y: 80, color: new Eye) 'rgba(0,0,0,1)'}) const eye2 = new Eye({x: 90, y: 80, color: 'rgba(0,0,0,1)'}) const tearDefault = {width: 6, height: 20, radius: 4 } const tear1 = new Tear({ x: 57, y: 92, ... tearDefault }) const tear2 = new Tear({ x: 98, y: 92, ... tearDefault }) const mouth = new Mouse({ x: 60, y: 118, color: 'rgba(0,0,0,1)'}) const graphics.push(face) graphics.push(eye1) graphics.push(eye2) graphics.push(mouth) graphics.push(tear1) graphics.push(tear2) function render() { graphics.forEach(graphic => { graphic.draw() }) } render()Copy the code
The last
This issue shares a simple canvas case, want to control the length, without adding animation, here to add, with tween.js can easily achieve the above expression tears effect oh, interested friends can practice by themselves. The code address
Next update learn pixi.js from League of Legends