Fabric.js - 分组 iText 不可编辑
Posted
技术标签:
【中文标题】Fabric.js - 分组 iText 不可编辑【英文标题】:Fabric.js - Grouped iText not editable 【发布时间】:2014-08-18 10:12:41 【问题描述】:标题说明了一切。当 fabric.IText
是 fabric.Group
的一部分时,它不再像可编辑文本那样做出反应。
这是一个用例:http://jsfiddle.net/vAjYd/
有没有办法解决这个问题?否则我必须写我的小组。
顺便说一句:fabric.Group
有很多优点,但是禁用的事件使得它无法用于 UI 元素(即这个用例或按钮组作为文本和矩形组)。
【问题讨论】:
你能分享你对这个问题的解决方案吗?我目前面临同样的问题,但似乎无法找到解决方案。我已经走到这一步了:http://pastebin.com/dQ742AvG 但无法弄清楚如何为 IText 启用编辑 fabricjs.com/docs/fabric.IText.html 不起作用.. 【参考方案1】:我回答这个问题可能为时已晚,但肯定有许多像我一样正在寻找这个答案的人会找到解决方案。 您可以找到一个完美的 iText 解决方案,用户可以在其中编辑 iText 的文本,它是 fabric.group 的一部分 Working solution
这里遵循的方法是:
-
当用户双击所需的组时,我们取消所有元素的组合
将焦点放在 iText 元素上
进入iText的编辑模式并选择所有文本,以便用户可以直接开始编辑..
一旦用户退出编辑模式,元素就会再次组合在一起。
// 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.7.1+ 版本。取消选择 IText 后标尺将无法恢复,并且控制台中显示以下错误:TypeError: this._objects[i] is undefined
.【参考方案2】:
我知道为时已晚,但这是解决您问题的有效代码: http://jsfiddle.net/fd4yrxq6/2/
我只是使用上面的 anwares 做了一些小改动:
...
// 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();
));
);
...
var canvas = new fabric.Canvas('canvas');
canvas.setWidth(300);
canvas.setHeight(300);
// Double-click event handler
var fabricDblClick = function (obj, handler)
return function ()
if (obj.clicked) handler(obj);
else
obj.clicked = true;
setTimeout(function ()
obj.clicked = false;
, 500);
;
;
// 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();
;
// 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 ()
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();
));
);
function addRuler()
var dimension_mark = new fabric.Path('M0,0L0,-5L0,5L0,0L150,0L150,-5L150,5L150,0z');
dimension_mark.set(
left: 150,
top: 70,
stroke: '#333333',
strokeWidth: 2,
scaleY: 1
);
var dimension_group = new fabric.Group([dimension_mark, dimensionText],
left: 50,
top: 50
);
canvas.add(dimension_group);
dimension_group.on('mousedown', fabricDblClick(dimension_group, function (obj)
ungroup(dimension_group);
canvas.setActiveObject(dimensionText);
dimensionText.enterEditing();
dimensionText.selectAll();
canvas.renderAll();
));
addRuler();
style =
left: 100,
top: 100,
width: 20,
height: 20,
fill: "green",
lockRotation: true
;
var rectangle = new fabric.Polygon([
x: 20, y: 0 ,
x: 0, y: 20 ,
x: 20, y: 40 ,
x: 40, y: 20 ,
], style);
canvas.add(rectangle);
.canvas
border: 1px solid Black;
width: 300px;
height: 300px;
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.2.0/fabric.min.js"></script>
<p id="addObj">Fabric.js - iText editing from Group</p>
<canvas id="canvas" class="canvas" ></canvas>
【讨论】:
【参考方案3】:感谢您的回答,但是它不能确保只有组中的项目被重新组合,我们必须将 '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();
));
);
【讨论】:
【参考方案4】:我是最后一个答案,但更好的答案, 一个gif演示值一千字
如您所见,效果很好,链接地址https://jsfiddle.net/hejie/rhf069ob/latest
解决方案:
-
创建一个组
双击群组,隐藏
textbox inside group
创建一个临时文本框进行编辑,与textbox inside group
的位置相同
在临时文本框上编辑后,将新文本提交到textbox inside group
,并删除临时文本框
完成。
为什么这个解决方案比group/ungroup
更好?
-
ungroup => editing => group,会创建新的 group 实例,原来的 group 实例被销毁,这对你的对象状态管理是非常有害的
旋转组后很难编辑,因为文本框 insdie 组也会旋转。
简而言之,不要使用分组/取消分组解决方案! 试试下面的代码:
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>
【讨论】:
【参考方案5】:目前 IText 无法处理这些事件,因为如果它包含在一个组中,它们就不会传递给它。我认为这也是处理该问题的首选方式,因为它会使用户感到困惑——如果他开始编辑多个文本怎么办。这最终可能会一团糟。也许你可以稍微修改你的脚本来解决这个问题。
【讨论】:
很抱歉,这不是一个答案。 “尝试解决方法”?举个例子或有帮助的东西怎么样?请看下面的答案 - 它有一个解决方法,甚至是一个工作示例! 我同意多个文本,但这不仅仅是一个案例 - 仍然需要功能,例如,当我们要将文本与圆圈相关联时【参考方案6】:如果您想要 itext 的真实视觉填充(如 css),您可以在 itext 后面添加一个不可选择的 Rect。并根据 itext 的所有事件调整 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 ));
;
【讨论】:
你能举一个你的解决方案的例子吗? @soundslikeodd 我已经附上了例子,你可以看看以上是关于Fabric.js - 分组 iText 不可编辑的主要内容,如果未能解决你的问题,请参考以下文章