使用stateProperties和hasStateChanged在fabric.js中实现撤消/重做 [英] Using stateProperties and hasStateChanged to implement undo / redo in fabric.js

查看:95
本文介绍了使用stateProperties和hasStateChanged在fabric.js中实现撤消/重做的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑:此问题的范围有所变化.请在下面查看更新.

The scope of this question has shifted a bit. Please see updates below.

我一直在为fabric.js进行各种撤消/重做操作(请参阅 fiddle ).尽管很幼稚,但它在大多数情况下都起作用(用于添加/删除/移动或调整单个对象的大小或添加/删除对象的组),但不适用于移动或调整对象组的大小.在小提琴中,请注意onObjectSelected()函数的控制台输出.

I've been working on an undo/redo of sorts for fabric.js (see fiddle). Though rather naive, it's working for the most part (for adding / removing / moving or resizing individual objects or adding / removing groups of objects), but not for moving or resizing groups of objects. In the fiddle, note console output from onObjectSelected() function.

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

To see what i mean, draw some objects on the fiddle canvas, and move or resize them individually. Undo / redo works as expected. However, when groups are selected and moved, i can't see how to retrieve the updated positions of the objects within the group, which means the undo / redo can't restore the correct values.

// 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(),但这并没有帮助.因此,如果有人能发现我在做什么错,或有解决此问题的想法,我将不胜感激.

The reason seems to be that object positions / scales are not updated when moved or resized as a group. I've tried various things, including running setCoords() for all objects, but that has not helped. So i'd appreciate if someone can spot what i'm doing wrong, or has an idea how to work around this.

为了阐明这一点,这只是一个普通的撤消/重做系统,使用两个数组在用户修改画布时在画布上存储对象的属性.单击撤消或重做时,属性将应用回画布上的对象.时遇到的问题是,当的被选择的对象,并随后修改,我不知道如何捕捉某些性质,例如该组中的对象的位置,因为属性当出于某种原因将对象修改为一个组时,该组中的对象中的所有对象都不会更新.

To clarify, this is just a normal undo / redo system using two arrays to store properties for objects on the canvas as the user modifies them. When undo or redo is clicked, the properties are applied back to the object on the canvas. The problem i'm having is that when groups of objects are selected and subsequently modified, i don't know how to capture certain properties, such as the positions of the objects in the group, because the properties of the objects within the group are not updated when the objects are modified as a group for some reason.

更新:我了解到,当对象位于用户选择的组中时,相对于其所在的组(而不是画布)记录对象位置,这很有意义.因此,在处理组中的对象时, add 是组左/上坐标的左/上坐标,以计算对象相对于画布的坐标. scaleX/Y应乘以scaleX/Y组.

Update: I've learned that when objects are in user selected groups, the object position is recorded relative to the group it's in, and no longer the canvas, which makes sense. So when dealing with an object in a group, add it's left/top coord to the group left/top coord to calculate the coord of the object relative to the canvas. scaleX/Y should be multiplied by group scaleX/Y.

更新:我已设法使它执行我想要的操作(请参阅新的小提琴).从那以后,我就了解了fabric的有状态"标志,stateProperties数组和hasStateChanged方法,这些方法可以简化事情.这些文档非常稀疏,到目前为止,我在此解决方案中使用它们的工作尚未成功.使用技巧或使用这些技巧的示例代码将不胜感激.具体来说,我希望能够控制要保存的属性,因为我不需要保存所有可变属性.

Update: I've mananaged to get this to do what i want (see new fiddle). I've since learned about fabric's 'stateful' flag, stateProperties array and hasStateChanged method, which would simplify things. The documentation for these is sparse and i've not been successful thus far in using them within this solution. Usage tips or example code using these would be appreciated. Specifically, I would like to be able to control which properties are saved, since i don't need to save all changeable properties.

onObjectSelected当前看起来像这样:

onObjectSelected looks like this at present:

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
      });
    }

  }

};

推荐答案

Fabricjs默认不支持自动缩放或定位.支持此功能的唯一方法是实施自己的缩放/定位机制.我也尝试了很多事情,但这些行目前效果最好.

Fabricjs does not support auto-scaling or positioning by default. The only way to support this is to implement your own scaling/positioning mechanisms. I also tried many things but these lines work best at this moment.

因此,基本上,请在您的js中添加以下功能:

So basically, please add these functions in your 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
    };
}

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

I used 2 custom variables here where _cv is the simplified structure that is arranged to store width and height of the target canvas, and, _ratio is for each objects (constant) ratio value.

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

And use them wisely when you create an object or resize canvas:

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);

这看起来有点长,但实际上非常简单明了.我很确定这将立即在您的身边工作.希望这会有所帮助.

This looks a little bit long but very simple and clear solution in fact. I'm pretty sure this will work immediately in your side. Hope this helps.

这篇关于使用stateProperties和hasStateChanged在fabric.js中实现撤消/重做的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆