调整织物矩形大小而不调整文本框大小

问题描述 投票:0回答:1

在这个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 ], {});
});
javascript fabricjs
1个回答
13
投票

编辑: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>

© www.soinside.com 2019 - 2024. All rights reserved.