For making Photo Collage Maker, I use fabric js which has object based clipping feature. This feature is great but image inside that clipping region cannot be scaled, moved or rotated. I want fixed position clipping region and image can be positioned inside the fixed clipping area as user want.
I googled and find very near solution
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();
Multiple Clipping Areas on fabric js canvas
where image of one clipping region is appeared in another clipping region. How can I avoid this or is there another way of accomplishing this using fabric js.
这可以通过使用光纤来实现clipTo
财产,但你必须“反向”的变换(缩放和旋转),在clipTo
功能。
当您使用clipTo
在织物性能,缩放和旋转限幅之后应用,这意味着剪辑被缩放和与图像旋转。 你必须通过应用的变换的完全相反对付这个clipTo
属性功能。
我的解决方案涉及到一Fabric.Rect
作为剪辑区域的“占位符”(这方面具有优势,因为你可以用布周围,因此移动物体剪辑区域。
请注意,我的解决方案使用罗短跑工具库,特别是_.bind()
见上下文代码)。
例如小提琴
分解
1.初始化面料
首先,我们希望我们的画布,当然:
var canvas = new fabric.Canvas('c');
2.剪辑区域
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
对象的名称属性, clipFor
,所以clipTo
功能可以找到他们想要修剪的一个:
clipRect1.set({
clipFor: 'pug'
});
canvas.add(clipRect1);
有没有成为该截取区域内的实际的对象,但它可以更容易管理,因为你可以把它走动用面料。
3.裁剪功能
我们定义这将通过将图像中使用的功能clipTo
属性分开,以避免重复代码:
由于angle
的图像对象的属性存储在度,我们将使用此将其转换为弧度。
function degToRad(degrees) {
return degrees * (Math.PI / 180);
}
findByClipName()
是一个方便的功能,这是使用螺短跑 ,以找到与clipFor
属性为图像物体被裁剪(例如,下面的图像中, 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
在上面的功能。
4. 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 = 'http://fabricjs.com/lib/pug.jpg';
是什么_.bind()
吗?
注意,参考被包裹在所述_.bind()
函数。
我使用_.bind()
用于以下两个原因:
- 我们需要一个引用传递
Image
对象clipByName()
- 该
clipTo
属性传递画布背景下,而不是对象。
基本上, _.bind()
您可以创建一个版本,使用您指定为对象的函数的this
方面。
Sources
- 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/
这可以更容易地完成。 织物提供了渲染方法被另一个对象上下文剪辑。
结帐这个小提琴 。 我看到这个的注释在这里 。
obj.clipTo = function(ctx) {
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
clippingRect.render(ctx);
ctx.restore();
};
正如我所测试的所有小提琴上面他们有一个错误。 这是当你将翻转X和Y值加在一起,剪裁边界将是错误的。 此外,为了不这样做的所有计算用于放置图片到合适的位置,你需要指定originX =“中心”和originY =为他们“中心”。
下面是一个剪辑功能更新从@natchiketa原码
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);
//logic for correct scaling
if (this.getFlipY() && !this.getFlipX()){
ctx.scale(scaleXTo1, -scaleYTo1);
} else if (this.getFlipX() && !this.getFlipY()){
ctx.scale(-scaleXTo1, scaleYTo1);
} else if (this.getFlipX() && this.getFlipY()){
ctx.scale(-scaleXTo1, -scaleYTo1);
} else {
ctx.scale(scaleXTo1, scaleYTo1);
}
//IMPORTANT!!! do rotation after scaling
ctx.rotate(degToRad(this.angle * -1));
ctx.beginPath();
ctx.rect(
clipRect.left - this.left,
clipRect.top - this.top,
clipRect.width,
clipRect.height
);
ctx.closePath();
ctx.restore();
}
请检查更新小提琴
随着面料1.6.0-RC.1最新的更新,您可以通过扭曲保持迁移的图像并拖动中间轴。
我对如何扭转倾斜,使裁剪区域停留了同样的麻烦。 我曾尝试下面的代码,试图扭转回来,但没有奏效。
var skewXReverse = - this.skewX;
var skewYReverse = - this.skewY;
ctx.translate( ctxLeft, ctxTop );
ctx.scale(scaleXTo1, scaleYTo1);
ctx.transform(1, skewXReverse, skewYReverse, 1, 0, 0);
ctx.rotate(degToRad(this.angle * -1));
演示: http://jsfiddle.net/uimos/bntepzLL/5/
更新以前的人的答案。
ctx.rect(
clipRect.oCoords.tl.x - this.oCoords.tl.x - clipRect.strokeWidth,
clipRect.oCoords.tl.y - this.oCoords.tl.y - clipRect.strokeWidth,
clipRect.oCoords.tr.x - clipRect.oCoords.tl.x,
clipRect.oCoords.bl.y - clipRect.oCoords.tl.y
);
现在我们能够扩展裁剪区域毫无疑问。