获得容纳盒的标准方法?

Lee*_*lor 5 javascript fabricjs

好的,如果我旋转然后选择以下形状,您将看到它们的边界框: 在此输入图像描述

我正在尝试编写一些代码来使对象相互对齐。所以我想得到每个对象的“包含框”。

我知道getBoundingRect,但是对于上述形状,这给了我以下结果: 在此输入图像描述

因此,这些盒子对我来说没什么用。是否有一种标准方法来获取所有形状的“包含框”?例如,我希望能够将以下盒子退还给我: 在此输入图像描述

因此,对于任何给定的形状,我希望能够获得红色边界矩形(不旋转)。

显然,我可以在 FabricJS 中为每种可能的形状编写一个例程,但我不想重新发明轮子!有任何想法吗?

编辑这是一个交互式片段,显示当前的边界框(红色):

$(function () 
{
    canvas = new fabric.Canvas('c');

    canvas.add(new fabric.Triangle({
      left: 50,
      top: 50,
      fill: '#FF0000',
      width: 50,
      height: 50,
      angle : 30
    }));

    canvas.add(new fabric.Circle({
      left: 250,
      top: 50,
      fill: '#00ff00',
      radius: 50,
      angle : 30
    }));

    canvas.add(new fabric.Polygon([
      {x: 185, y: 0},
      {x: 250, y: 100},
      {x: 385, y: 170},
      {x: 0, y: 245} ], {
        left: 450,
        top: 50,
        fill: '#0000ff',
        angle : 30
      }));

    canvas.on("after:render", function(opt) 
    { 
        canvas.contextContainer.strokeStyle = '#FF0000';
        canvas.forEachObject(function(obj) 
        {
            var bound = obj.getBoundingRect();

            canvas.contextContainer.strokeRect(
                bound.left + 0.5,
                bound.top + 0.5,
                bound.width,
                bound.height
            );
        });
    });

    canvas.renderAll();
});
Run Code Online (Sandbox Code Playgroud)
<script src="//cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.6/fabric.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="c" width="800" height="600"></canvas><br/>
Run Code Online (Sandbox Code Playgroud)

Div*_*her 2

我遇到了同样的问题,并且找到了解决方法。我从活动对象创建了一个临时 SVG 并将其放置在视口之外。然后我使用本机getBBox()函数测量了真实的边界框。

更新

显然,上面的解决方案仅适用于 Firefox (76),所以我想出了一个不同的解决方案。由于我找不到正常工作的本机函数来获取形状的真实边界框,因此我决定扫描像素并从那里检索边界。

小提琴: https://jsfiddle.net/divpusher/2m7c61gw/118/

怎么运行的:

下面的演示

// ---------------------------
// the magic

var tempCanv, ctx, w, h;

function getImageData(dataUrl) { 
  // we need to use a temp canvas to get imagedata
  if (tempCanv == null) {
    tempCanv = document.createElement('canvas');
    tempCanv.style.border = '1px solid blue';
    tempCanv.style.visibility = 'hidden';
    ctx = tempCanv.getContext('2d');
  	document.body.appendChild(tempCanv);
	}
    
	return new Promise(function(resolve, reject) {
  	if (dataUrl == null) return reject();    
    
    var image = new Image();
    image.addEventListener('load', function() {
      w = image.width;
      h = image.height;
      tempCanv.width = w;
      tempCanv.height = h;
      ctx.drawImage(image, 0, 0, w, h);
      var imageData = ctx.getImageData(0, 0, w, h).data.buffer;         
			resolve(imageData, false);      
    });
    image.src = dataUrl;
	});
  
}

function scanPixels(imageData) {
	var data = new Uint32Array(imageData),
      len = data.length,
      x, y, y1, y2, x1 = w, x2 = 0;
  
  // y1
  for(y = 0; y < h; y++) {
    for(x = 0; x < w; x++) {
      if (data[y * w + x] & 0xff000000) {
        y1 = y;
        y = h;
        break;
      }
    }
  }
  
  // y2
  for(y = h - 1; y > y1; y--) {
    for(x = 0; x < w; x++) {
      if (data[y * w + x] & 0xff000000) {
        y2 = y;
        y = 0;
        break;
      }
    }
  }

  // x1
  for(y = y1; y < y2; y++) {
    for(x = 0; x < w; x++) {
      if (x < x1 && data[y * w + x] & 0xff000000) {
        x1 = x;
        break;
      }
    }
  }

  // x2
  for(y = y1; y < y2; y++) {
    for(x = w - 1; x > x1; x--) {
      if (x > x2 && data[y * w + x] & 0xff000000) {
        x2 = x;
        break;
      }
    }
  }
  
  return {
  	x1: x1,
    x2: x2,
    y1: y1,
    y2: y2
  }
}



// ---------------------------
// align buttons

function alignLeft(){
	var obj = canvas.getActiveObject();
  obj.set('left', 0);
  obj.setCoords();
  canvas.renderAll();
}


function alignLeftbyBoundRect(){
	var obj = canvas.getActiveObject();
  var bound = obj.getBoundingRect();
  obj.set('left', (obj.left - bound.left));
  obj.setCoords();
  canvas.renderAll();
}

function alignRealLeft(){
	var obj = canvas.getActiveObject();
  getImageData(obj.toDataURL())
  	.then(function(data) {    
    	var bound = obj.getBoundingRect();
    	var realBound = scanPixels(data);  
      obj.set('left', (obj.left - bound.left - realBound.x1));      
      obj.setCoords();
      canvas.renderAll(); 
    });
}


// ---------------------------
// set up canvas

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

var path = new fabric.Path('M 0 0 L 150 50 L 120 150 z');
path.set({
  left: 170,
  top: 30,
  fill: 'rgba(0, 128, 0, 0.5)',
  stroke: '#000',
  strokeWidth: 4,
  strokeLineCap: 'square',
  angle: 65
});
canvas.add(path);
canvas.setActiveObject(path);

var circle = new fabric.Circle({
  left: 370,
  top: 30,
  radius: 45,
  fill: 'blue',
  scaleX: 1.5,
  angle: 30
});
canvas.add(circle);


canvas.forEachObject(function(obj) {
  var setCoords = obj.setCoords.bind(obj);
  obj.on({
    moving: setCoords,
    scaling: setCoords,
    rotating: setCoords
  });
});


canvas.on('after:render', function() {
	canvas.contextContainer.strokeStyle = 'red';
  
	canvas.forEachObject(function(obj) {
      
		getImageData(obj.toDataURL())
  		.then(function(data) {    	
        var boundRect = obj.getBoundingRect();
        var realBound = scanPixels(data);                
        canvas.contextContainer.strokeRect(
					boundRect.left + realBound.x1, 
        	boundRect.top + realBound.y1, 
        	realBound.x2 - realBound.x1, 
        	realBound.y2 - realBound.y1
        );    
    });           

	});
});
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.3/fabric.min.js"></script>

<p>&nbsp;</p>
<button onclick="alignLeft()">align left (default)</button>&nbsp;&nbsp;&nbsp;
<button onclick="alignLeftbyBoundRect()">align left (by bounding rect)</button>&nbsp;&nbsp;&nbsp;
<button onclick="alignRealLeft()">align REAL left (by pixel)</button>&nbsp;&nbsp;&nbsp;
<p></p>
<canvas id="c" width="600" height="250" style="border: 1px solid rgb(204, 204, 204); touch-action: none; user-select: none;" class="lower-canvas"></canvas>
<p></p>
Run Code Online (Sandbox Code Playgroud)