javascript - 如何连续旋转画布圈?
问题描述
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>
解决方案
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>
推荐阅读
- objective-c - iOS Linked-in SDK 返回 null AuthToken ,即使身份验证成功
- ios - 有没有办法用纵横交错的单元格制作 UICollectionView?
- c# - 从哪里开始编码以及如何从 .txt 读取文件?
- mysql - 查询正在运行,但没有得到正确的结果。有没有我可以使用的替代品?
- javascript - jQuery,如果“a”标签有类或没有
- mysql - 是否可以根据“IF”语句在 SELECT 列之间进行更改?
- python - 如何在python中重载“@”运算符?
- typescript - 如何将 Auth0 与 typescript-express-decorators 一起使用
- python - 如何设置 QGraphicsLineItem 的ObjectName
- python - 我的简单散景图中出现的额外线条是什么?