window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
$(function() {
var centerX = 400,
centerY = 400,
radius = 300;
function make_base(ctx, angle) {
base_image = new Image();
base_image.src = 'https://gxzzxb.files.wordpress.com/2014/12/16mario.png';
base_image.onload = function(){
ctx.save();
var x = centerX + Math.sin(angle) * radius;
var y = centerY + Math.cos(angle) * radius;
ctx.translate(x, y);
ctx.drawImage(base_image, 0, 0);
ctx.rotate(angle);
ctx.restore();
}
}
function draw(step) {
var ctx = document.getElementById("canvas").getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
var angle;
for (var i = 0; i < 180; i += 10) {
angle = step + (i * Math.PI / 90);
make_base(ctx, angle);
}
}
var step = 0;
draw(step);
setInterval(function() {
draw(step);
++step;
}, 20);
});
问题是当我尝试循环图像以创建环形并使用 setInterval 对其进行动画处理时,清除并重新绘制 Canvas 上的图像结果会闪烁。
有没有办法让轨道动画变得平滑?或者创建轨道动画的更好方法?
最佳答案
Canvas 动画。
在浏览器上制作动画时,始终使用 requestAnimationFrame
,它给出的结果比 setInterval
好得多和setTimeout
仅将新像素数据与刷新时间同步移动到显示器。
为什么闪烁
很复杂...
基本原因是您每次想要使用图像时都会加载该图像。只有在代码停止运行后才会触发 onload。当您的代码停止运行时, Canvas 内容将呈现在显示屏上,并且它将是空的,因为您刚刚清除了它(在退出绘制函数之前不会触发任何事件)。然后 onload 事件将开始触发,每个事件都会在 Canvas 上绘制图像。当事件退出时,浏览器会认为“好吧,你想把它显示出来!”整个 Canvas 再次呈现在显示器上。因为这种情况发生的速度比屏幕刷新率快得多,所以你会得到奇怪的剪切、闪烁、跳跃效果。
另一个原因是使用setInterval
DominicValenciana 的答案仍然使用 setInterval
如果你仔细观察,你会发现它并不像使用 requestAnimationFrame
那样平滑。 。好的动画应该像丝绸一样光滑(尽可能接近)
演示下面的一些提示。
This answer还将帮助解释图像加载和绘图。
(function () {
const oneDeg = Math.PI / 180;
const D1 = Math.PI / 180; // again for lazy programmers
const centerX = 400;
const centerY = 400;
const radius = 300;
// create and load image
const base_image = new Image();
base_image.src = 'https://gxzzxb.files.wordpress.com/2014/12/16mario.png';
// create size and add to DOM a canvas
const c = document.createElement("canvas");
c.width = 800;
c.height = 800;
document.body.appendChild(c);
const ctx = c.getContext("2d"); // get context
var step = 0; // the animation step
function draw() { // the main draw function
var x,y,i,angle;
if(base_image.complete){ // has the image loaded
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
ctx.clearRect(0, 0, c.width, c.height);
for (i = 0; i < Math.PI * 2; i += D1 * 10) { // 360 in steps of 10
angle = step + i; // angle in radians
x = centerX + Math.sin(angle) * radius;
y = centerY + Math.cos(angle) * radius;
ctx.setTransform(1, 0, 0, 1, x, y); // set translate and scale no need for save restore
ctx.rotate(angle); // set angle
ctx.drawImage(base_image, 0, 0); //draw image
}
step += 1* D1; // step 1 deg per frame
}
requestAnimationFrame(draw);
}
draw();
})();
Angular 、PI 和三 Angular 函数
您对 Sprite 位置的计算
x = Math.sin(angle);
y = Math.cos(angle);
在显示屏顶部(12 点钟位置)为零( Angular = 0)。要匹配 Canvas 旋转功能使用的相同 Angular ,请使用
x = Math.cos(angle);
y = Math.sin(angle);
屏幕右侧(3 点钟位置)为零
如果您想继续创作动画和计算机图形。忘记360度吧。圆是 2PI 绕不是 360,PI 是半圈 180,PI/2 是四分之一圈 90,PI/30 是每分钟分针运动,绕一圈的距离是 2*PI*radius
,绕半圈PI * radius
。 JS 中的每个三 Angular 函数都使用弧度(不是烦人或迟钝,而是因为它使用起来更简单),如果你在 deg 中工作,你总是需要在某个阶段使用 PI,所以只需删除不必要的开销。
常量和变量
如果您从不打算更改变量,则仅初始化变量一次。更好的是,如果变量引用永远不需要更改,请将其设为常量 const myVar = 10;
如果您尝试以任何方式更改它,都会抛出错误 myVar = 10;
错误!!!!!
性能决定一切的动画
制作动画时,保持最佳性能非常重要,这样您就可以在动画质量上投入更多。设置变换时,使用ctx.setTransform(scale,0,0,scale,x,y);
要容易得多。 ctx.rotate(angle);
因为您不需要仅为变换而保存和恢复整个 Canvas 状态。如果您希望获得默认转换,请使用 ctx.setTransform(1,0,0,1,0,0);
不要支持死者
不需要 moz,webkit 前缀 requestAnimationFrame
无处不在。据我所知浏览器是否不支持 requestAnimationFrame
它不支持 Canvas ,我们不应该像浏览器那样支持 Canvas 。另外 requestAnimationFrame 不仅仅适用于 Canvas ,它还应该用于您创建的任何基于 DOM javascript 的动画。
关于javascript - Canvas 动画中的闪烁图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40265707/