Fabric.js: 在很多对象上使用 animate 方法太慢了

Fabric.js: using animate method on many object is so slow

我正在使用 animate 方法为 100 个对象设置动画,但在我的情况下,性能太慢了,我该如何解决?

我的演示代码: https://jsfiddle.net/cs6jqj2w/

请看一下fabricJS demo

此外,我使用您的生成随机数的函数修改了该演示,并在此 fiddle

中创建了 100 个 shpaes
(function() {
  var canvas = this.__canvas = new fabric.Canvas('c');
  fabric.Object.prototype.transparentCorners = false;

  var Cross = fabric.util.createClass(fabric.Object, {
    objectCaching: false,
    initialize: function(options) {
      this.callSuper('initialize', options);
      this.animDirection = 'up';

      this.width = 100;
      this.height = 100;

      this.w1 = this.h2 = 100;
      this.h1 = this.w2 = 30;
    },

    animateWidthHeight: function() {
      var interval = 2;

      if (this.h2 >= 30 && this.h2 <= 100) {
        var actualInterval = (this.animDirection === 'up' ? interval : -interval);
        this.h2 += actualInterval;
        this.w1 += actualInterval;
      }

      if (this.h2 >= 100) {
        this.animDirection = 'down';
        this.h2 -= interval;
        this.w1 -= interval;
      }
      if (this.h2 <= 30) {
        this.animDirection = 'up';
        this.h2 += interval;
        this.w1 += interval;
      }
    },

    _render: function(ctx) {
      ctx.fillRect(-this.w1 / 2, -this.h1 / 2, this.w1, this.h1);
      ctx.fillRect(-this.w2 / 2, -this.h2 / 2, this.w2, this.h2);
    }
  });


  for (var i = 0; i < 100; i++){
   canvas.add(
   new Cross({ top: getRandomInt(0,500), left: getRandomInt(0,500)})
   );
  }


  setTimeout(function animate() {
    canvas.forEachObject(function(obj){ obj.animateWidthHeight(); obj.dirty = true; });
    canvas.renderAll();
    setTimeout(animate, 10);
  }, 10);
})();

function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

更新: 您的动画效果不佳,因为您必须对 canvas.

进行过多渲染

您必须生成 99 个不带渲染的项目,最后一个带渲染。另外,为了完成所有形状的动画,最后一项对我来说必须是动画的最长持续时间。

var fabric = window.fabric

var canvas = new fabric.StaticCanvas('c')


function createItem(canvas) {
  var item = new fabric.Circle({
    left: -100,
    top: getRandomInt(0, 500),
    opacity: Math.random().toFixed(2),
    radius: getRandomInt(10, 50),
  })
  item.keepGoing = true
  canvas.add(item)
 // itemTopAnim(canvas, item, getNextTop(item.top))
//  itemLeftAnim(canvas, item)
return item;
}

function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function getNextTop(top) {
  if (top < (canvas.height / 2)) {
    return top + getRandomInt(50, 200)
  }
  return top - getRandomInt(50, 200)
}

function itemTopAnim(canvas, item, top) {
  item.animate('top', top, {
    duration: getRandomInt(1, 3) * 1000,
   // onChange: canvas.renderAll.bind(canvas),
    easing: fabric.util.ease.easeInOutCubic,
    onComplete: function() {
      item.keepGoing && itemTopAnim(canvas, item, getNextTop(item.top))
    }
  })
}

function itemTopAnimLast(canvas, item, top) {
  item.animate('top', top, {
    duration: 3 * 1000,
    onChange: canvas.renderAll.bind(canvas),
    easing: fabric.util.ease.easeInOutCubic,
    onComplete: function() {
      item.keepGoing && itemTopAnim(canvas, item, getNextTop(item.top))
    }
  })
}

function itemLeftAnim(canvas, item) {
  item.animate('left', canvas.width - item.radius, {
    duration: getRandomInt(5, 10) * 1000,
    //onChange: canvas.renderAll.bind(canvas),
    onComplete: function() {
      item.keepGoing = false
    }
  })
}

function itemLeftAnimLast(canvas, item) {
  item.animate('left', canvas.width - item.radius, {
    duration: 10 * 1000,
    onChange: canvas.renderAll.bind(canvas),
    onComplete: function() {
      item.keepGoing = true
    }
  })
}

for (var i = 0; i < 100; i++) {
var item = createItem(canvas);
if (i == 99){
itemLeftAnimLast(canvas, item)
itemTopAnimLast(canvas, item, getNextTop(item.top))
} else {
itemLeftAnim(canvas, item)
itemTopAnim(canvas, item, getNextTop(item.top))
}

}

检查此更新fiddle

希望它能让您更多地了解它现在是如何工作的。

听起来你现在已经可以使用了。只是想我要补充一点,http://fabricjs.com/fabric-intro-part-2 上的文档还提到在为大量对象设置动画时,您可能想使用 requestAnimationFrame 而不是 onChange 回调:

The reason animate doesn't automatically re-render canvas after each change is due to performance. After all, we can have hundreds or thousands animating objects on canvas, and it wouldn't be good if every one of them tried to re-render screen. In the case of many objects, you could use something like requestAnimationFrame (or other timer-based) loop to render canvas continuosly on its own, without calling renderAll for each object. But most of the time, you'll probably need to explicitly specify canvas.renderAll as "onChange" callback.

我遇到了和你类似的问题,这个建议很有效。