Fabric JS 2.4.1 如何使用 clipPath 裁剪已缩放为小于或大于 1:1 比例的图像
Posted
技术标签:
【中文标题】Fabric JS 2.4.1 如何使用 clipPath 裁剪已缩放为小于或大于 1:1 比例的图像【英文标题】:Fabric JS 2.4.1 How to use clipPath to crop image that has been scaled smaller or larger then 1:1 Ratio 【发布时间】:2019-03-13 16:49:46 【问题描述】:在调整目标图像的大小后,在计算蒙版矩形的位置和大小时,我一直试图找出我做错了什么。
下面是fabric JS的文档:
clipPath :fabric.Object 一个fabricObject,没有描边,用它们的形状定义一个剪裁区域。当对象被渲染时,clipPath 对象被用黑色填充,并且上下文被放置在对象 cacheCanvas 的中心。如果您希望 clipPath 的 0,0 与对象中心对齐,请使用 clipPath.originX/Y 到 'center' 类型: 织物.对象 资源: fabric.js,第 12991 行
当图像未调整大小(比例为 1:1 X 和 Y)时,我的代码可以完美运行。在名为 rescaleMask 的代码函数中,我尝试将掩码定位到零中心 X 和 Y,当我在一张方格纸上手动运行我的数学时,它似乎数学是正确的。显然,我不知道有一件事情会导致定位以不同的方式关闭,具体取决于执行裁剪的象限。这里有很多代码,但重要的是掩码是动态创建的,而不是硬编码的。问题一定出在 rescaleMask 函数中,因此希望可以忽略其余代码。
我创建了一个带有编号的正方形的测试图像图,我将通过单击蒙版按钮来裁剪它,用鼠标左键在其中一个框周围绘制一个矩形,然后单击裁剪按钮。在创建蒙版和裁剪之前调整图像大小时会出现此问题。
这是测试图片:
这是一个 jsfiddle fabric Creating rect with a mouse dynamic js 2.4.1 sent as fix #4
<canvas id="c" 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】:您需要考虑target.scaleX
和target.scaleY
作为掩码。
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" style="border:1px solid #ccc"></canvas>
<button id="mask">Mask</button>
<button id="crop">Crop</button>
【讨论】:
完美地解决了它。我在不同的地方玩过 mask.scaleXY 和 target.scaleXY ,但我的结果没有看到任何变化,因为我将它们部署在错误的地方。谢谢你说得这么清楚,马吕斯。我相信这也会帮助其他人理解。以上是关于Fabric JS 2.4.1 如何使用 clipPath 裁剪已缩放为小于或大于 1:1 比例的图像的主要内容,如果未能解决你的问题,请参考以下文章