为了制作Photo Collage Maker,我使用具有基于对象剪辑功能的fabric js.此功能很棒但是剪切区域内的图像无法缩放,移动或旋转.我希望固定位置剪切区域和图像可以根据用户需要定位在固定剪切区域内.
我用Google搜索并找到非常接近的解决方案
var canvas = new fabric.Canvas('c'); var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.rect(10,10,150,150); ctx.rect(180,10,200,200); ctx.closePath(); ctx.stroke(); ctx.clip();
fabric js canvas上的多个剪辑区域
其中一个剪辑区域的图像出现在另一个剪辑区域中.我怎样才能避免这种情况,或者是否有另一种使用结构js实现此目的的方法.
这可以使用Fabric使用clipTo
属性来完成,但是您必须在clipTo
函数中"反转"转换(缩放和旋转).
clipTo
在Fabric中使用该属性时,剪切后会应用缩放和旋转,这意味着剪裁会随图像缩放并旋转.您必须通过在属性函数中应用完全相反的转换来对此进行反击clipTo
.
我的解决方案涉及将Fabric.Rect
服务作为剪辑区域的"占位符"(这具有优势,因为您可以使用Fabric来移动对象,从而移动剪辑区域.
请注意,我的解决方案使用Lo-Dash实用程序库,尤其适用于_.bind()
(请参阅上下文代码).
首先,我们想要我们的画布,当然:
var canvas = new fabric.Canvas('c');
var clipRect1 = new fabric.Rect({
originX: 'left',
originY: 'top',
left: 180,
top: 10,
width: 200,
height: 200,
fill: 'none',
stroke: 'black',
strokeWidth: 2,
selectable: false
});
我们为这些Rect
对象提供了一个name属性,clipFor
因此clipTo
函数可以找到它们想要剪切的对象:
clipRect1.set({
clipFor: 'pug'
});
canvas.add(clipRect1);
剪辑区域不必具有实际对象,但它使管理更容易,因为您可以使用Fabric移动它.
我们分别定义图像clipTo
属性将使用的函数,以避免代码重复:
由于angle
Image对象的属性以度为单位存储,我们将使用它将其转换为弧度.
function degToRad(degrees) {
return degrees * (Math.PI / 180);
}
findByClipName()
是一个便利函数,它使用Lo-Dash来查找具有clipFor
要剪裁的Image对象的属性(例如,在下图中,name
将是'pug'
):
function findByClipName(name) {
return _(canvas.getObjects()).where({
clipFor: name
}).first()
}
这是完成工作的部分:
var clipByName = function (ctx) {
var clipRect = findByClipName(this.clipName);
var scaleXTo1 = (1 / this.scaleX);
var scaleYTo1 = (1 / this.scaleY);
ctx.save();
ctx.translate(0,0);
ctx.rotate(degToRad(this.angle * -1));
ctx.scale(scaleXTo1, scaleYTo1);
ctx.beginPath();
ctx.rect(
clipRect.left - this.left,
clipRect.top - this.top,
clipRect.width,
clipRect.height
);
ctx.closePath();
ctx.restore();
}
注意:请参阅下文,了解this
上述功能的使用说明.
fabric.Image
使用对象clipByName()
最后,可以实例化图像并使其使用如下clipByName
函数:
var pugImg = new Image();
pugImg.onload = function (img) {
var pug = new fabric.Image(pugImg, {
angle: 45,
width: 500,
height: 500,
left: 230,
top: 170,
scaleX: 0.3,
scaleY: 0.3,
clipName: 'pug',
clipTo: function(ctx) {
return _.bind(clipByName, pug)(ctx)
}
});
canvas.add(pug);
};
pugImg.src = 'https://fabricjs.com/lib/pug.jpg';
_.bind()
办?请注意,引用包含在_.bind()
函数中.
我正在使用_.bind()
以下两个原因:
我们需要传递一个引用Image
对象clipByName()
该clipTo
属性传递画布上下文,而不是对象.
基本上,_.bind()
允许您创建使用您指定的对象作为this
上下文的函数版本.
http://lodash.com/docs#bind
http://fabricjs.com/docs/fabric.Object.html#clipTo
http://html5.litten.com/understanding-save-and-restore-for-the-canvas-context/
我已经调整了@natchiketa的解决方案,因为剪辑区域的定位没有正确定位并且在旋转时都是不稳定的.但现在一切似乎都很好.看看这个修改过的小提琴:http: //jsfiddle.net/PromInc/ZxYCP/
唯一真正的更改是在@natchiketa提供的代码的第3步的clibByName函数中进行的.这是更新的功能:
var clipByName = function (ctx) { this.setCoords(); var clipRect = findByClipName(this.clipName); var scaleXTo1 = (1 / this.scaleX); var scaleYTo1 = (1 / this.scaleY); ctx.save(); var ctxLeft = -( this.width / 2 ) + clipRect.strokeWidth; var ctxTop = -( this.height / 2 ) + clipRect.strokeWidth; var ctxWidth = clipRect.width - clipRect.strokeWidth + 1; var ctxHeight = clipRect.height - clipRect.strokeWidth + 1; ctx.translate( ctxLeft, ctxTop ); ctx.rotate(degToRad(this.angle * -1)); ctx.scale(scaleXTo1, scaleYTo1); ctx.beginPath(); ctx.rect( clipRect.left - this.oCoords.tl.x, clipRect.top - this.oCoords.tl.y, ctxWidth, ctxHeight ); ctx.closePath(); ctx.restore(); }
我找到两个小捕获:
向剪辑对象添加笔划似乎会使事情偏离几个像素.我试图补偿定位,但是在旋转时它会在底部和右侧增加2个像素.所以我选择完全删除它.
偶尔旋转图像时,它会在剪辑的随机边上以1px的间距结束.
更新到Promlnc的答案.
您需要替换上下文转换的顺序才能执行正确的裁剪.
翻译
缩放
回转
否则,您将错误地剪切对象 - 当您在不保持纵横比(仅更改一个维度)的情况下进行缩放.
代码(69-72):
ctx.translate( ctxLeft, ctxTop ); ctx.rotate(degToRad(this.angle * -1)); ctx.scale(scaleXTo1, scaleYTo1);
替换为:
ctx.translate( ctxLeft, ctxTop ); ctx.scale(scaleXTo1, scaleYTo1); ctx.rotate(degToRad(this.angle * -1));
见:http: //jsfiddle.net/ZxYCP/185/
正确的结果:
更新1:
我开发了多边形剪辑功能. http://jsfiddle.net/ZxYCP/198/