javascript - Canvas 动画中的闪烁图像

标签 javascript html 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 对其进行动画处理时,清除并重新绘制 Canvas 上的图像结果会闪烁。

有没有办法让轨道动画变得平滑?或者创建轨道动画的更好方法?

Codepen

最佳答案

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/

相关文章:

javascript - 通过高度限制可编辑的div区域,在Javascript中模拟onkeyup事件

javascript - 在 HTML5 中将两个图像组合在一个 Canvas 上

java - 合并两个图像总是顶部和底部。但我想在 android 中并排

java - 获取 Canvas 中的鼠标位置(java)

Javascript访问div

javascript - 以 ; 结尾的函数表达式与不

html - 伪元素背景图片不出现

JavaScript 获取元素的 data-*

javascript - 垂直滚动多个 div

javascript - 有没有办法在没有 jQuery 的情况下做同样的事情,只使用 CSS3 和 HTML5