在这个jsFiddle中,我有一个包含矩形和文本框的 Fabric 组。我需要能够在不缩放文本的情况下缩放矩形,因此我尝试在选择组时取消分组,并在清除选择时再次分组。另外:
如何让jsFiddle工作?
注意
文本和矩形始终一起移动,即使在任何选择之前或之后也是如此。
var canvas = window._canvas = new fabric.Canvas('c');
var text = new fabric.Textbox("Some text", {
width: 100,
height: 22,
fontSize: 12,
editable: true
});
var rect = new fabric.Rect({
width: 100,
height: 22,
fill: 'yellow'
});
var group = new fabric.Group([ rect, text ], {
left: 30,
top: 30
});
canvas.add(group);
group.on('selected', function (e){
canvas.remove(group);
canvas.add(rect);
canvas.add(text);
canvas.renderAll();
canvas.setActiveObject(rect);
});
canvas.on('selection:cleared', function(e) {
group = new fabric.Group([ rect, text ], {});
});
编辑:OP澄清了一些要求,因此对解决方案进行了相应的编辑。
虽然分组乍一看似乎是一个好主意,但如果您仔细考虑一下,您的文本/矩形组合所需的唯一分组功能就是能够一起移动。这意味着创建一个组并尝试禁用所有不需要的功能实际上比将文本粘合到矩形并仅处理您关心的事件更困难。
Fabric.js 有一个很棒的 subclassing 机制,我们将用它来扩展
fabric.Rect
类。
下面的代码几乎是不言自明的,我只注意几个关键方面:
rectOptions
构造函数中的
textOptions
和 fabric.RectWithText
是您通常传递到 fabric.Rect
和 fabric.Textbox
构造函数中的对象。Textbox
由 text
实例的 RectWithText
属性引用。recalcTextPosition
使用几个三角公式来计算文本相对于矩形的位置,给定两者之间的初始偏移量。moving
、scaling
和 rotating
事件以顺利地重新计算文本的位置。mousedown:before
、mousedblclick
、editing:exited
确保双击时文本保持可编辑状态。canvas.preserveObjectStacking
将文本保持在顶部。const canvas = new fabric.Canvas('c')
fabric.RectWithText = fabric.util.createClass(fabric.Rect, {
type: 'rectWithText',
text: null,
textOffsetLeft: 0,
textOffsetTop: 0,
_prevObjectStacking: null,
_prevAngle: 0,
recalcTextPosition: function () {
const sin = Math.sin(fabric.util.degreesToRadians(this.angle))
const cos = Math.cos(fabric.util.degreesToRadians(this.angle))
const newTop = sin * this.textOffsetLeft + cos * this.textOffsetTop
const newLeft = cos * this.textOffsetLeft - sin * this.textOffsetTop
const rectLeftTop = this.getPointByOrigin('left', 'top')
this.text.set('left', rectLeftTop.x + newLeft)
this.text.set('top', rectLeftTop.y + newTop)
},
initialize: function (rectOptions, textOptions, text) {
this.callSuper('initialize', rectOptions)
this.text = new fabric.Textbox(text, {
...textOptions,
selectable: false,
evented: false,
})
this.textOffsetLeft = this.text.left - this.left
this.textOffsetTop = this.text.top - this.top
this.on('moving', () => {
this.recalcTextPosition()
})
this.on('rotating', () => {
this.text.rotate(this.text.angle + this.angle - this._prevAngle)
this.recalcTextPosition()
this._prevAngle = this.angle
})
this.on('scaling', (e) => {
this.recalcTextPosition()
})
this.on('added', () => {
this.canvas.add(this.text)
})
this.on('removed', () => {
this.canvas.remove(this.text)
})
this.on('mousedown:before', () => {
this._prevObjectStacking = this.canvas.preserveObjectStacking
this.canvas.preserveObjectStacking = true
})
this.on('mousedblclick', () => {
this.text.selectable = true
this.text.evented = true
this.canvas.setActiveObject(this.text)
this.text.enterEditing()
this.selectable = false
})
this.on('deselected', () => {
this.canvas.preserveObjectStacking = this._prevObjectStacking
})
this.text.on('editing:exited', () => {
this.text.selectable = false
this.text.evented = false
this.selectable = true
})
}
})
const rectOptions = {
left: 10,
top: 10,
width: 200,
height: 75,
fill: 'rgba(30, 30, 30, 0.3)',
}
const textOptions = {
left: 35,
top: 30,
width: 150,
fill: 'white',
shadow: new fabric.Shadow({
color: 'rgba(34, 34, 100, 0.4)',
blur: 2,
offsetX: -2,
offsetY: 2
}),
fontSize: 30,
}
const rectWithText = new fabric.RectWithText(rectOptions, textOptions, 'Some text')
canvas.add(rectWithText)
body {
background: ivory;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.4.0/fabric.js"></script>
<canvas id="c" width="300" height="200"></canvas>