使用stateProperties和hasStateChanged在fabric.js中实现undo / redo

时间:2014-08-18 07:05:46

标签: javascript fabricjs

编辑:此问题的范围有所改变。请参阅以下更新。

我一直在为fabric.js做一些撤销/重做(见fiddle)。虽然相当天真,但它大部分都在工作(用于添加/移除/移动或调整单个对象的大小或添加/删除对象组),但不适用于移动或调整对象组。在小提琴中,注意onObjectSelected()函数的控制台输出。

要了解我的意思,请在小提琴画布上绘制一些对象,然后单独移动或调整它们。撤消/重做按预期工作。但是,当选择并移动组时,我看不到如何检索组内对象的更新位置,这意味着撤消/重做无法恢复正确的值。

// onObjectSelected function quoted here to satisfy stackoverflow 
// question requirements. See full jsfiddle for context.
for (i in canvas.getObjects()) {
        if (canvas.item(i).active === true ) {
            console.log(canvas.item(i));
            selectedObject.push({
                "itemNum"   : i,
                "svdObject" : fabric.util.object.clone(canvas.item(i))
            });
        }
    }

原因似乎是当作为一组移动或调整大小时,对象位置/比例不会更新。我尝试了各种各样的东西,包括为所有对象运行setCoords(),但这没有帮助。所以如果有人能够发现我做错了什么,或者知道如何解决这个问题,我会很感激。

编辑:为了澄清,这只是一个普通的撤销/重做系统,当用户修改它们时,使用两个数组来存储画布上对象的属性。单击撤消或重做时,属性将应用于画布上的对象。我遇到的问题是,当选择 groups 对象并随后进行修改时,我不知道如何捕获某些属性,例如组中对象的位置,因为属性当由于某种原因将对象修改为组时,组中的对象不会更新。

更新:我已经了解到,当对象位于用户选择的组中时,对象位置会相对于它所在的组进行记录,而不再是画布,这是有意义的。因此,当处理组中的对象时,它的左/顶坐标添加到组左/顶部坐标以计算对象相对于画布的坐标。 scaleX / Y应乘以group scaleX / Y.

更新:我已经被管理以实现我的目标(参见new fiddle)。我已经了解了fabric的'stateful'标志,stateProperties数组和hasStateChanged方法,这将简化事情。这些文档很少,到目前为止我还没有成功地在这个解决方案中使用它们。使用这些的使用技巧或示例代码将不胜感激。具体来说,我希望能够控制保存哪些属性,因为我不需要保存所有可更改的属性。

onObjectSelected目前看起来像这样:

function onObjectSelected() {

  selectedObject = [];

  for (i in canvas.getObjects()) {

    if (canvas.item(i).active === true) {

      // shft-ctrl-j to see item properties on click (chrome)
      console.log(canvas.item(i));

      // objects can be flipped, but not groups 
      var groupLeft = 0;
      var groupTop = 0;
      var groupScaleX = 1;
      var groupScaleY = 1;
      var groupAngle = 0;


      // if it's a group, add group coords also
      if (typeof canvas.item(i).group != "undefined") {
        var groupLeft = canvas.item(i).group.left;
        var groupTop = canvas.item(i).group.top;
        var groupScaleX = canvas.item(i).group.scaleX;
        var groupScaleY = canvas.item(i).group.scaleY;
        var groupAngle = canvas.item(i).group.angle;
      }

      selectedObject.push({
        "itemNum": i,
        "left": canvas.item(i).left + groupLeft,
        "top": canvas.item(i).top + groupTop,
        "scaleX": canvas.item(i).scaleX * groupScaleX,
        "scaleY": canvas.item(i).scaleY * groupScaleY,
        "angle": canvas.item(i).angle + groupAngle,
        "flipX": canvas.item(i).flipX,
        "flipY": canvas.item(i).flipY
      });
    }

  }

};

1 个答案:

答案 0 :(得分:0)

Fabricjs默认不支持自动缩放或定位。支持这一点的唯一方法是实现自己的扩展/定位机制。我也尝试了很多东西,但这些线条目前运作得最好。

所以基本上,请在你的js中添加这些功能:

function prepareRatio(o){
    saveRatio(o);
    o.on('modified', function(e){
        saveRatio(o);
    });
}

function restoreRatio(o){
    o.set({
        left: o._ratio._left * _cv.width,
        top: o._ratio._top * _cv.height,
        width: o._ratio._width * _cv.width,
        height: o._ratio._height * _cv.height
    });

    o.setCoords();
}

function saveRatio(obj){
    obj._ratio = { 
        _left: obj.left / _cv.width,
        _top: obj.top / _cv.height,
        _width: obj.width / _cv.width,
        _height: obj.height / _cv.height
    };
}

我在这里使用了两个自定义变量,其中_cv是用于存储目标画布的宽度和高度的简化结构,_ratio用于每个对象(常量)比值。

在创建对象或调整画布大小时明智地使用它们:

fabric.Image.fromURL('xxxx.png', function(img) {

    img.set({
        // TODO: give object width and height, left and top
    });

    // save ratio once you create an object
    prepareRatio(img);

    canvas.add(img);

    // OPTIONAL: add your own codes
});

function redraw() {

    // TODO: resize your canvas as you want

    _cv = {
        width: canvas.getWidth(),
        height: canvas.getHeight()
    };

    canvas.forEachObject(function(obj) {
        // restore ratio every time when you resize your canvas
        restoreRatio(obj);
    });
}

window.addEventListener('resize', redraw, false);

这看起来有点长,但事实上非常简单明了。我很确定这会立即在你身边发挥作用。希望这会有所帮助。