如何用大量的Objects改进canvas fabric.js的性能

时间:2017-03-31 11:17:01

标签: svg html5-canvas fabricjs

我需要创建一个拥有大约30k对象的应用程序,用户可以平移,缩放或“选择单击”任何这些对象。 正在使用Fabric.js Canvas

我使用SVG和svg-pan-zoom插件(没有Canvas Element)做了同样的事情,效果更好

问题:点击时缩放,平移或对象存在显着滞后

  1. 会删除Fabric.js提高性能吗?
  2. 会切换到WebGL提高性能吗?
  3. 尝试过Fabric特定选项

    fabric.Object.prototype.objectCaching = false;
    fabric.Object.prototype.statefullCache = false;
    fabric.Object.prototype.noScaleCache = true;
    fabric.Object.prototype.needsItsOwnCache = false;
    

    更新 继续更新Fiddle

    供参考:

    1. canvas-vs-svg-vs-div Stackoverflow

    2. Stackoverflow

1 个答案:

答案 0 :(得分:4)

不要在IO活动中渲染!

虽然不是对更新速度的完全修复,但这个答案将使交互速度加倍。

鼠标和事件与画布(和DOM)交互的一个常见的,几乎是标准的错误是将渲染委托给鼠标/触摸事件。这是非常糟糕的做法,因为鼠标事件以比显示器显示的更高的速率发射。当您排队鼠标事件(伪渲染事件)并为鼠标的每次移动重新渲染时,渲染时间很长会变得更糟

  

注意阻止代码会停止鼠标事件,但只要引擎处于空闲状态,鼠标就会再次以全速率开始。

使用鼠标事件来获取鼠标状态。使用与显示同步的动画循环仅在需要时渲染并且有时间可用。应该累积记录轮子和鼠标移动增量等内容。

mouse.dx += event.movementX; 
mouse.dy += event.movementY; 
mouse.wheel += event.wheelDelta;

并在主渲染循环中使用它们......

function update(){

    // ... code to use mouse
    // consume deltas
    mouse.x = mouse.y = mouse.wheel = 0;

...这可确保在渲染更新之间可能有许多鼠标事件时,可以准确地遵循鼠标状态。

示例,将事件与渲染分开。

在你提供给下面的小提琴中改变你的代码,在我的机器上,它使渲染速度加倍(这仍然非常慢)。

// from just after the function applyZoom replace all the code 
var mouse = {  // holds the mouse state
    x : 0,
    y : 0,
    down : false,
    w : 0,
    delta : new fabric.Point(0,0),
}
// event just track mouse state
function zoom(e) {
    if(e != null) { e.preventDefault() }
    var evt=window.event || e;
    mouse.x = e.offsetX;
    mouse.y = e.offsetY;
    mouse.w += evt.detail? evt.detail*(-120) : evt.wheelDelta;
    return false;
}
canvas.on('mouse:up', function (e) { mouse.down = false });
canvas.on('mouse:out', function (e) { mouse.down = false });
canvas.on('mouse:down', function (e) { mouse.down = true });
canvas.on('mouse:move', function(e) {
    if (e && e.e) {
        mouse.delta.x += e.e.movementX;
        mouse.delta.y += e.e.movementY;
    }
});
// main animation loop
function update(){
    if(mouse.w !== 0){  // if the wheel has moved do zoom
        var curZoom = canvas.getZoom();
         canvas.zoomToPoint(
             { x : mouse.x, y: mouse.y }, 
             canvas.getZoom() + mouse.w / 4000
         );
         mouse.w = 0; // consume wheel delta
    }else if(mouse.down) {  // if mouse button down
         canvas.relativePan(mouse.delta);
    }
    // consume mouse delta
    mouse.delta.x = 0;
    mouse.delta.y = 0;
    requestAnimationFrame(update);
}
requestAnimationFrame(update);