Fabric JS 2.4.1 How to use clipPath to crop image that has been scaled smaller or larger then 1:1 Ratio

橙三吉。 提交于 2021-02-11 14:27:51

问题


I have been trying to figure out what I am doing wrong when calculation the position and size of the mask rectangle once the target image has been resized.

Below is the documentation from fabric JS:

clipPath :fabric.Object a fabricObject that, without stroke define a clipping area with their shape. filled in black the clipPath object gets used when the object has rendered, and the context is placed in the center of the object cacheCanvas. If you want 0,0 of a clipPath to align with an object center, use clipPath.originX/Y to 'center' Type: fabric.Object Source: fabric.js, line 12991

The code I have works perfectly when the image is not resized (scale 1:1 X & Y). In the code's function named rescaleMask I attempt to position the mask to a zero center X & Y and when I run my math manually on a piece of graph paper it appears the math is correct. Obviously there is a piece that I am unaware of that is causing the positioning to be off in different ways depending on the quadrant in which the crop is being performed. There is quite a bit of code here but it is important that the mask is created dynamically and not hard coded. The problem must be in the rescaleMask function so hopefully the rest of the code can be ignored.

I created a test image graph with numbered squares which I will crop by clicking the mask button, drawing a rectangle around one of the boxes with the mouse left button and then clicking the crop button. The problem occurs when you resize the image before creating the mask and cropping.

Here is the test image:

Here is a jsfiddle fabric Creating rect with a mouse dynamic js 2.4.1 sent as fix #4

<canvas id="c" width="500" height="500" style="border:1px solid #ccc"></canvas>
<button id="mask">Mask</button>
<button id="crop">Crop</button>

JS

var lastSelectedPicture = null;
var isInsertingCropRectangle = false;
var canvas = new fabric.Canvas('c', {
  selection: true,
  preserveObjectStacking: true,
  height: 700,
  width: 800
});

var crop_rect, isDown, origX, origY, mask, target;
var done = false;

var src = "https://stealth-apsvaw.streamhoster.com/fabric_js_2_4_1_crop_test/graph_paper_540.png";
fabric.Image.fromURL(src, function(img) {
  img.selectable = true;
  img.id = 'target';
  img.top = 30;
  img.left = 30;
  canvas.add(img);
});

canvas.on('object:added', function(e) {
  target = null;
  mask = null;
  canvas.forEachObject(function(obj) {
    //alert(obj.get('id'));
    var id = obj.get('id');
    if (id === 'target') {
      target = obj;
     canvas.setActiveObject(obj);
   }
    if (id === 'mask') {
      //alert(done);
      //alert('mask');
      mask = obj;
    }
  });
});

canvas.on('object:modified', function(e) {
  e.target.setCoords();
  canvas.renderAll();
});

//////////////////////////////////////////////////////////
// MASK
//////////////////////////////////////////////////////////
document.getElementById("mask").addEventListener("click", function() {
  isInsertingCropRectangle = true;
    canvas.discardActiveObject();
    lastSelectedPicture.selectable = false;
    lastSelectedPicture.setCoords();
    lastSelectedPicture.dirty = true;
    canvas.renderAll();
    canvas.discardActiveObject();
    isInsertingCropRectangle = true;
});

//////////////////////////////////////////////////////////
// CROP
//////////////////////////////////////////////////////////
document.getElementById("crop").addEventListener("click", function() {
  if (target !== null && mask !== null) {
    target.setCoords();
    // Re-scale mask
    mask = rescaleMask(target, mask);
    mask.setCoords();

    // Do the crop
    target.clipPath = mask;

    target.dirty=true;
    canvas.setActiveObject(target);
    canvas.bringToFront(target);
    target.selectable = true;
    canvas.remove(mask);
    canvas.renderAll();
    console.log(target);
  }
});

//////////////////////////////////////////////////////////
// RE-SCALE MASK FOR CROPPING
// P R O B L E M  I N  T H I S  F U N C T I O N
//////////////////////////////////////////////////////////
function rescaleMask(target, mask){
  mask.scaleX = 1;
  mask.scaleY = 1;
  var targetCenterX = target.width * target.scaleX / 2;
  var targetCenterY = target.height * target.scaleY / 2;
  var maskOverlapX = mask.left - target.left;
  var maskOverlapY = mask.top - target.top;
  var centerBasedX = maskOverlapX - targetCenterX;
  var centerBasedY = maskOverlapY - targetCenterY;

  if( maskOverlapX >= targetCenterX){
    centerBasedX = maskOverlapX - targetCenterX;
  }
  else{
    centerBasedX = -(targetCenterX) + maskOverlapX;
  }

  if( maskOverlapY >= targetCenterY){
    centerBasedY = maskOverlapY - targetCenterY;
  }
  else{
    centerBasedY = -(targetCenterY) + maskOverlapY;
  }

  console.log('targetleft = '+target.left);
  console.log('targettop = '+target.top);
  console.log('targetCenterX = '+targetCenterX);
  console.log('targetCenterY = '+targetCenterY);
  console.log('maskleft = '+mask.left);
  console.log('masktop = '+mask.top);
  console.log('maskOverlapX = '+maskOverlapX);
  console.log('maskOverlapY = '+maskOverlapY);
  console.log('centerBasedX = '+centerBasedX);
  console.log('centerBasedY = '+centerBasedY);

  mask.left = centerBasedX;
  mask.top = centerBasedY;
  mask.originX = 'left';
  mask.originY = 'top';

  mask.setCoords();
  mask.dirty=true;
  canvas.renderAll();



  //var newMask = mask;
  return(mask);
}

canvas.on('mouse:down', function(o) {
  if( isInsertingCropRectangle == true ){
    console.log('mouse down done = '+done);
    if (done) {
      canvas.renderAll();
      return;
    }
    isDown = true;
    var pointer = canvas.getPointer(o.e);
    origX = pointer.x;
    origY = pointer.y;
    crop_rect = new fabric.Rect({
      left: origX,
      top: origY,
      width: pointer.x - origX,
      height: pointer.y - origY,
      opacity: .3,
      transparentCorners: false,
      selectable: true,
      id: 'mask'
    });
    canvas.add(crop_rect);
    canvas.renderAll();
  }
  else{

  }
});

canvas.on('mouse:move', function(o) {
  if( isInsertingCropRectangle == true ){
    console.log('mouse move done = '+done);
    if (done) {
      canvas.renderAll();
      return;
    }
    if (!isDown) return;
    var pointer = canvas.getPointer(o.e);

    if (origX > pointer.x) {
      crop_rect.set({
        left: Math.abs(pointer.x)
      });
    }
    if (origY > pointer.y) {
      crop_rect.set({
        top: Math.abs(pointer.y)
      });
    }

    crop_rect.set({
      width: Math.abs(origX - pointer.x)
    });
    crop_rect.set({
      height: Math.abs(origY - pointer.y)
    });


    crop_rect.setCoords();
    canvas.renderAll();
  }
  else{

  }
});

canvas.on('mouse:up', function(o) {
  if( isInsertingCropRectangle == true ){
    console.log('mouse up done = '+done);
    if (done) {
      canvas.renderAll();
      return;
    }
    isDown = false;

    crop_rect.set({
      selectable: true
    });
    done = true;
  }
  else{

  }
});

  canvas.on('selection:created', function(event) {
    console.log("canvas.on('selection:created'");
    selectionChanged(event);
  });

  canvas.on('selection:updated', function(event) {
    console.log("canvas.on('selection:updated'");
    selectionChanged(event);
  });

  function selectionChanged(event){
    console.log("selectionChanged");
    console.log("selectionChanged type = "+event.target.type);
    switch(event.target.type) {
      case 'textbox':
        break;
      case 'image':
        lastSelectedPicture = event.target;
        break;
      case 'rect':
        break;
      case 'group':
        break;
      default:
        break;
    }

  }

回答1:


You need to take in consideration the target.scaleX and target.scaleY for mask.

var	lastSelectedPicture = null;
var isInsertingCropRectangle = false;
canvas = new fabric.Canvas('c', {
  selection: true,
  preserveObjectStacking: true,
  height: 700,
  width: 800
});

var crop_rect, isDown, origX, origY, mask, target;
var done = false;

var src = "https://stealth-apsvaw.streamhoster.com/fabric_js_2_4_1_crop_test/graph_paper_540.png";
fabric.Image.fromURL(src, function(img) {
  img.selectable = true;
  img.id = 'target';
  img.top = 30;
  img.left = 30;
  canvas.add(img);
});

canvas.on('object:added', function(e) {
  target = null;
  mask = null;
  canvas.forEachObject(function(obj) {
    //alert(obj.get('id'));
    var id = obj.get('id');
    if (id === 'target') {
      target = obj;
	   canvas.setActiveObject(obj);
   }
    if (id === 'mask') {
      //alert(done);
      //alert('mask');
      mask = obj;
    }
  });
});

canvas.on('object:modified', function(e) {
  e.target.setCoords();
  canvas.renderAll();
});

//////////////////////////////////////////////////////////
// MASK
//////////////////////////////////////////////////////////
document.getElementById("mask").addEventListener("click", function() {
	isInsertingCropRectangle = true;
		canvas.discardActiveObject();
		lastSelectedPicture.selectable = false;
		lastSelectedPicture.setCoords();
		lastSelectedPicture.dirty = true;
		canvas.renderAll();
		canvas.discardActiveObject();
		isInsertingCropRectangle = true;
});

//////////////////////////////////////////////////////////
// CROP
//////////////////////////////////////////////////////////
document.getElementById("crop").addEventListener("click", function() {
  if (target !== null && mask !== null) {
    target.setCoords();
		// Re-scale mask
    mask = rescaleMask(target, mask);
    mask.setCoords();
    
    // Do the crop
    target.clipPath = mask;
    
    target.dirty=true;
    canvas.setActiveObject(target);
    canvas.bringToFront(target);
    target.selectable = true;
    canvas.remove(mask);
    canvas.renderAll();
    console.log(target);
  }
});

//////////////////////////////////////////////////////////
// RE-SCALE MASK FOR CROPPING
// P R O B L E M  I N  T H I S  F U N C T I O N
//////////////////////////////////////////////////////////
function rescaleMask(target, mask){
  mask.scaleX = 1;
  mask.scaleY = 1;

  mask.scaleX/=target.scaleX;
  mask.scaleY/=target.scaleY;
 
  var targetCenterX = target.width * target.scaleX / 2;
	var targetCenterY = target.height * target.scaleY / 2;

  var maskOverlapX = mask.left  - target.left;
  var maskOverlapY = mask.top - target.top;
	var centerBasedX = maskOverlapX - targetCenterX;
	var centerBasedY = maskOverlapY - targetCenterY;

  if( maskOverlapX >= targetCenterX){
  	centerBasedX = (maskOverlapX - targetCenterX)/target.scaleX;
  }
  else{
 
  	centerBasedX = (-(targetCenterX) + maskOverlapX)/target.scaleX;
  }

  if( maskOverlapY >= targetCenterY){
  	centerBasedY = (maskOverlapY - targetCenterY)/target.scaleY;
  }
  else{
  	centerBasedY = (-(targetCenterY) + maskOverlapY)/target.scaleY;
  }
  
 
  
  
  console.log('targetleft = '+target.left);
  console.log('targettop = '+target.top);
  console.log('targetCenterX = '+targetCenterX);
  console.log('targetCenterY = '+targetCenterY);
  console.log('maskleft = '+mask.left);
  console.log('masktop = '+mask.top);
	console.log('maskOverlapX = '+maskOverlapX);
	console.log('maskOverlapY = '+maskOverlapY);
  console.log('centerBasedX = '+centerBasedX);
  console.log('centerBasedY = '+centerBasedY);

  mask.left = centerBasedX;
  mask.top = centerBasedY;
  mask.originX = 'left';
  mask.originY = 'top';
	
  mask.setCoords();
  mask.dirty=true;
  canvas.renderAll();
  
  
  
  //var newMask = mask;
  return(mask);
}

canvas.on('mouse:down', function(o) {
	if( isInsertingCropRectangle == true ){
    console.log('mouse down done = '+done);
    if (done) {
      canvas.renderAll();
      return;
    }
    isDown = true;
    var pointer = canvas.getPointer(o.e);
    origX = pointer.x;
    origY = pointer.y;
    crop_rect = new fabric.Rect({
      left: origX,
      top: origY,
      width: pointer.x - origX,
      height: pointer.y - origY,
      opacity: .3,
      transparentCorners: false,
      selectable: true,
      id: 'mask'
    });
    canvas.add(crop_rect);
    canvas.renderAll();
  }
  else{
  
  }
});

canvas.on('mouse:move', function(o) {
	if( isInsertingCropRectangle == true ){
    console.log('mouse move done = '+done);
    if (done) {
      canvas.renderAll();
      return;
    }
    if (!isDown) return;
    var pointer = canvas.getPointer(o.e);

    if (origX > pointer.x) {
      crop_rect.set({
        left: Math.abs(pointer.x)
      });
    }
    if (origY > pointer.y) {
      crop_rect.set({
        top: Math.abs(pointer.y)
      });
    }

    crop_rect.set({
      width: Math.abs(origX - pointer.x)
    });
    crop_rect.set({
      height: Math.abs(origY - pointer.y)
    });


    crop_rect.setCoords();
    canvas.renderAll();
  }
  else{
  
  }
});

canvas.on('mouse:up', function(o) {
	if( isInsertingCropRectangle == true ){
    console.log('mouse up done = '+done);
    if (done) {
      canvas.renderAll();
      return;
    }
    isDown = false;

    crop_rect.set({
      selectable: true
    });
    done = true;
  }
  else{
  
  }
});

	canvas.on('selection:created', function(event) {
		console.log("canvas.on('selection:created'");
		selectionChanged(event);
	});
	
	canvas.on('selection:updated', function(event) {
		console.log("canvas.on('selection:updated'");
		selectionChanged(event);
	});

	function selectionChanged(event){
		console.log("selectionChanged");
		console.log("selectionChanged type = "+event.target.type);
		switch(event.target.type) {
			case 'textbox':
				break;
			case 'image':
				lastSelectedPicture = event.target;
				break;
			case 'rect':
				break;
			case 'group':
				break;
			default:
				break;
		}
		
	}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.1/fabric.min.js"></script>
<canvas id="c" width="500" height="500" style="border:1px solid #ccc"></canvas>
<button id="mask">Mask</button>
<button id="crop">Crop</button>


来源:https://stackoverflow.com/questions/52712035/fabric-js-2-4-1-how-to-use-clippath-to-crop-image-that-has-been-scaled-smaller-o

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!