Recently, the author has a requirement. The requirement content is: a group of text is displayed around the ring. The user can add text. The text surrounds the ring. Each word corresponds to a small blue dot around the ring. When the user mouses over The small blue dot above the ring will radiate the triangle and then display the text. Let’s take a look at the animation effect first!
As shown in the picture above, when the mouse is placed on the corresponding small blue dot, a triangle-like ray needs to be emitted, and the corresponding text is displayed outside the triangle, and the small blue dot becomes a small white dot.
When the user enters content above, the content is added around the ring below. As shown in the picture above.
The author's original idea was to use css to implement it, just like the dynamic secondary menu in the picture below.
However, considering that the content on the edge of the ring is variable and needs to be positioned around the ring, CSS may be difficult to implement. So wow, the author decided to use canvas to achieve it. (The author has only recently learned canvas. If there is anything wrong, I accept your corrections).
Implementation process:first:
The html part of the code is as follows:
<canvas style=margin-left: 50px;padding-top: 20px; display:block; id=canvas > The current version of your browser does not support canvas</canvas>
The specific implementation steps are as follows:
1. Draw a large circle.
Use the canvas method: context.arc(x, y, radius, startAngle, endAngle [, anticlockwise]);
x, y: circle center coordinates, radius: circle center radius, startAngle: drawing the starting radian, endAngle: drawing the end radian, [, anticlockwise]: optional parameter, whether to draw the arc clockwise or counterclockwise.
For the convenience of drawing, the author moved the origin of the canvas from the previous upper left corner to the center of the canvas.
The radius of the ring calculated by the author is r-80
canvas.width = 500canvas.height = 500//Calculate the radius of the center of the canvas let r = 500 / 2//Move the origin of the canvas to the center of the canvas when the interface is initialized ctx.translate(r,r) //Move the brush to round
The specific code is as follows:
//Canvas initialization let canvas = document.getElementById('canvas')let ctx= canvas.getContext('2d')let ratio = getPixelRato(ctx)canvas.width = 500canvas.height = 500//Calculate the radius of the center of the canvas let r = 500 / 2// When the interface is initialized, move the origin of the canvas to the center of the canvas ctx.translate(r,r) //Move the brush to the circle ctx.lineWidth = 3; //Set the line width of the brush ctx.beginPath(); //Start the brush//Draw the gradient edge color of the edge of the circle var arcColor = ctx.createLinearGradient(-170, -170, 0, 170)arcColor.addColorStop(0, '#8ec1ff')arcColor.addColorStop(0.2, '#83beff')arcColor.addColorStop(0.5, '#75b1ff')arcColor.addColorStop(0.7,'#5998ff')arcColor.addColorStop(1, '#2065ff')ctx .strokeStyle= arcColor;//Set the color of the brush ctx.arc(0,0,r - 80,0,2*Math.PI,false) //Draw a circle, coordinates 0,0, radius 250-80, full circle (0 -360 degrees), false means clockwise ctx.closePath()ctx.stroke() //DrawingThe drawing results are as follows
2. Draw the background image in the middle of the ring (the origin of the current canvas is the center of the canvas)
drawImage(image, dx, dy, dWidth, dHeight)
image: Canvas image resource, such as <img> image, SVG image, Canvas element itself, etc.
dx, dy: Plan an area on the Canvas canvas to place pictures. dx is the horizontal and vertical coordinates of the upper left corner of this area.
dWidth, dHeight: Plan an area on the Canvas canvas to place pictures, the width and height of this area.
The following coordinates are calculated by the author
let image = new Image()image.src = 'image/quan.png'image.onload = () => { // Move the origin to the center ctx.drawImage(image,-140,-140,280,280)}The drawing results are as follows:
3. Draw text and small dots on the ring (the origin of the current canvas is the center of the canvas)
Drawing targets for text and small dots:
3.1 Small dots are evenly displayed on the large ring
3.2 The text is scattered a little outside the small dots
Solution:1. The author uses an array to store the current words
let textArr = ['Broad sea and sky','Technical capabilities','Strong funds','Maintenance control','Live and work in peace and contentment','Watch the flowers','Finish the finishing touch','Get rid of the dross','Go against the wind',' career development']
2. Because the number of small dots and the number of words are the same, the number of both of them is the length of the array textArr above.
3. The radian of a full circle is 2π. To make the small dots equally divide the ring, the author first calculates the radian of the point where each small dot is located.
for(let i = 0;i<lengths;i++){ // Calculate radians let rad = 2*Math.PI/lengths*i}4. According to trigonometric functions, the coordinates (x, y) of the current small dot on the canvas can be calculated (the origin of the current canvas is the center of the canvas)
Among them, the relationship between radian, small dot, ring, ring radius and canvas origin, the author drew a picture to describe them.
Calculate the coordinates of text:
// Calculate the coordinates of the center of the small circle let x = (r - 40)*Math.cos(rad)let y = (r - 40)*Math.sin(rad)
Calculate the coordinates of the small dot: Because the center of the small dot must fall on the ring, the calculated horizontal and vertical coordinates are,
// Calculate the coordinates of the text let x = (r - 80)*Math.cos(rad) let y = (r - 80)*Math.sin(rad)
The specific code is as follows:
// Draw text ctx.font = '13px Arial'ctx.textAlign = 'center'ctx.textBaseline = 'middle'ctx.fillStyle=#000000let lengths = textArr.lengthtextArr.forEach(function(text,i){ //radians let rad = 2*Math.PI/lengths*i // Calculate the coordinates of the center of the small circle let x = (r - 40)*Math.cos(rad) let y = (r - 40)*Math.sin(rad) ctx.fillText(text,x+0.5,y+0.5)});// Draw small dots for(let i = 0;i<lengths;i++){ // // let rad = 2*Math.PI/lengths*i let x = (r - 80)*Math.cos(rad) let y = (r - 80)*Math.sin(rad)// // Draw small gray translucent dots on the edge ctx.beginPath() ctx.fillStyle = 'rgba(226,235,250, 0.8)' ctx.arc(x,y,8,0,2*Math.PI,false) ctx.closePath() ctx.fill() // Draw small blue dots ctx.beginPath() ctx.fillStyle = '#208fe5' ctx.arc(x,y,4,0,2*Math.PI,false) ctx.closePath( ) ctx.fill() }The drawing results are as follows:
4. Draw a triangle outside each small dot (the origin of the current canvas is the center of the canvas)
4.1 Because you want to draw a triangle shape, the idea of drawing a triangle is to draw lines to both sides with the center of the current small dot as the starting point, then use ctx.fill() to close the shape and fill the interior with gradient color.
Draw a triangle: the coordinates are calculated by themselves. The author adds and subtracts 35 on the abscissa and 70 on the ordinate (whatever you like, hahaha)
//Start the brush ctx.beginPath() ctx.moveTo(x,y) ctx.lineTo(x-35,y+70) ctx.lineTo(x+35,y+70) ctx.closePath()
Draw the text below the triangle: (To distinguish it from the previous text, I used red for my text here)
ctx.fillStyle= '#e3211c' ctx.fillText(textArr[i],x,y+75)
The specific code is as follows:
for(let i = 0;i<lengths;i++){ // // let rad = 2*Math.PI/lengths*i let x = (r - 80)*Math.cos(rad) let y = (r - 80)*Math.sin(rad) // // Draw s triangle // // ctx.rotate( -Math.PI / 4) ctx.beginPath() //Start the brush ctx.moveTo(x,y) ctx.lineTo(x-35,y+70) ctx.lineTo(x+35,y+70) ctx.closePath() // // Set color gradient-- ->Add color from the center to both sides var sColor = ctx.createLinearGradient (x,y,x+18,y+50) sColor.addColorStop(0,'rgba(106,128,243,0.5)') sColor.addColorStop(0.6,'rgba(83,183,243,0.5)') sColor.addColorStop(0.7,'rgba(129,200,224,0.5)') sColor.addColorStop(0.8,'rgba(130,219,251,0.5)') sColor.addColorStop(1,'rgba(195,228,223,0.5)') ctx.fillStyle= sColor ctx.fill() ctx.fillStyle= '#e3211c' ctx.fillText(textArr[i],x,y+75)}The drawing results are as follows:
4.2 The requirement is that the direction of each triangle is to spread outward, and now the direction of the triangles is downward, so now you need to use the canvas rotation method.
ctx.save() ctx.translate(x,y) // The rotation angle is centered on each small dot ctx.rotate( rad - Math.PI/2 ) // Because the small dot ctx.translate(- x, -y) . Omit the code for drawing triangles and text. . ctx.restore()
It can be seen from the calculation that taking the center of the small dot as the starting point of rotation, the arc of the triangle's rotation should be the arc of the current small dot minus π/2, because the starting position of the rotation always starts from the positive direction of the x-coordinate axis, That is, it starts at 0 radians, but now the triangles are all at π/2 radians, so:
Radian of rotation = Radian of small dot - π/2
Remember to use the Canvas state storage method save() when rotating.
restore() pops the stored Canvas state from the top of the stack in turn. If there is no stored Canvas state, there will be no change in executing this method.
Be sure to remember to use the restore() method at the end. At this point, the author shed tears of regret. . .
Specific code:
for(let i = 0;i<lengths;i++){ // // let rad = 2*Math.PI/lengths*i let x = (r - 80)*Math.cos(rad) let y = (r - 80)*Math.sin(rad) // Draw an s triangle ctx.save() // The rotation angle is centered on each small dot because the small dots are at the beginning ctx.translate(x,y) ctx.rotate( rad - Math.PI/2 ) ctx.translate(-x, -y) // Start the brush ctx.beginPath() ctx.moveTo(x,y) ctx.lineTo(x-35,y+70 ) ctx.lineTo(x+35,y+70) ctx.closePath() //Set color gradient--->Add color var from the center to both sides sColor = ctx.createLinearGradient (x,y,x+18,y+50) sColor.addColorStop(0,'rgba(106,128,243,0.5)') sColor.addColorStop(0.6,'rgba(83,183,243,0.5)') sColor.addColorStop(0.7,'rgba(129,200,224,0.5)') sColor.addColorStop(0.8,'rgba(130,219,251,0.5)') sColor.addColorStop(1,'rgba(195,228,223,0.5)') ctx.fillStyle= sColor ctx.fill() ctx.fillStyle= '#e3211c' ctx.fillText(textArr[i],x,y+75) ctx.restore()}Plot the result:
Take a closer look, what? ? ? Some text is upside down due to rotation problems. Through observation, the result is that when the radian is greater than π, the text becomes upside down.
It's time to write a wave of if judgments. . . .
How to rotate text:
function rotateContext(ctx, x, y, degree) { // Rotate text ctx.translate(x, y) // ctx.rotate(degree * Math.PI / 180) ctx.rotate(degree) ctx.translate(-x , -y) }Determine small dots whose radian is greater than π
if (rad > Math.PI) { // Because the text needs to be displayed on the edge of the triangle, the text should rotate with the triangle so that it can always be maintained on the // edge of the triangle. After rotation, the text will appear when the radian is greater than π. Reverse the problem, so rotate the text ctx.save() ctx.beginPath() // Rotate the text rotateContext(ctx, x, y+75, Math.PI) ctx.font = '13px Arial' ctx.textAlign = 'center' ctx.fillStyle = #ff2238 ctx.fillText(textArr[i], x, y+ 75) ctx.restore()} else { ctx.fillStyle = '#ff2238' ctx.fillText(textArr[i ], x, y + 75)}The drawing results are as follows:
Looking forward to victory, we are almost successful. At least we have a rough layout. The revolution has not yet succeeded, and comrades still need to work hard! !
5. The following is the implementation. When the mouse is over the small dot, the triangle on the edge and the text on the edge of the triangle are displayed, but the text on the edge of the circle is not displayed.
Idea:1. Bind the mouse entry event to the canvas
2. Determine whether the coordinates of the current mouse position on the canvas are equal to the coordinates near a small dot. If so, display the triangle corresponding to the small dot.
5.1 Bind the mousemove event to the canvas: the mouse is above the event
canvas.addEventListener('mousemove',clickEvent)5.2 Calculate the current coordinates of the mouse on the canvas
The calculation method is: use the current coordinates of the mouse on the DOM to subtract the distance from the left or top of the canvas to calculate the distance of the canvas.
The drawOne method in the figure below is the drawing method, which will be discussed later in the article.
function clickEvent() { // The coordinates of the mouse position let x = event.clientX - canvas.getBoundingClientRect().left let y = event.clientY - canvas.getBoundingClientRect().top drawOne(x,y)}5.3, because the coordinates of the mouse on the canvas calculated above are calculated with the upper left corner of the canvas as the origin, but the origin of the current canvas has already moved to the center of the canvas (250,250), so when used to determine whether a click is small dots You need to subtract 250 from both the horizontal and vertical coordinates to compare with the coordinates of the small dot on the current canvas. When I was making the judgment, I found a problem. I don’t know why the difference in the y direction of the author is 260 instead of 250. So the author subtracted 260 in the y direction.
The code is as follows:
Among them, Cx, Cy are the coordinates of the mouse on the canvas (the upper left corner of the canvas is the origin), x, y are the coordinates of the current small dot,
The author directly calculated the position of 15px near the center of the small dot, and all triangles were displayed, and the small dot turned white.
The most important thing is that you need to clear the previous canvas every time you redraw: remember to use the clearRect method to clear the canvas
let XX = Cx - 250let YY = Cy- 260let leftX = x - 15 let rightX = x + 15let topY = y - 15let bottomY = y + 15if (XX >= leftX && XX <= rightX && YY <= bottomY && YY > = topY ) {//It is clicked. . . . . . //Write the drawing code in the middle}The code is followed by a link:
6. Define an Input on the interface and bind the change event to the input.
Implementation: The interface is redrawn every time the value in the Input changes.
html code:
<input type=text id=inpt style=margin-left: 100px;margin-top: 50px placeholder=Please enter...>
js code:
let inpt = document.getElementById('inpt') inpt.addEventListener('change', function () { if (inpt.value !== '') { textArr.push(inpt.value) drawAll(2) //This The method is the drawing method, the source code will be given later in the article}})7. There is a problem. Every time you click on the interface and redraw the interface, there will be a flashing situation.
As shown below:
Every time you slide, because the coordinates of the mouse change, you need to clear the content around the ring and redraw it. So you need to clear the canvas to achieve dynamic effects.
clearRect() is very commonly used in Canvas animation drawing. It continuously clears the canvas content and then draws it to form an animation effect.
clearRect() can make a rectangular area in the Canvas element's canvas transparent.
context.clearRect(x, y, width, height);
x, y: x, y coordinates of the upper left corner of the rectangle.
width, height: the width and height of the cleared rectangular area.
Since clearRect() can only clear the rectangular area of the canvas, every time it is cleared, the background image in the middle will be cleared together.
Therefore, the background image must be reloaded every time, and it takes a certain amount of time to load the image, so it will flash every time it appears.
Solution:drawImage(image, dx, dy, dWidth, dHeight)
The parameter image: Canvas image resource, such as <img> image, SVG image, Canvas element itself, etc.
Then you can use other canvas to cache images.
Use an additional canvas to draw the background image, but for that canvas not displayed in the interface: display:none, and then use it to clear the canvas and directly render the cached canvas object to the middle of the canvas to be displayed, that is, there is no need to It is time-consuming to load images once.
html code:
<canvas width=280 height=280 style=margin-left: 50px;padding-top: 20px; display:none; id=canvas2> </canvas>
js code:
//Use caching to solve the flashing problem of redrawn images var tempCanvas = document.getElementById('canvas2')const tempCtx = tempCanvas.getContext('2d')tempCanvas.width = 280; tempCanvas.height = 280let image = new Image( )image.src = 'image/quan.png'image.onload = () => { // The origin moves to the center tempCtx.drawImage(image,0,0,280,280)}After clearing the canvas, draw the cached canvas:tempCanvas directly when redrawing the picture.
// Draw the cached canvas directly to the interface (the intermediate tire interface is cached) ctx.drawImage(tempCanvas,-140,-140)
Okay, I succeeded, here is the result picture:
The source code address is as follows:
https://github.com/Linefate/Dynamic-effect-of-canvas-ring.git
SummarizeThe above is the editor's introduction to using html5 canvas to draw ring animations. I hope it will be helpful to you. If you have any questions, please leave me a message and the editor will reply to you in time. I would also like to thank everyone for your support of the VeVb martial arts website!