在fabric js中进行缩放时保持线条宽度

17
注意:我已经参考了SO问题,但对我的情况没有用,因为
1)我正在尝试保持以前的边框,但现在在缩放时重新计算边框。
我已添加以下代码以停止在缩放对象时自动增加边框。现在的问题是,我已将5像素的边框添加到对象上,但在缩放对象时它不会维护之前添加的边框。
canvas.on('object:scaling', (e) => {
  var o = e.target;
  if (!o.strokeWidthUnscaled && o.strokeWidth) {
    o.strokeWidthUnscaled = o.strokeWidth;
  }

  if (o.strokeWidthUnscaled) {
    o.strokeWidth = o.strokeWidthUnscaled / o.scaleX;
  }
});

现在我想要的是在缩放对象时防止边框增加。边框应该保持原样。
这里是片段/Codepen

var canvas = new fabric.Canvas('canvas1');

$('.add_shape').click(function() {
  var cur_value = $(this).attr('data-rel');
  if (cur_value != '') {
    switch (cur_value) {
      case 'rectangle':
        var rect = new fabric.Rect({
          left: 50,
          top: 50,
          fill: '#aaa',
          width: 50,
          height: 50,
          opacity: 1,
          stroke: '#000',
          strokeWidth: 1
        });
        canvas.add(rect);
        canvas.setActiveObject(rect);
        break;
      case 'circle':
        var circle = new fabric.Circle({
          left: 50,
          top: 50,
          fill: '#aaa',
          radius: 50,
          opacity: 1,
          stroke: '#000',
          strokeWidth: 1
        });
        canvas.add(circle);
        canvas.setActiveObject(circle);
        break;
    }
  }
});

canvas.on('object:scaling', (e) => {
  var o = e.target;
  if (!o.strokeWidthUnscaled && o.strokeWidth) {
    o.strokeWidthUnscaled = o.strokeWidth;
  }
  if (o.strokeWidthUnscaled) {
    o.strokeWidth = o.strokeWidthUnscaled / o.scaleX;
  }
});

/* Control the border  */
$('#control_border').change(function() {
  var cur_value = parseInt($(this).val());
  var activeObj = canvas.getActiveObject();
  if (activeObj == undefined) {
    alert('Please select the Object');
    return false;
  }
  activeObj.set({
    strokeWidth: cur_value
  });
  canvas.renderAll();
});
button {
  max-resolution: 10px;
  height: 30px;
}

div {
  margin: 10px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.0.0-beta.7/fabric.js"></script>

<div>
  <button class="add_shape" data-rel="circle">Add Circle</button>

  <button class="add_shape" data-rel="rectangle">Add Rectangle</button>

  <label class="control-label">Border</label>
  <input id="control_border" type="range" min="0" max="10" step="1" value="0" />
</div>

<canvas id="canvas1" width="600" height="300" style="border:1px solid #000000;"></canvas>

步骤

1) 添加矩形
2) 应用边框(假设为5)
3) 缩放该对象

现在您会发现应用的边框已经消失了。所以如何解决这个问题呢?

更新

我已尝试以下选项,但并不起作用,基本上我正在尝试维护像矩形、圆、三角形、线段、多边形之类的对象的strokeWidth/Border。

到目前为止,我尝试过:

//1st try
canvas.on('object:scaling', (e) => {
    var o = e.target;
    o.strokeWidth = o.strokeWidth / ((o.scaleX + o.scaleY) / 2);
    var activeObject = canvas.getActiveObject();
    activeObject.set('strokeWidth',o.strokeWidth);
});

//2nd try
canvas.on('object:scaling', (e) => {
    if (!o.strokeWidthUnscaled && o.strokeWidth) {
        o.strokeWidthUnscaled = o.strokeWidth;
    }
    if (o.strokeWidthUnscaled) {
        o.strokeWidth = o.strokeWidthUnscaled / o.scaleX;
    }
});

//3rd try
fabric.Object.prototype._renderStroke = function(ctx) {
    if (!this.stroke || this.strokeWidth === 0) {
        return;
    }
    if (this.shadow && !this.shadow.affectStroke) {
        this._removeShadow(ctx);
    }
    ctx.save();
    ctx.scale(1 / this.scaleX, 1 / this.scaleY);
    this._setLineDash(ctx, this.strokeDashArray, this._renderDashedStroke);
    this._applyPatternGradientTransform(ctx, this.stroke);
    ctx.stroke();
    ctx.restore();
};

我参考的问题:

https://github.com/kangax/fabric.js/issues/66

在Fabricjs中,当处理组(Group)的情况下调整大小时无法保持strokeWidth的厚度

Fabricjs如何缩放对象但保持边框(stroke)宽度不变

调整fabricjs矩形以保持边框大小

https://github.com/kangax/fabric.js/issues/2012

但是无法找到解决方案。


1
activeObj.set({strokeWidth:cur_value,strokeWidthUnscaled:null}); 使对象的strokeWidthUnscaled为null,这样在缩放时它将被重置。 - Durga
@Durga,有没有办法确定我们选择的是组还是其他物体?我的意思是,如果我们选择单个对象,则它不是组,但如果它具有多个元素,则是组。 - DS9
你好,我将尝试向您展示如何使用覆盖标准方法完成此操作。可能会遇到一些奇怪的边缘情况,但应该不错。 - AndreaBogazzi
@AndreaBogazzi 会等待您的回答。 - DS9
我也遇到了这个问题。笔画被重置为原始笔画宽度。 - Mayur Kukadiya
显示剩余7条评论
5个回答

15

从 Fabric.js 版本 2.7.0 开始,这变得更加容易了。现在有一个 strokeUniform 属性,当启用时,可以防止对象的缩放值影响描边宽度。

obj.set('strokeUniform', true);

https://github.com/fabricjs/fabric.js/pull/5546

var canvas = new fabric.Canvas('canvas1');

$('.add_shape').click(function() {
  var cur_value = $(this).attr('data-rel');
  if (cur_value != '') {
    switch (cur_value) {
      case 'rectangle':
        var rect = new fabric.Rect({
          left: 50,
          top: 50,
          fill: '#aaa',
          width: 50,
          height: 50,
          opacity: 1,
          stroke: '#000',
          strokeWidth: 1,
          noScaleCache: false,
          strokeUniform: true,
        });
        canvas.add(rect);
        canvas.setActiveObject(rect);
        break;
      case 'circle':
        var circle = new fabric.Circle({
          left: 50,
          top: 50,
          fill: '#aaa',
          radius: 50,
          opacity: 1,
          stroke: '#000',
          strokeWidth: 1,
          noScaleCache: false,
          strokeUniform: true
        });
        canvas.add(circle);
        canvas.setActiveObject(circle);
        break;
    }
  }
});

/* Control the border  */
$('#control_border').change(function() {
  var cur_value = parseInt($(this).val());
  var activeObj = canvas.getActiveObject();
  if (activeObj == undefined) {
    alert('Please select the Object');
    return false;
  }
  activeObj.set({
    strokeWidth: cur_value
  });
  canvas.renderAll();
});
button {
  max-resolution: 10px;
  height: 30px;
}

div {
  margin: 10px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.7.0/fabric.min.js"></script>

<div>
  <button class="add_shape" data-rel="circle">Add Circle</button>
  <button class="add_shape" data-rel="rectangle">Add Rectangle</button>
  <label class="control-label">Border</label>
  <input id="control_border" type="range" min="0" max="10" step="1" value="0" />
</div>

<canvas id="canvas1" width="600" height="300" style="border:1px solid #000000;"></canvas>


这在文本或itext对象上不起作用。 - Shmack

7

第一次将strokeWidth设置为strokeWidthUnscaled,然后在缩放时设置@,为了缓存,在修改后只有在缩放后才将其设置为false。

object.strokeWidth = object.strokeWidthUnscaled/((object.scaleX+object.scaleY)/2);

选择新的笔画宽度,请执行以下操作:
var newStrokeWidth = cur_value / ((activeObj.scaleX + activeObj.scaleY) / 2)
activeObj.set({
  strokeWidth: newStrokeWidth,
  strokeWidthUnscaled : cur_value
});

例子

var canvas = new fabric.Canvas('canvas1');
var globalStrokeWidth = 1;

$('.add_shape').click(function() {
  var cur_value = $(this).attr('data-rel');
  if (cur_value != '') {
    switch (cur_value) {
      case 'rectangle':
        var rect = new fabric.Rect({
          left: 50,
          top: 50,
          fill: '#aaa',
          width: 50,
          height: 50,
          opacity: 1,
          stroke: '#000',
          strokeWidth: globalStrokeWidth
        });
        canvas.add(rect);
        canvas.setActiveObject(rect);
        break;
      case 'circle':
        var circle = new fabric.Circle({
          left: 50,
          top: 50,
          fill: '#aaa',
          radius: 50,
          opacity: 1,
          stroke: '#000',
          strokeWidth: globalStrokeWidth
        });
        canvas.add(circle);
        canvas.setActiveObject(circle);
        break;
    }
  }
});

canvas.on('object:scaling', (e) => {
  var o = e.target;
  if (!o.strokeWidthUnscaled && o.strokeWidth) {
    o.strokeWidthUnscaled = o.strokeWidth;
  }
  if (o.strokeWidthUnscaled) {
    if(o.objectCaching) o.objectCaching = false;
    o.strokeWidth = o.strokeWidthUnscaled / ((o.scaleX + o.scaleY) / 2);
  }
});

canvas.on('object:modified', (e) => {
  var o = e.target;
  if(!o.objectCaching) o.objectCaching = true;
  canvas.renderAll();
});

/* Control the border  */
$('#control_border').change(function() {
  var cur_value = parseInt($(this).val());
  globalStrokeWidth = cur_value;
  var activeObj = canvas.getActiveObject();
  if (activeObj == undefined) {
    alert('Please select the Object');
    return false;
  }
  var newStrokeWidth = cur_value / ((activeObj.scaleX + activeObj.scaleY) / 2)
  activeObj.set({
    strokeWidth: newStrokeWidth,
    strokeWidthUnscaled : cur_value
  });
  activeObj.setCoords();
  canvas.renderAll();
});
button {
  max-resolution: 10px;
  height: 30px;
}

div {
  margin: 10px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.0.0-beta.7/fabric.js"></script>

<div>
  <button class="add_shape" data-rel="circle">Add Circle</button>

  <button class="add_shape" data-rel="rectangle">Add Rectangle</button>

  <label class="control-label">Border</label>
  <input id="control_border" type="range" min="0" max="10" step="1" value="0" />
</div>

<canvas id="canvas1" width="600" height="300" style="border:1px solid #000000;"></canvas>


除了以下情况,它在所有情况下都有效:添加对象后,使用(顶部/底部/左侧/右侧)标记缩放对象(即不成比例地缩放(即从正方形制作矩形))。在这种情况下,边框会出错。 - DS9
@DS9 雅,我明白了,得等安德烈的回答 ;) - Durga
好的,我会等待 :)。 - DS9
为什么这个功能不直接内置在text和itext中呢?文档中的strokeUniform说明了它不能与itext和text一起使用(以及特定的方法/函数)。 - Shmack

2
在处理这个问题时,我注意到调整对象大小后,宽度和高度的值并没有改变,只有scaleX和scaleY发生了变化。为了保持描边宽度不变,需要将scaleX和scaleY重置为1,并用新的值更新宽度和高度。
对于矩形:
var currObj = canvas.getActiveObject();
if (currObj.type === 'rect') {
  var newWidth = currObj.width * currObj.scaleX,
      newHeight = currObj.height * currObj.scaleY;

    currObj.set({
      'width': newWidth,
      'height': newHeight,
      scaleX: 1,
      scaleY: 1
    });
}

对于椭圆形:

最初的回答:

var currObj = canvas.getActiveObject();
if (currObj.type === 'ellipse') {
  var newRX = currObj.rx * currObj.scaleX,
      newRY = currObj.ry * currObj.scaleY;

  currObj.set({
    'rx': newRX,
    'ry': newRY,
    scaleX: 1,
    scaleY: 1
    });
}

请别忘记后面要调用canvas.renderAll()方法;

http://jsfiddle.net/eLoamgrq/5/


1
在对象:缩放中,我注释掉了基于比例计算的部分。由于未缩放的值为1,并且被缩放除以后,边框总是等于一个分数(而不是保持原样)。

var canvas = new fabric.Canvas('canvas1');

$('.add_shape').click(function() {
  var cur_value = $(this).attr('data-rel');
  if (cur_value != '') {
    switch (cur_value) {
      case 'rectangle':
        var rect = new fabric.Rect({
          left: 50,
          top: 50,
          fill: '#aaa',
          width: 50,
          height: 50,
          opacity: 1,
          stroke: '#000',
          strokeWidth: 1
        });
        canvas.add(rect);
        canvas.setActiveObject(rect);
        break;
      case 'circle':
        var circle = new fabric.Circle({
          left: 50,
          top: 50,
          fill: '#aaa',
          radius: 50,
          opacity: 1,
          stroke: '#000',
          strokeWidth: 1
        });
        canvas.add(circle);
        canvas.setActiveObject(circle);
        break;
    }
  }
});

canvas.on('object:scaling', (e) => {
  var o = e.target;
  if (!o.strokeWidthUnscaled && o.strokeWidth) {
    o.strokeWidthUnscaled = o.strokeWidth;
  }
  if (o.strokeWidthUnscaled) {
    //o.strokeWidth = o.strokeWidthUnscaled / o.scaleX;
  }
});

/* Control the border  */
$('#control_border').change(function() {
  var cur_value = parseInt($(this).val());
  var activeObj = canvas.getActiveObject();
  if (activeObj == undefined) {
    alert('Please select the Object');
    return false;
  }
  activeObj.set({
    strokeWidth: cur_value
  });
  canvas.renderAll();
});
button {
  max-resolution: 10px;
  height: 30px;
}

div {
  margin: 10px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.0.0-beta.7/fabric.js"></script>

<div>
  <button class="add_shape" data-rel="circle">Add Circle</button>

  <button class="add_shape" data-rel="rectangle">Add Rectangle</button>

  <label class="control-label">Border</label>
  <input id="control_border" type="range" min="0" max="10" step="1" value="0" />
</div>

<canvas id="canvas1" width="600" height="300" style="border:1px solid #000000;"></canvas>


1
这不是一个答案,因为它根本没有保持strokeWidth。 - Durga
检查开发者工具。如果将描边宽度设置为4,然后调整大小,则描边宽度保持在4。它不会重置为小于1的分数。除非我误解了要求,否则这确实保持了设置的描边宽度。 - Ken Hansen
在缩放时,请检查对象的strokeWidth,它会发生变化。用户希望保持与缩放之前相同的strokeWidth,而不仅仅是其值。 - Durga

0

如果您的对象在组内,strokeUniform属性将不起作用。在这种情况下,您可以减少内部对象的缩放并在缩放时更新其宽度和高度属性。例如:

object.on('scaling', function() {
     
    object.item(0).set('scaleX', 1/object.scaleX);
    object.item(0).set('scaleY', 1/object.scaleY);
    var newobjwidth = Math.round(object.width * object.scaleX);
    var newobjheight = Math.round(object.height * object.scaleY);
    object.item(0).set('width', newobjwidth);
    object.item(0).set('height', newobjheight);
    object.item(0).setCoords();
 
}

如果您的对象模糊,请在缩放后将其删除并重新添加。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接