首页 > 解决方案 > 如何连续旋转画布圈?

问题描述

https://codepen.io/sakekr7/pen/KKmzXOV 我有一个圆形画布,其中放置了 n 个项目,我想旋转所有放置的项目?我怎样才能做到这一点?

[![`

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

标签: javascripthtmlhtml5-canvas

解决方案


requestAnimationFrame

使用requestAnimationFrame(callback) (rAF) 来渲染动画。

回调函数负责渲染每个动画帧。在这种情况下,它将清除画布并绘制圆圈。

回调函数以毫秒(1/1000 秒)为单位获取时间。您可以使用它来设置旋转角度。第一个示例使用time和 常量rate来定义每秒的旋转次数。

在回调函数中,您需要通过调用 rAF 来请求新帧。

开始动画请求第一帧。

渲染

修改您的代码,使其成为可以为动画的每一帧调用的函数。在示例中,您修改的代码在函数drawCircles(angle)中,其中角度是当前的弧度旋转。

向它传递一个参数,即当前旋转。

例子

下面的代码片段做了上面描述的事情。

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>

在许多设备上,帧速率会非常稳定,您可以使用固定速率时间来获得更流畅的动画。如下一个片段所示。请注意,如果设备丢帧,时间会漂移。

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>

更新

重新评论

@MDN.API.CanvasRenderingContext2D@ 不适合 3D。最好的选择是使用 WebGL 但是 WebGL 的学习曲线很陡峭。

3D 可以在 2D API 上完成,但您需要在 JS 中实现所有 3D 渲染代码,这将比 WebGl 慢几个数量级。

通过 2D API 实现 3D

下面的示例使用 2D 画布来渲染以 3D 方式旋转的卡通阴影球体的旋转环。

它是最基本的示例,不支持相机、灯光、纹理等。

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) => b.z - a.z);

// 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 - c.z) / perspectiveRange;

     // Scale radius, x, y pos and line with by perspective scale
     const r = Math.abs(p * circleRadius);
     const x = p * c.x;
     const y = p * c.y;
     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>


推荐阅读