在画布动画中闪烁的图像

Flickering images in canvas animation

本文关键字:闪烁 图像 动画 布动画      更新时间:2023-09-26
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,清除和重新绘制画布上的图像结果闪烁。

有办法使轨道动画平滑吗?或者更好的方法来创建轨道动画?

Codepen

画布动画。

当浏览器上的动画总是使用requestAnimationFrame时,它提供了比setIntervalsetTimeout更好的结果,它只移动新的像素数据到显示器与刷新时间同步。

为什么闪烁

很复杂…

基本原因是每次想要使用它时都要加载图像。onload直到代码停止运行后才会触发。当代码停止运行时,画布内容将呈现给显示器,并且它将为空,因为您刚刚清除了它(在退出绘制函数之前不会触发任何事件)。然后onload事件将开始触发,每个事件都在画布上绘制图像。当事件退出时,浏览器认为"好吧,你想把它显示出来!"整个画布再次呈现在显示器上。因为这比屏幕刷新率快得多,所以你会得到奇怪的剪切,闪烁,跳跃效果。

另一个原因是使用setInterval的答案来自DominicValenciana仍然使用setInterval,如果你仔细看,你会发现它不像使用requestAnimationFrame那样流畅。好的动画应该平滑如丝(以及尽可能接近)

下面的一些提示。

这个答案也将有助于解释图像加载和绘制。

(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();
})();

角度、PI和三角函数

你对精灵位置的计算

x = Math.sin(angle);
y = Math.cos(angle);

在显示器的顶部(12点钟位置)为零(角度= 0)。要匹配画布旋转函数使用的相同角度,请使用

x = Math.cos(angle);
y = Math.sin(angle);

这个在屏幕的右边有0 (3clock的位置)

如果你想继续创建动画和计算机图形。忘掉360度吧,圆是2PI,不是360,PI是半圈180,PI/2是四分之一圈90,PI/30是分针每分钟的运动,绕一个圆的距离是2*PI*radius,绕半个圆的距离是PI * radius。JS中的每个三角函数都使用弧度(不是为了烦人,也不是为了钝化,而是因为它使用起来更简单),如果你用度来工作,在某些阶段你总是需要使用PI,所以只要减少不必要的开销就可以了。

常量和变量

如果你不打算改变变量,只初始化它一次。更好的是,如果一个变量引用永远不需要改变,让它成为一个常量const myVar = 10;,如果你试图以任何方式改变它,它会抛出一个错误myVar = 10; error !!!!!

动画,表现就是一切

当制作动画时,保持最大的性能是很重要的,这样你就可以在动画质量上投入更多。当设置转换时,使用ctx.setTransform(scale,0,0,scale,x,y); ctx.rotate(angle);要容易得多,因为您不需要为转换保存和恢复完整的画布状态。如果您希望获得默认转换,请使用ctx.setTransform(1,0,0,1,0,0);

不支持已死

不需要moz,webkit前缀requestAnimationFrame无处不在。据我所知,如果浏览器不支持requestAnimationFrame,它不支持画布,我们不应该支持浏览器。此外,requestAnimationFrame不只是用于画布,它应该用于任何基于DOM javascript的动画你创建。

问题是每次在圆圈中渲染图像时都会重新加载图像的新实例。网络延迟导致您看到的这种闪烁。通过首先加载图像,然后重用数据,我已经删除了闪烁。

这是一个工作版本:http://codepen.io/anon/pen/JRVJRJ

我已经将base_image的加载移出make_base函数,并使其成为make_base函数中重用的全局变量。导致图像被加载一次,并重新应用多次。

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) {
      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);
    }
  }
base_image = new Image();
    base_image.src = 'https://gxzzxb.files.wordpress.com/2014/12/16mario.png';
    base_image.onload = function(){
      var step = 0;
    draw(step);
    setInterval(function() {
  draw(step);
          ++step;
        }, 20);
  }
  });