Fabric.js - 分组的iText不可编辑

时间:2014-06-27 10:20:15

标签: fabricjs

标题说明了所有。当fabric.IText成为fabric.Group的一部分时,它不再像可编辑的文字那样做出反应。

以下是一个用例:http://jsfiddle.net/vAjYd/

有没有办法解决这个问题?否则我必须写上我的小组。

顺便说一下:fabric.Group有许多优点,但是禁用事件使得无法用于UI元素(即这个用例或按钮组作为文本和组的组)。

6 个答案:

答案 0 :(得分:18)

我可能来不及回答这个问题,但肯定很多人喜欢我搜索这个答案应该符合解决方案。 你可以找到一个完美的iText解决方案,用户可以编辑iText的文本,这是fabric.group的一部分。 Working solution

此处遵循的方法是:

  1. 当用户双击所需的组时,我们将所有元素取消组合
  2. 专注于iText元素
  3. 进入iText&的编辑模式选择所有文本,以便用户可以直接开始编辑..
  4. 用户退出编辑模式后,元素将再次组合在一起。

    // ungroup objects in group
    var items
    var ungroup = function (group) {
        items = group._objects;
        group._restoreObjectsState();
        canvas.remove(group);
        for (var i = 0; i < items.length; i++) {
            canvas.add(items[i]);
        }
        // if you have disabled render on addition
        canvas.renderAll();
    };
    
    // Re-group when text editing finishes
    var dimensionText = new fabric.IText("Dimension Text", {
        fontFamily: 'Comic Sans',
        fontSize: 14,
        stroke: '#000',
        strokeWidth: 1,
        fill: "#000",
        left: 170,
        top: 60
    });
    dimensionText.on('editing:exited', function () {
        var items = [];
        canvas.forEachObject(function (obj) {
            items.push(obj);
            canvas.remove(obj);
        });
        var grp = new fabric.Group(items.reverse(), {});
        canvas.add(grp);
        grp.on('mousedown', fabricDblClick(grp, function (obj) {
            ungroup(grp);
            canvas.setActiveObject(dimensionText);
            dimensionText.enterEditing();
            dimensionText.selectAll();
        }));
    });
    

答案 1 :(得分:0)

目前,IText无法处理这些事件,因为如果它们包含在一个组中,它们就不会传递给它。我认为这也是处理它的首选方式,因为它会让用户感到困惑 - 如果他开始编辑多个文本会怎样。这可能最终会变得一团糟。也许你可以稍微修改你的脚本来解决这个问题。

答案 2 :(得分:0)

感谢您的回答,但是不能确保只重新组合组中的项目,我们必须将“ canvas.forEachObject ”更改为“ groupItems ”因为它会尝试将画布中的所有项目仅放回属于该组的所有项目。请参阅以下代码

// ungroup objects in group
var groupItems = []
var ungroup = function (group) {
    groupItems = group._objects;
    group._restoreObjectsState();
    canvas.remove(group);
    for (var i = 0; i < groupItems.length; i++) {
        canvas.add(groupItems[i]);
    }
    // if you have disabled render on addition
    canvas.renderAll();
};

// Re-group when text editing finishes
var dimensionText = new fabric.IText("Dimension Text", {
    fontFamily: 'Comic Sans',
    fontSize: 14,
    stroke: '#000',
    strokeWidth: 1,
    fill: "#000",
    left: 170,
    top: 60
});
dimensionText.on('editing:exited', function () {
    var items = [];
    groupItems.forEach(function (obj) {
        items.push(obj);
        canvas.remove(obj);
    });
    var grp = new fabric.Group(items.reverse(), {});
    canvas.add(grp);
    grp.on('mousedown', fabricDblClick(grp, function (obj) {
        ungroup(grp);
        canvas.setActiveObject(dimensionText);
        dimensionText.enterEditing();
        dimensionText.selectAll();
    }));
});

答案 3 :(得分:0)

如果您希望为itext提供真实的视觉填充(如css),则可以在itext后面添加一个不可选择的Rect。并根据所有事件(例如移动,缩放等)调整Rect位置。

self.createITextWithPadding = function (event) {

               var itext = new fabric.IText('Done', {
                   fontSize: 18,
                   padding : tnbConstants.ITEXT.PADDING,
                   fill: '#FFF',

               });

               itext.left = (event.pageX - $("#tnb-panel").position().left)-itext.width/2;
               itext.top = (event.pageY-$("#tnb-panel").position().top)-itext.height/2;

               var bg = new fabric.Rect({
                    fill: '#32b775',
                   left : itext.left - tnbConstants.ITEXT.PADDING,
                   top :itext.top - tnbConstants.ITEXT.PADDING,
                   rx: 5,
                   ry: 5,
                   width: itext.width + tnbConstants.ITEXT.PADDING*2,
                   height:itext.height + tnbConstants.ITEXT.PADDING*2,
                   selectable : false
               });

               itext.bgRect = bg;
               itext.on("moving",self.adjustBackRect);
               itext.on("scaling",self.adjustBackRect);

               return itext;
           }

self.adjustBackRect = function (e) {//e is event
               var text = e.target;
               var bgRect = text.bgRect;

               bgRect.set({
                   left : text.left - text.padding,
                   top : text.top - text.padding,
                   width : text.width * text.scaleX + text.padding * 2,
                   height : text.height * text.scaleY + text.padding * 2 ,
               });


               console.log("text width :" + (text.width * text.scaleX + text.padding*2));
               console.log("text height :" + (text.height * text.scaleY + text.padding*2));
               console.log("bg width :" + (bgRect.width ));
               console.log("bg height :" + (bgRect.height ));
           };

答案 4 :(得分:0)

我知道为时已晚,但这是您遇到的问题的有效代码: http://jsfiddle.net/fd4yrxq6/2/

我刚刚使用上述软件做了一些小改动:

...
// ungroup objects in group
var items;
var ungroup = function (group) {
    items = group._objects;
    group._restoreObjectsState();
    canvas.remove(group);
    canvas.renderAll();
    for (var i = 0; i < items.length; i++) {
        canvas.add(items[i]);
    }
    // if you have disabled render on addition
    canvas.renderAll();
};
...
dimensionText.on('editing:exited', function () {
        for (var i = 0; i < items.length; i++) {
        canvas.remove(items[i]);
    }
    var grp = new fabric.Group(items, {});
    canvas.add(grp);
    grp.on('mousedown', fabricDblClick(grp, function (obj) {
        ungroup(grp);
        canvas.setActiveObject(dimensionText);
        dimensionText.enterEditing();
        dimensionText.selectAll();
    }));
});
...

答案 5 :(得分:0)

我是最后一个答案,但更好的答案, 一个 gif 演示价值一千个字 enter image description here

如您所见,效果很好,链接地址 https://jsfiddle.net/hejie/rhf069ob/latest

解决方案

  1. 创建一个群组
  2. 双击该组,隐藏 textbox inside group
  3. 创建一个临时文本框进行编辑,位置与 textbox inside group 相同
  4. 编辑临时文本框后,将新文本提交到 textbox inside group,并删除临时文本框
  5. 完成。

为什么这个解决方案比 group/ungroup 更好?

  1. ungroup =>editing => group,会创建新的组实例,原来的组实例被破坏,这对你的对象状态管理非常有害
  2. 旋转组后很难编辑,因为文本框内组也旋转了。

简而言之,不要使用分组/取消分组的解决方案! 尝试以下代码:

var canvas = new fabric.Canvas('canvas');
canvas.setWidth(300);
canvas.setHeight(300);

function createGroup() {
    let circle = new fabric.Circle({
    radius:40,
    fill: 'rgba(200, 0, 0, 0.3)',
    originX: 'center',
    originY: 'center',
  });
  
  let text = new fabric.Textbox("AJLoveChina", {
    originX: 'center',
    originY: 'center',
    textAlign: 'center',
    fontSize: 12,
  })
  
  let group = new fabric.Group([circle, text], {
    left: 100,
    top: 100,
    originX: 'center',
    originY: 'center',
  });
  
  group.on('mousedblclick', () => {
    // textForEditing is temporary obj, 
    // and will be removed after editing
    let textForEditing = new fabric.Textbox(text.text, {
      originX: 'center',
      originY: 'center',
      textAlign: text.textAlign,
      fontSize: text.fontSize,
      
      left: group.left,
      top: group.top,
    })
    
    // hide group inside text
    text.visible = false;
    // note important, text cannot be hidden without this
    group.addWithUpdate();
    
    textForEditing.visible = true;
    // do not give controls, do not allow move/resize/rotation on this 
    textForEditing.hasConstrols = false;

    
    // now add this temporary obj to canvas
    canvas.add(textForEditing);
    canvas.setActiveObject(textForEditing);
    // make the cursor showing
    textForEditing.enterEditing();
    textForEditing.selectAll();
    
    
    // editing:exited means you click outside of the textForEditing
    textForEditing.on('editing:exited', () =>{
        let newVal = textForEditing.text;
      let oldVal = text.text;
      
      // then we check if text is changed
      if (newVal !== oldVal) {
        text.set({
            text: newVal,
          visible: true,
        })
        
        // comment before, you must call this
        group.addWithUpdate();
        
        // we do not need textForEditing anymore
        textForEditing.visible = false;
        canvas.remove(textForEditing);
        
        // optional, buf for better user experience
        canvas.setActiveObject(group);
      }
    })
  })
  
  canvas.add(group);
}

createGroup();
#canvas {
  width:300px;
  height:300px;
  border: 1px solid #333;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.4.0/fabric.min.js"></script>
<h4>fabricjs editing textbox inside fabric Group</h4>
<canvas id="canvas"></canvas>