画布动画中闪烁的图像

时间:2016-10-26 15:00:01

标签: javascript html5 canvas fabricjs

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

2 个答案:

答案 0 :(得分:2)

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

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

我已将base_image的{​​{1}}加载移出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);
  }
  });

答案 1 :(得分:2)

画布动画。

当浏览器上的动画始终使用resquestAnimationFrame时,只需将新的像素数据与刷新时间同步移动到显示器,它就会比setIntervalsetTimeout提供更好的结果。

闪烁的原因

这很复杂......

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

另一个原因是使用setInterval来自 DominicValenciana 的答案仍然使用setInterval,如果你仔细观察,你会发现它不像使用{{{ 1}}。好的动画应该像丝绸一样光滑(尽可能接近)

演示下面的一些提示。

This answer也有助于解释图片加载和绘图。

&#13;
&#13;
requestAnimationFrame
&#13;
&#13;
&#13;

Angles,PI和Trig

您对精灵位置的计算

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

在显示屏顶部(12点位置)有零(角度= 0)。要匹配画布旋转功能使用的相同角度,请使用

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

屏幕右侧(3挂锁位置)<零>

如果您想继续创建动画和计算机图形。忘了大约360度。圈子是2PI不是360左右,PI是半圈180,PI / 2是四分之一圈90,PI / 30是每分钟一分钟的动作,圈子周围的距离是x = Math.cos(angle); y = Math.sin(angle); ,大约半圈2*PI*radius。 JS中的每个trig函数都使用弧度(不是烦人的,或者是钝的,但因为它使用起来要简单得多),如果你在deg中工作,你总是需要在某个阶段使用PI,所以只需减少不必要的开销。 / p>

常量和变量

如果您从未打算更改变量,则仅初始化变量一次。更好的是,如果变量引用永远不需要更改,则使其成为常量PI * radius如果您尝试以任何方式更改它将导致错误const myVar = 10; ERROR !!!!!

表现一切的动画

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

不要支持死者

不需要moz,webkit前缀ctx.setTransform(1,0,0,1,0,0);无处不在。据我所知,如果浏览器不支持requestAnimationFrame它不支持画布,我们不应该支持浏览器。 requestAnimationFrame不仅适用于canvas,它还应该用于您创建的任何基于DOM javascript的动画。