【问题标题】:Flickering images in canvas animation画布动画中闪烁的图像
【发布时间】:2017-03-09 00:31:54
【问题描述】:
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

【问题讨论】:

    标签: javascript html canvas fabricjs


    【解决方案1】:

    画布动画。

    在浏览器上制作动画时始终使用requestAnimationFrame,它通过仅将新像素数据与刷新时间同步移动到显示器上,提供比setIntervalsetTimeout 更好的结果。

    为什么会闪烁

    这很复杂……

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

    另一个原因是使用setIntervalDominicValenciana的答案仍然使用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();
    })();

    角度、PI 和 Trig

    您对精灵位置的计算

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

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

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

    屏幕右侧(3 点钟位置)为零

    如果您想继续创建动画和计算机图形。忘记大约 360 度。圆圈是2PI 不是360 左右,PI 是半圈180,PI/2 是四分之一圈90,PI/30 是分针每分钟走动,绕圈的距离是2*PI*radius,绕半圈@987654332 @。 JS 中的每个三角函数都使用弧度(不是烦人或钝,而是因为它更易于使用),如果您在 deg 中工作,您将始终需要在某个阶段使用 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,前缀为 requestAnimationFrame 的 webkit 无处不在。据我所知,如果浏览器不支持requestAnimationFrame,它不支持画布,我们不应该像浏览器那样支持。 requestAnimationFrame 不仅适用于画布,还应该用于您创建的任何基于 DOM javascript 的动画。

    【讨论】:

    • 感谢您提供这么棒的信息!我将把它设置为更好的解决方案和更详细的解释的答案:)
    • 这对我有用,但我在使用时遇到了问题。当我多次加载它时。它减慢了我的机器速度。有什么办法可以清除动画帧?我试过 cancelAnimationFrame 但没有运气。
    【解决方案2】:

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

    这是一个工作版本: 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);
      }
      });
    

    【讨论】:

    • 我通过添加 Alpha 通道并修复像素颜色值来清理图像。 -> i.imgur.com/5hwSfRt.png(这将消除白色重叠):D
    • 谢谢!希望问题所有者会发现它有用。
    • 感谢 dominicvalenciana,这太棒了!但是我将使用blindman67 使用requestAnimationFrame 而不是setInterval 作为这个答案,再次感谢您的努力和mr-polywhirl 的清洁图像版本:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    • 2012-05-11
    • 1970-01-01
    • 2016-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多