简体   繁体   中英

How to rotate a canvas circle continuously?

https://codepen.io/saketkr7/pen/KKmzXOV I have a circle canvas in which n items are placed, I want to rotate all items placed? How can I do that?

[![`

 const canvas = document.getElementById('myCanvas'); var items = ['a', 'b' , 'c', 'd', 'e', 'g']; const ctx = canvas.getContext('2d'); var n = 6; var numElements = 8; var angle = 0; var step = (2*Math.PI) / numElements; var rotateAngle = 36 * Math.PI / 180; for(var i = 0; i < numElements; i++) { var x = 500/2 + 100 * Math.cos(angle); var y = 500/2 + 100 * Math.sin(angle); console.log(x, y); ctx.beginPath(); ctx.arc(x, y, 10, 0, 2 * Math.PI); ctx.stroke(); angle += step; }
 <!DOCTYPE html> <html> <body> <canvas id="myCanvas" width="500" height="500"> Your browser does not support the HTML canvas tag. </canvas> </body> </html>

`] 1 ] 1

requestAnimationFrame

Use requestAnimationFrame(callback) (rAF) to render the animation.

The callback function is responsible for rendering each animation frame. In this case it will clear the canvas and draw the circles.

The callback function gets the time in milliseconds (1/1000th seconds). You can use that to set the rotation angle. The first example uses the time and the constant rate to define the number of rotations per second.

In the callback function you need to request the new frame by calling rAF.

To start the animation request the first frame.

Rendering

Modify your code so that it is a function that can be called for each frame of the animation. In the example your modified code is in the function drawCircles(angle) where angle is the current rotation in radians.

Pass it an argument that is the current rotation.

Example

The snippet below does what is described above.

 const ctx = canvas.getContext('2d'); const rate = 0.2; // Number of rotations per second function drawCircles(angle) { var i = 0; const numElements = 8; const step = (2 * Math.PI) / numElements; ctx.beginPath(); while (i < numElements) { const x = ctx.canvas.width / 2 + 100 * Math.cos(angle + i * step); const y = ctx.canvas.height / 2 + 100 * Math.sin(angle + i * step); ctx.moveTo(x + 10, y); ctx.arc(x, y, 10, 0, 2 * Math.PI); i++; } ctx.stroke(); } requestAnimationFrame(renderLoop); // rAF to start animation function renderLoop(time) { // rAF callback ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); drawCircles(((time * Math.PI * 2) / 1000) * rate); requestAnimationFrame(renderLoop); // request next frame }
 <canvas id = "canvas" width="220" height="220"></canvas>

On many devices the frame rate will be very stable, you can use a fixed rate time to get a smoother animation. As shown in next snippet. Be aware that time will drift if device drops frames.

 const ctx = canvas.getContext('2d'); const rate = 0.2; // APPROX Number of rotations per second var frame = 0; // counts frames function drawCircles(angle) { var i = 0; const numElements = 8; const step = (2 * Math.PI) / numElements; ctx.beginPath(); while (i < numElements) { const x = ctx.canvas.width / 2 + 100 * Math.cos(angle + i * step); const y = ctx.canvas.height / 2 + 100 * Math.sin(angle + i * step); ctx.moveTo(x + 10, y); ctx.arc(x, y, 10, 0, 2 * Math.PI); i++; } ctx.stroke(); } requestAnimationFrame(renderLoop); // rAF to start animation function renderLoop() { // rAF callback ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); time = frame++ * (1000 / 60); // Assumes 60 fps drawCircles(((time * Math.PI * 2) / 1000) * rate); requestAnimationFrame(renderLoop); // request next frame }
 <canvas id = "canvas" width="220" height="220"></canvas>

Update

Re comments

The @MDN.API.CanvasRenderingContext2D@ is not best for 3D. The best option is to use WebGL However there is a steep learning curve for WebGL.

3D can be done on the 2D API but you need to implement all the 3D rendering code in JS which will be orders of magnitude slower than WebGl.

3D via 2D API

The example below uses the 2D canvas to render a rotating ring of toon-shaded spheres rotating in 3D.

It is the most basic example and will not support cameras, lights, textures, etc..

 const ctx = canvas.getContext('2d'); const rate = 0.2; // APPROX Number of rotations per second const numCircles = 18; const perspectiveRange = 300; // dist between front and back planes const ringRadius = 60; // in pixels const circleRadius = 10; // in pixels. Radius of circle at z === 0 const colors = [["#B11", "#F22"], ["#DB0", "#FF0"]]; var frame = 0; // counts frames function drawCircles(angle, rotY) { // rotZ rotates around Y axis (in and out of screen) var i = 0; ctx.fillStyle = "#FF0"; const step = (2 * Math.PI) / numCircles; const circles = []; // The transform for y rotation const dx = Math.cos(rotY); const dy = Math.sin(rotY); // get 3D location of each circle while (i < numCircles) { const x = ringRadius * Math.cos(angle + i * step); const y = ringRadius * Math.sin(angle + i * step); circles.push({x: x * dx, y, z: x * dy, colIdx: i % 2}); i++; } // sort circles from back to front circles.sort((a, b) => bz - az); // center on canvas ctx.setTransform(1,0,0,1, ctx.canvas.width / 2, ctx.canvas.height / 2); // draw 3D circles with perspective for (const c of circles) { const col = colors[c.colIdx]; // Calculate perspective scale. The further from the front the // smaller the perspective scale const p = (perspectiveRange - cz) / perspectiveRange; // Scale radius, x, y pos and line with by perspective scale const r = Math.abs(p * circleRadius); const x = p * cx; const y = p * cy; ctx.lineWidth = 1.5 * p; // shaded color ctx.fillStyle = col[0]; ctx.beginPath(); ctx.arc(x, y, r, 0, 2 * Math.PI); ctx.fill(); ctx.stroke(); // highlight color ctx.fillStyle = col[1]; ctx.beginPath(); ctx.arc(x - r * 0.1, y - r * 0.1, r * 0.8, 0, 2 * Math.PI); ctx.fill(); ctx.fillStyle = "#FFFA"; ctx.beginPath(); ctx.arc(x - r * 0.3, y - r * 0.3, r * 0.3, 0, 2 * Math.PI); ctx.fill(); } // reset canvas transform ctx.setTransform(1,0,0,1,0, 0); } requestAnimationFrame(renderLoop); // rAF to start animation function renderLoop() { // rAF callback ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); time = frame++ * (1000 / 60); // Assumes 60 fps const ang = ((time * Math.PI * 2) / 1000) * rate drawCircles(ang, ang / 2); requestAnimationFrame(renderLoop); // request next frame }
 <canvas id = "canvas" width="180" height="180"></canvas>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM