An eight thousand word super detailed Canvas from zero to one starter guide
Introduction of Canvas
Provides a way to draw graphics using JavaScript and HTML
Note: all of my code examples are based on TS + Webpack environment, there are friends who do not understand can gotsAnd [webpack 】 (Webpack.docschina.org/)
- Compatibility: IE9+, can be compatible with IE7, IE8 through this plug-in
1. Canvas draws geometric graphics
1.1 Steps to create a Canvas element
- Create canvas elements with a default width of 300 and height of 150
- Gets the context object context
- I’m going to draw my axes with the origin in the upper left corner of the screen
const canvas = document.querySelector<HTMLCanvasElement>("#canvas") | |new HTMLCanvasElement();
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
Copy the code
Note that when setting the canvas width and height here, do not use style to set it, because after setting this, we will get the default canvas width and height, so we need to set the width and height directly on the canvas
<canvas id="canvas" width="500" height="300" />
Copy the code
1.2 Drawing straight lines
1.2.1 a straight line
- Ctx.moveto (x, y): Moves the brush to the (x, y) position
- Ctx. lineTo(x1, y1): Let the brush draw a line from (x, y) to (x1, y1)
- Ctx.stroke () : Start drawing
const canvas = document.querySelector<HTMLCanvasElement>("#canvas") | |new HTMLCanvasElement();
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
ctx.moveTo(0.0)
ctx.lineTo(300.300)
ctx.stroke()
Copy the code
Draw straight lines
ctx.moveTo(200.100)
ctx.lineTo(400.100)
ctx.lineTo(400.50)
ctx.moveTo(200.300)
ctx.lineTo(400.300)
ctx.stroke()
Copy the code
If there is no moveTo, the drawing will continue from the previous lineTo position. If there is, the brush will moveTo the moveTo position to draw the lineTo
1.2.2 rectangle
- Ctx.strokerect (x, y, width, height) : Draw a hollow rectangle with width length and height starting from the position (x, y)
- Ctx. strokeStyle = “”; Modify the color of the edge
ctx.strokeStyle = "red";
ctx.strokeRect(100.100.200.100);
Copy the code
The order of strokeStyle and strokeRect cannot be reversed
- Ctx.fillrect (x, y, width, height) : Draw a solid rectangle with width length and height starting at (x, y) position
- FillStyle = “”; Modify the color of the fill
ctx.fillStyle = "purple"
ctx.fillRect(100.100.200.100);
Copy the code
The order of fillStyle and fillRect cannot be reversed
- Rect (x, y, width, height) : Start at (x, y) and draw a rectangle with width and height. Unlike the above two rectangles, the rectangle drawn by this method is not immediately displayed. You need to call either fill() or stroke() to display the stroke and fill colors
ctx.strokeStyle = "blue";
ctx.fillStyle = "pink"
ctx.rect(100.100.200.100)
ctx.fill();
ctx.stroke();
Copy the code
- Ctx.clearrect (x, y, width, height) : Clears rectangles from position (x, y) width length, height height
- This clears only the fill color that is cleared, not the stroke color
ctx.strokeStyle = "blue";
ctx.fillStyle = "pink"
ctx.rect(100.100.200.100)
ctx.fill();
ctx.stroke();
ctx.clearRect(120.120.160.60)
Copy the code
1.2.3 polygon
-
Polygons are implemented via lineTo and moveTo
-
triangle
ctx.moveTo(200.100)
ctx.lineTo(200.300)
ctx.lineTo(400.200)
ctx.lineTo(200.100)
ctx.stroke()
Copy the code
You can use your imagination and try to draw a hexagon, a pentagon, etc
1.3 Curve Figure
- Ctx. arc(x, y, radius, startAngle,endAngle, anticlockwise) : center position is (x, y), radius is radius, startAngle startAngle,endAngle endAngle, draw an arc
- Anticlockwise: true anticlockwise, false anticlockwise
- Angle: Degree x math.pi /180
1.3.1 round
- The hollow circle
ctx.arc(250.250.100.0 ,360 * Math.PI/180);
ctx.stroke();
Copy the code
- Solid round
ctx.arc(250.250.100.0 ,360 * Math.PI/180);
ctx.fill()
Copy the code
- Concentric circles
You can try to draw concentric circles, I’m sure you’ll draw them like this:
ctx.arc(250.250.100.0 ,360 * Math.PI/180);
ctx.stroke();
ctx.arc(250.250.50.0 ,360 * Math.PI/180);
ctx.stroke();
Copy the code
If YOU draw it, you’ll see that the two circles are connected:
- Ctx.beginpath () : Starts a new path
- Ctx.closepath () : terminates a path
- These two methods usually come in pairs
Modify code:
// Start a path
ctx.beginPath()
ctx.arc(250.250.100.0 ,360 * Math.PI/180);
ctx.stroke();
// Pick up the pen
ctx.closePath()
// Start a new path
ctx.beginPath()
ctx.arc(250.250.50.0 ,360 * Math.PI/180);
ctx.stroke();
ctx.closePath()
Copy the code
Note: The stroke() and fill() methods only apply to the current path and are invalid when closed!
1.3.1 arc
- When drawing arcs, you do not need to close the ctx.closepath () path
ctx.beginPath()
ctx.arc(250.250.100.0 ,60 * Math.PI/180);
ctx.stroke();
Copy the code
Shut down the path
There is another way to draw an arc:
- CTX. ArcTo (cx,cy,x2,y2,radius) :(cx,cy) is the control point and (x2,y2) is the end point of the arc. The reason why there is no starting point is that when you draw an arc this way, the general length of the arc starts with lineTo() or moveTo()
Example:
ctx.moveTo(50.100);
ctx.lineTo(200.100); // Draw a horizontal line, (200,100) corresponds to the beginning of the arc
ctx.arcTo(250.100.250.200.50) // Control point (250, 100), arc end point (250, 200), radius 50
ctx.lineTo(250.250);
ctx.stroke();
Copy the code
It’s a little bit hard to understand, but you can draw it yourself.
1.4 Bezier curve
Bezier curve, also known as Bezier curve or Bezier curve, is a mathematical curve used in two-dimensional graphics applications. General vector graphics software uses it to accurately draw curves. A Bates curve consists of line segments and nodes, which are draggable fulcrum points, and line segments that are like stretchable rubber bands. The pen tool we see on the drawing tool is used to do this kind of vector. A Bezier curve is a curve controlled by one or more points.
1.4.1 Quadratic Bessel curve
- CTX. QuadraticCurveTo (Cx, cy, x2, y2) : Similar to arcTo, Bessel curves are also composed of three coordinates: (x1, y1) as the starting point provided by moveTo or lineTo, (Cx, cy) as the control point, and (x2, y2) as the ending point.
Note: Quadratic Bezier curves have only one control point
ctx.moveTo(100.200)
ctx.quadraticCurveTo(150.200.200.300)
ctx.stroke()
Copy the code
Draw a chat bubble
ctx.moveTo(75.25);
ctx.quadraticCurveTo(25.25.25.62);
ctx.quadraticCurveTo(25.100.50.100);
ctx.quadraticCurveTo(50.120.30.125);
ctx.quadraticCurveTo(60.120.65.100);
ctx.quadraticCurveTo(125.100.125.62);
ctx.quadraticCurveTo(125.25.75.25);
ctx.stroke();
Copy the code
1.4.2 Cubic Bezier curves
- CTX. BezierCurveTo (CX1, CY1, CX2, CY2, X, Y) : A cubic Bezier curve is composed of two control points and end coordinates (x, y). The same starting coordinates are also provided by lineTo and moveTo.
ctx.moveTo(200.100);
ctx.bezierCurveTo(150.50.250.200.200.200);
ctx.stroke();
Copy the code
1.4.3 Quartic Bezier curves
Here is:
2. Canvas line style
lineWidth
- ctx.lineWidth
ctx.beginPath()
ctx.lineWidth = 2
ctx.moveTo(100.100)
ctx.lineTo(300.100);
ctx.stroke();
ctx.closePath()
ctx.beginPath()
ctx.lineWidth = 10
ctx.moveTo(300.100);
ctx.lineTo(300.200);
ctx.stroke();
ctx.closePath()
Copy the code
2.2 lineCap line style at the beginning and end
- ctx.lineCap
- Lines begin and end, not links
- Ctx. lineCap = ‘butt’ Default value for wireless caps
- Ctx. lineCap = ’round’
- Ctx. lineCap = ‘square
Note: Setting a style other than the default here will make the line segment longer by adding a cap to the end of the line segment. The extra length is half the width of the line segment
The style of the lineJoin
-
ctx.lineJoin
-
It’s where the lines join, not where the lines start and end
-
Ctx. lineJoin = ‘miter’ default, sharp corners
- Ctx. lineJoin = ‘bevel’ bevel
- Ctx. lineJoin = ’round’ rounded corners
Conclusion:
2.4 setLineDash Sets line false and solid styles
- Ctx.setlinedash ([solid line width, distance])
ctx.beginPath()
ctx.setLineDash([2.2]);
ctx.moveTo(100.100)
ctx.lineTo(300.100);
ctx.stroke();
ctx.closePath()
Copy the code
3. Text operations
3.1 Text operation method
- Draw the fillText ctx.filltext (text, x, y, maxWidth)
Exceeding maxWidth is compressed to maxWidth length
ctx.fillText("Ha ha".100.100)
Copy the code
- Draw strokeText ctx.strokeText(text, x, y, maxWidth)
ctx.strokeText("Ha ha".100.100)
Copy the code
- Get text length measureText
Get the UI length of the text in px, not the word length of the text
const l = ctx.measureText("Ha ha").width;
console.log(l); / / 20
Copy the code
3.2 Text Attributes
- ctx.font = “font-style font-weight font-size/line-height font-family”
ctx.font = "bold 100px Kai"
ctx.strokeText("Ha ha".100.100)
Copy the code
- Text alignment cxt.textalign
ctx.font = "bold 30px 宋体"
ctx.moveTo(100.100)
ctx.lineTo(100.500)
ctx.stroke()
ctx.textAlign = 'start'
ctx.strokeText("Ha ha".100.100)
ctx.textAlign = 'end'
ctx.strokeText("Ha ha".100.150)
ctx.textAlign = 'center'
ctx.strokeText("Ha ha".100.200)
ctx.textAlign = 'left'
ctx.strokeText("Ha ha".100.250)
ctx.textAlign = 'right'
ctx.strokeText("Ha ha".100.300)
Copy the code
- Text alignment textBaseline
Font is still the most used text manipulation property, and alignment is rarely used
4. Graphical operation
4.1 Basic picture operations
Canvas provides image import through drawImage() method to perform a series of cropping, tiling and other processing on images.
4.1.1 Picture drawing
- Ctx. drawImage(image, dx, dy) : image is the image element, and (dx, dy) is the coordinate to draw the upper left corner of the image.
const img = new Image();
img.src = '/src/img/aaa.png'
img.onload = () = > {
ctx.drawImage(img, 100.100);
}
Copy the code
Note: If the image object is created by new, it needs to be done after onLoad
- Ctx. drawImage(image, dx, dy, dw, dh) : dw and dh are the width and height of the image to draw.
const img = new Image();
img.src = '/src/img/aaa.png'
img.onload = () = > {
ctx.drawImage(img, 100.100.50.50);
}
Copy the code
- DrawImage (image, sx, sy, sw, sh, dx, dy, dw, dh) :(sx, sy)
Note: here sx, sy, sw, sh are for the source image, dx, dy, dw, dh are for the drawn image on the canvas
const img = new Image();
img.src = '/src/img/aaa.png'
img.onload = () = > {
ctx.drawImage(img, 50.50.50.50.0.0.100.100)}Copy the code
The Sprite effect can be achieved by loading the image once and showing different image elements by cropping them in different positions.
4.1.2 Tiling effect
- ctx.createPattern(element, type)
- Type The following types are available:
- Repeat: x, y axis fully tiled
- Repeat -x: X-axis tile
- Repeat -y: y tile
- No-repeat: not tiled
Tiling is usually used with rect and Arc circles. Element can tile images, videos, and even canvas elements.
- Tile image
const img = new Image();
img.src = '/src/img/aaa.png'
img.onload = () = > {
ctx.rect(0.0.300.300)
const pattern = ctx.createPattern(img, 'repeat') as CanvasPattern;
ctx.fillStyle = pattern;
ctx.fill();
}
Copy the code
- Tile canvas
// Create a new canvas
const canvas2 = document.createElement('canvas')
canvas2.width = 50;
canvas2.height = 50;
const ctx2 = canvas2.getContext('2d') as CanvasRenderingContext2D;
ctx2.fillStyle = 'blue'
ctx2.arc(25.25.25.0.360 * Math.PI/180);
ctx2.fill()
ctx.rect(0.0.300.300)
const pattern = ctx.createPattern(canvas2, 'repeat-x') as CanvasPattern;
ctx.fillStyle = pattern;
ctx.fill();
Copy the code
Fill text with pictures
// Fill the image with text
const pattern = ctx.createPattern(img, 'repeat') as CanvasPattern
ctx.font = 'bold 100px Kai'
ctx.fillStyle = pattern;
ctx.fillText('CANVAS'.80.100);
Copy the code
4.1.3 Clipping function
The cropping function requires three steps:
- Create a cropping template, that is, crop an image into what shape
- Call clipping method: cxt.clip()
- Cut the picture into the shape of the first step
// 1. Create a reduction template
ctx.arc(50.50.50.0.360 * Math.PI/180);
/ / 2. Tailoring
ctx.clip()
// 3. Draw cropped image
ctx.drawImage(img, 0.0);
Copy the code
4.2 Deformation Operation
2 translation
- Ctx.translate (x, y) : Translate the graph by x, y distance
Note: after a translation, the graph before the translation is not cleared
ctx.beginPath()
ctx.fillRect(0.0.100.100)
setTimeout(() = > {
ctx.translate(200.200);
ctx.fillRect(0.0.100.100)},1000)
ctx.closePath()
Copy the code
4.2.2 zoom
- Ctx.scale (x, y) : Scale the graph along the x and y axes
Note: After scaling, the xy coordinate of the graph is also scaled, and the width of the line is also scaled
ctx.fillRect(200.200.100.100)
ctx.scale(1.5.1.5)
ctx.fillStyle = 'rgba (0, 0, 0, 0.3)'
ctx.fillRect(200.200.100.100)
ctx.closePath()
Copy the code
We can scale x and y to the left using Translate
ctx.fillRect(200.200.100.100)
ctx.translate(-100, -100)
ctx.scale(1.5.1.5)
ctx.fillStyle = 'rgba (0, 0, 0, 0.3)'
ctx.fillRect(200.200.100.100)
ctx.closePath()
Copy the code
Holdings rotation
- Ctx. rotate(deg) : Rotates a graph by deg degrees
Note: The center of the rotation is the origin of the coordinates
ctx.fillRect(200.200.50.50)
ctx.rotate(10 * Math.PI/180)
ctx.fillRect(200.200.50.50)
Copy the code
- The center of rotation can be changed using Translate
ctx.fillRect(200.200.50.50)
ctx.translate(225, -95)
ctx.rotate(45 * Math.PI/180)
ctx.fillRect(200.200.50.50)
Copy the code
4.2.4 One-step writing method
- ctx.transform(a,b,c,d,e,f)
- A Horizontal scaling
- B Horizontal inclination
- C Vertical inclination
- D Vertical scaling
- E horizontal movement
- F Vertical movement
Ctx.transform (1,0,0,1,-100,-100) translate(-100, -100)
4.3 Pixel Operation
- GetImageData (x, y, W, h) gets image pixel data
- PutImageData () redraws the pixel data onto the canvas
Note that the image must be drawn on the canvas before getImageData()
- GetImageData (x, y, w, h) : Get the pixel data of canvas starting from (x, y), width W, height H
const img = new Image()
img.src = '/src/img/aaa.png'
img.onload = () = > {
ctx.drawImage(img, 0.0);
console.log(ctx.getImageData(0.0.500.500));
}
Copy the code
In which the data is the data from pixel data, data inside the data corresponding to r, g, b, a four values, once every four cycle: [R2, R1, G1, B1 and A1 G2, B2, A2,… , and RGBA are array structures, respectively
- PutImageData (pixiList, x, y) : Draws pixel data in (x, y) coordinates
const img = new Image()
img.src = '/src/img/aaa.png'
img.onload = () = > {
ctx.drawImage(img, 0.0);
const imageData = ctx.getImageData(0.0.500.500)
ctx.putImageData(imageData, 100.100)}Copy the code
We can process the data and output it to the canvas again
Note: if you don’t want your browser to get stuck, it’s best not to print data in a for loop!! 😒
4.3.1 Color Flipping (Positive and negative effects)
- Color flip is to invert R, G, and B, namely: 255-R, 255-g, and 255-b
for (let i = 0; i < imageData.data.length; i+=4) {
// Iterate over each pixel
imageData.data[i] = 255 - imageData.data[i]
imageData.data[i+1] = 255 - imageData.data[i+1]
imageData.data[i+2] = 255 - imageData.data[i+2]
}
ctx.clearRect(0.0, canvas.width, canvas.height)
ctx.putImageData(imageData, 0.0)
Copy the code
4.3.2 Black-and-white effect
- Is to take the average of the RGB three colors, and then set this average for all pixels
for (let i = 0; i < imageData.data.length; i+=4) {
// Iterate over each pixel
const avg = (imageData.data[i] + imageData.data[i+1] + imageData.data[i+2) /3
imageData.data[i] = avg
imageData.data[i+1] = avg
imageData.data[i+2] = avg
}
Copy the code
It’s better to use weighted averages
const avg = imageData.data[i] * 0.3 + imageData.data[i] * 0.4 + imageData.data[i] * 0.3
Copy the code
Take weighted average value of RGB respectively to achieve retro effect
imageData.data[i] = imageData.data[i] * 0.39 + imageData.data[i + 1] * 0.76 + imageData.data[i+2] * 0.18
imageData.data[i+1] = imageData.data[i] * 0.35 + imageData.data[i + 1] * 0.68 + imageData.data[i+2] * 0.16
imageData.data[i+2] = imageData.data[i] * 0.27 + imageData.data[i + 1] * 0.53 + imageData.data[i+2] * 0.13
Copy the code
4.3.3 Adjust light and Dark
- Adding a positive number to each RGB value brightens it, and adding a negative number darkens it
for (let i = 0; i < imageData.data.length; i+=4) {
const l = 80 / / - 80
imageData.data[i] += l
imageData.data[i+1] += l
imageData.data[i+2] += l
}
Copy the code
4.3.4 Red, green and blue masks
- R takes the average value of RGB, GB is 0, and the same applies to other masks
for (let i = 0; i < imageData.data.length; i+=4) {
const avg = (imageData.data[i] + imageData.data[i+1] + imageData.data[i+2) /3
imageData.data[i] = avg
imageData.data[i+1] = 0
imageData.data[i+2] = 0
}
Copy the code
4.3.5 Modify transparency
- The opacity can be modified by multiplying the A value by A decimal
for (let i = 0; i < imageData.data.length; i+=4) {
imageData.data[i+3] = imageData.data[i+3] * 0.5
}
Copy the code
You can think about how to convert a picture into pixel wind, here is an idea for you, you can first reduce the picture, and then enlarge, the specific threshold, you can try.
4.3.6 Creating pixel regions
- CreateImageData (w, h) creates an area of pixels w wide and h high
- CreateImageData (imageData) creates a pixel manipulation area the same width and height as imageData
Perform pixel manipulation on a region
const imageData2 = ctx.createImageData(300.300)
for (let i = 0; i < 300 * 300 * 4; i+=4) {
imageData2.data[i] = 100
imageData2.data[i+1] = 100
imageData2.data[i+2] = 188
imageData2.data[i+3] = 255
}
Copy the code
Gradients and Shadows
5.1 Linear Gradient
- Ctx.createlineargradient (x1, y1, x2, y2) : Create a gradient object that starts with (x1, y1) and ends with (x2, y2)
- addColorStop(value, color); Add more than one color to the gradient object created above. Value is the offset of the gradient position (0-1) and color is the gradient color
ctx.rect(0.0, canvas.width, canvas.height)
const gnt = ctx.createLinearGradient(0.0, canvas.width, 0);
gnt.addColorStop(0.1.'red');
gnt.addColorStop(0.2.'green');
gnt.addColorStop(0.3.'blue');
gnt.addColorStop(0.4.'purple');
ctx.fillStyle = gnt
ctx.fill()
Copy the code
- Text gradient
ctx.font = 'bold 50px Kai'
const gnt = ctx.createLinearGradient(0.0, canvas.width, 0);
gnt.addColorStop(0.'purple');
gnt.addColorStop(1.'#fff');
ctx.fillStyle = gnt
ctx.fillText("Canvas".100.100);
Copy the code
5.2 Radial Gradient
- CreateRadialGradient (x1,y1,r1,x2,y2,r2) :(x1,y1),r1 is the center coordinate and radius of the gradient at the beginning, (x2,y2),r2 is the center coordinate and radius of the gradient at the end
const gnt = ctx.createRadialGradient(250.250.100.300.300.50);
gnt.addColorStop(0.'red')
gnt.addColorStop(0.5.'blue')
gnt.addColorStop(1.'yellow')
ctx.fillStyle = gnt;
ctx.arc(250.250.100.0.2*Math.PI);
ctx.fill()
Copy the code
5.3 the shadow
- ShadowOffsetX: right offset of shadow
- ShadowOffsetY: Downward offset of shadow
- ShadowColor: shadowColor
- ShadowBlur: the degree of shadowBlur. The greater the value, the more blurred it is
ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.shadowColor = 'red'
ctx.shadowBlur = 5
ctx.fillRect(100.100.100.100)
Copy the code
- Set a shadow for the text
ctx.font = 'bold 50px Kai'
ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.shadowColor = 'red'
ctx.shadowBlur = 5
ctx.fillText("Canvas".100.100);
Copy the code
- Set a shadow on the image
ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.shadowColor = 'red'
ctx.shadowBlur = 5
const img = new Image();
img.src = '/src/img/aaa.png'
img.onload = () = > {
ctx.drawImage(img, 100.100.100.100)}Copy the code
Note: The code to set the shadow should be written before the drawing code
6 canvas path
In Canvas, all graphs except rectangle are drawn based on path
6.1 Start Path and Close Path
- BeginPath () and closePath ()
6.1.1 Start Path
Take a look at the following example:
ctx.moveTo(100.100)
ctx.lineTo(300.100)
ctx.strokeStyle = "red"
ctx.stroke()
ctx.moveTo(100.200)
ctx.lineTo(300.200)
ctx.strokeStyle = "blue"
ctx.stroke()
Copy the code
Code we draw two straight lines, the above is red, the following is blue, but we found that the actual picture, purple is above, the following is blue, because the second picture of blue, he set up the blue style will affect the style of the first set of red, is directly to cover above, so the red + blue, the first line becomes purple.
Canvas is drawn according to states. Each drawing will detect all states such as: StrokeStyle, fillStyle, etc., if we just set it once, then the whole program will always use that state, and if we use beginPath() every time, a new path, then they will use their own state.
Change the above code to this:
ctx.moveTo(100.100)
ctx.lineTo(300.100)
ctx.strokeStyle = "red"
ctx.stroke()
ctx.beginPath()
ctx.moveTo(100.200)
ctx.lineTo(300.200)
ctx.strokeStyle = "blue"
ctx.stroke()
Copy the code
Add beginPath so that both lines show the correct color.
Note: The criterion for determining whether a path is the same is whether the beginPath() method is used, not whether there is a visual beginPath
Using the following methods only draws graphics and does not start new paths: moveTo(), lineTo(), strokeRect(), fillRect(), rect(), Arc (), arcTo(), quadricCurveTo() and bezierCurveTo()
6.1.2 Closing a Path
Here are a few concepts:
- Close path: Links the start and end points of a path to form a closed graph
- End path: means to start a new path, so to end a path, only beginPath() is used to start a new path.
Look at this example:
ctx.arc(250.250.100.0.Math.PI)
ctx.strokeStyle = 'red'
ctx.stroke();
Copy the code
Currently there is only one path, but we have not closed the path, so the arc is not sealed.Add the closePath ()
ctx.arc(250.250.50.0.Math.PI)
ctx.strokeStyle = 'red'
ctx.closePath()
ctx.stroke();
Copy the code
And then we see that the arc is closed
Note: Close path is written before stroke
Let’s add another blue arc:
ctx.arc(250.250.50.0.Math.PI)
ctx.strokeStyle = 'red'
ctx.closePath()
ctx.stroke();
ctx.arc(150.250.50.0.Math.PI)
ctx.strokeStyle = 'blue'
ctx.closePath()
ctx.stroke();
Copy the code
We can see that the second arc is connected to the first one, and the first red arc has changed color. This is because clothPath is a closed path. It only connects the beginning and end of the graph. And the beginning of the second arc is also the end of the first arc, so they are joined together, to separate and display the correct color, we just add beginPath:
ctx.arc(250.250.50.0.Math.PI)
ctx.strokeStyle = 'red'
ctx.closePath()
ctx.stroke();
ctx.beginPath()
ctx.arc(150.250.50.0.Math.PI)
ctx.strokeStyle = 'blue'
ctx.closePath()
ctx.stroke();
Copy the code
Remember not to confuse closing paths with ending paths!!
6.2 Checking whether a point is in the current path
- isPointInPath(x, y)
ctx.arc(250.250.50.0.Math.PI)
ctx.closePath()
ctx.stroke()
console.log(ctx.isPointInPath(250.250)); // true
Copy the code
Note: the isPointInPath method does not support fillRect and strokeRect. If you want to use a rectangle, you can use rect() instead
7. State of canvas
Canvas is drawing based on state. If we do not use beginPath to start a new path, then this state is used globally. We can store and restore state through two methods provided by canvas.
7.1 clip () method
Before using the clip() method, draw a base shape. Then call clip(). This base shape becomes the clipping region
Note: fillRect and strokeRect methods cannot be used as clipping regions, rect can be used instead!!
// 1. Draw the basic graph
ctx.rect(100.100.100.100)
// 2. Crop the rectangle as the clipping area
ctx.clip()
// 3. Draw an image outside the rectangle (fill color also works)
const img = new Image()
img.src = '/src/img/man.jpg'
img.onload = () = > {
ctx.drawImage(img, 0.0, canvas.width, canvas.height)
}
Copy the code
7.2 the save () and restore ()
Think about it, we cut the picture above, if we do not need to cut the picture to draw, this time will also be cut, how to show the picture behind? We just need to save the state before the clipping, and then start a new path to do the clipping operation, we do not need to clipping, we just need to restore the state before the clipping can continue to do the following things.
The current state can be saved by calling the save method, and the saved state can be restored using the Restore method
// 1. Save the unclipped environment
ctx.save();
// 2. Start clipping
ctx.beginPath();
ctx.arc(250.250.100.0.Math.PI * 2);
ctx.stroke();
ctx.clip();
const img = new Image();
img.src = "/src/img/man.jpg";
img.onload = () = > {
ctx.drawImage(img, 0.0, canvas.width, canvas.height);
fn()
};
function fn() {
// 3. After the clipping is complete, restore the previous state and add the graph that does not need clipping
ctx.restore();
const img2 = new Image();
img2.src = "/src/img/aaa.png";
img2.onload = () = > {
ctx.drawImage(img2, 100.250.50.50);
};
}
Copy the code
The process goes something like this:
You can use beginPath to create two different colored lines. You can also use save and restore to create two different colored lines.
8. Canvas some other methods
8.1 toDataURL ()
Save canvas canvas as an image and return base64, which can be downloaded by browser using window.location = ctx.todataURL (“image/ PNG “).
8.2 globalAlpha property
Change global transparency, range 0-1, to write first, for global effect.
8.3 globalCompositeOperation properties
Normally, when drawing a graph on canvas, the back overwrites the front. You can use this attribute to set the overwriting method. There are several attributes below, and you can try the effect by yourself:
9. Event operations
9.1 Encapsulating a Mouse
window.tools = {};
window.tools.getMouse = function (element) {
var mouse = { x: 0.y: 0 };
element.addEventListener("mousemove".function (e) {
var x, y;
var e = e || window.event;
if (e.pageX || e.pageY) {
x = e.pageX;
y = e.pageY;
} else {
x =
e.clientX +
document.body.scrollLeft +
document.documentElement.scrollLeft;
y =
e.clientY +
document.body.scrollTop +
document.documentElement.scrollTop;
}
x -= element.offsetLeft;
y -= element.offsetTop;
mouse.x = x;
mouse.y = y;
});
return mouse;
};
Copy the code
Use:
// @ts-ignore
const mouse = window.tools.getMouse(canvas)
canvas.addEventListener('mousemove'.function(){
console.log(mouse.x, mouse.y);
})
Copy the code
9.2 Keyboard Events
window.addEventListener('keydown'.function(e){
console.log(e);
})
Copy the code
10. Physics, Mathematics and animation
10.1 animation requestAnimationFrame
We don’t want to use setInterval for animation, setInterval itself is not very good, so we use requestAnimationFrame instead, but it has compatibility, so we’ll write a tool method and use it later.
window.requestAnimationFrame =
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame ||
function (callback) {
return window.setTimeout(callback, 1000 / 60);
};
Copy the code
Usage:
let x = 0;
(function move(){
window.requestAnimationFrame(move)
ctx.clearRect(0.0, canvas.width, canvas.height)
ctx.beginPath()
ctx.arc(x, 250.50.0.2*Math.PI)
ctx.stroke()
x+=2}) ()Copy the code
10.2 Trigonometric Functions
10.2.1 Definition of trigonometric functions
First, let’s take a look at the six trigonometric functions we use:
- Sin (theta) : Math. Sin (theta * Math. PI / 180)
- Cos (theta) : Math. Cos (theta * Math. PI / 180)
- Tan (theta) : Math. Tan (theta * Math. PI / 180)
- arcsin(x/R):Math.asin(x/R)*(180/Math.PI)
- arccos(y/R):Math.acos(x/y)*(180/Math.PI)
- arctan(x/y):Math.atan(x/y)*(180/Math.PI)
The specific corresponding relationship is shown as follows:
Note that all the angles in JS are radians, i.e. Math.PI = 180 degrees, and you can convert the degree * math. PI/180 in this way
- Try writing an arrow that moves with the mouse
class Arrow {
x = 0;
y = 0;
ctx: CanvasRenderingContext2D;
constructor(x: number, y: number, ctx: CanvasRenderingContext2D) {
this.x = x;
this.y = y;
this.ctx = ctx;
this.drawArrow(0);
}
drawArrow(deg: number) {
this.ctx.save()
this.ctx.translate(this.x, this.y)
this.ctx.rotate(deg)
this.ctx.moveTo(0.0);
this.ctx.lineTo(-50, -10);
this.ctx.lineTo(20, -10);
this.ctx.lineTo(20, -30);
this.ctx.lineTo(50.0);
this.ctx.lineTo(20.30);
this.ctx.lineTo(20.10);
this.ctx.lineTo(-50.10);
this.ctx.lineTo(-50, -10);
this.ctx.fillStyle = 'black'
this.ctx.fill();
this.ctx.restore()
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, 2.0.2 * Math.PI);
this.ctx.fillStyle = 'red'
this.ctx.fill();
}
linePoint(x: number, y: number) {
this.ctx.beginPath();
this.ctx.setLineDash([2.2]);
this.ctx.moveTo(this.x, this.y);
this.ctx.lineTo(x, y);
this.ctx.stroke();
}
getDeg(x: number, y: number) {
const deg = Math.atan2(y - this.y, x - this.x) * (180 / Math.PI);
this.rotate(deg);
}
rotate(deg: number) {
this.drawArrow(deg * Math.PI / 180)}doAnimate(x: number, y: number) {
this.ctx.clearRect(0.0, canvas.width, canvas.height);
this.linePoint(x, y);
this.getDeg(x, y); }}let arrow = new Arrow(100.100, ctx);
// @ts-ignore
const mouse = window.tools.getMouse(canvas);
canvas.addEventListener("mousemove".function () {
arrow.doAnimate(mouse.x, mouse.y);
});
Copy the code
If you have rotation logic, it is best to draw the image from the top left corner of the screen and use the translate() method to move it to the desired position so that the rotation will not be incorrect
10.2.2 ellipse
The equation of the ellipse is :(x/a)² + (y/b)² = 1, that is, any point (x,y) on the ellipse satisfies this equation
According to the above figure, the coordinates of each point of the ellipse are:
- x = centerX + Math.cos(angle)*radiusX
- y = centerY + Math.sin(angle)*radiusY
class Ellips {
centerX = 0
centerY = 0
radiusX = 10
radiusY = 5
canvas: HTMLCanvasElement;
ctx: CanvasRenderingContext2D;
constructor(canvas: HTMLCanvasElement, cx:number, cy:number, rx:number, ry:number) {
this.centerX = cx;
this.centerY = cy;
this.radiusX = rx;
this.radiusY = ry;
this.canvas = canvas;
this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D
this.draw()
}
draw() {
this.ctx.beginPath()
this.ctx.moveTo(this.centerX + this.radiusX, this.centerY)
for (let deg = 1; deg < 360; deg++) {
const x = this.centerX + Math.cos(deg * Math.PI/180) *this.radiusX
const y = this.centerY + Math.sin(deg * Math.PI/180) *this.radiusY
this.ctx.lineTo(x, y)
}
this.ctx.closePath()
this.ctx.stroke()
}
}
new Ellips(canvas, 250.250.100.50)
Copy the code
10.2.3 Waveform motion
- If the x coordinate of a Canvas object is given as a sine function, it will swing from side to side
const speed = 0.1 / / speed
const swing = 5 // Swing amplitude
function XMove(deg:number) {
window.requestAnimationFrame(() = > {
ctx.beginPath()
ctx.clearRect(0.0, canvas.width, canvas.height)
ctx.translate(Math.sin(deg) * swing,0)
ctx.arc(100.250.30.0.2 * Math.PI)
ctx.stroke()
deg+=speed;
XMove(deg)
})
}
XMove(0)
Copy the code
- If the y coordinate of canvas object is given as sine function, and the x coordinate normally increases constant, the sine function trajectory will move
const speed = 0.1
const swing = 5
function XMove(deg:number) {
window.requestAnimationFrame(() = > {
ctx.beginPath()
ctx.clearRect(0.0, canvas.width, canvas.height)
ctx.translate(1.Math.sin(deg) * swing)
ctx.arc(30.250.30.0.2 * Math.PI)
ctx.fill()
deg+=speed;
XMove(deg)
})
}
XMove(0)
Copy the code
10.3 Accelerated motion
- Uniformly accelerated motion
let v = 0;
let a = 0.1;
(function XMove() {
window.requestAnimationFrame(() = > {
ctx.beginPath()
ctx.clearRect(0.0, canvas.width, canvas.height)
ctx.translate(v, 0)
ctx.arc(40.250.30.0.2 * Math.PI)
ctx.stroke()
v+=a;
XMove()
})
})()
Copy the code
You can think about accelerating uniformly and then going back.
Gravity is the acceleration in the y direction, and friction is the acceleration in the opposite direction, so you can multiply a by a decimal.
The basic content of Canvas is about this much, there is no problem to learn the ABOVE API to do some small effects, you can try to study the collision detection, competent children can try to write a simple physics engine.
All the code for the project is here
References:
Canvas TUTORIAL Canvas MDN written by Teacher Mo Zhenjie