Fabric.jscanvas上的多个裁剪区域

为了制作照片拼贴制作 ,我使用具有基于对象的裁剪function的面料js。 该function非常好,但是剪辑区域内的图像不能缩放,移动或旋转。 我想要固定的位置剪辑区域和图像可以定位在用户想要的固定剪辑区域内。

我GOOGLE了,发现非常接近的解决scheme

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(); 

面料jscanvas上的多个裁剪区域

其中一个剪辑区域的图像出现在另一个剪辑区域中。 我怎样才能避免这种情况,或者是有另一种方法来完成这个使用结构js。

这可以通过使用clipTo属性的Fabric完成,但是您必须在clipTo函数中“反转”转换(缩放和旋转)。

在Fabric中使用clipTo属性时,缩放和旋转将裁剪之后应用,这意味着裁剪将与图像一起缩放和旋转。 您必须通过在clipTo属性函数中应用转换的完全相反来对此进行处理。

我的解决scheme涉及将Fabric.Rect用作剪辑区域的“占位符”(这具有优势,因为您可以使用Fabric来移动对象,从而剪辑区域。

请注意,我的解决scheme使用Lo-Dash实用程序库,特别是_.bind() (请参阅上下文的代码)。

示例小提琴


分解

1.初始化结构

首先,我们需要我们的canvas,当然:

 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函数可以find他们想要被剪裁的对象:

 clipRect1.set({ clipFor: 'pug' }); canvas.add(clipRect1); 

不必为剪辑区域设置实际的对象,但可以更轻松地进行pipe理,因为您可以使用Fabric来移动它。

3.裁剪function

我们定义了图像的clipTo属性分别使用的函数,以避免代码重复:

由于Image对象的angle属性是以度数存储的,我们将使用它来将其转换为弧度。

 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(); } 

注意在上面的function中,请参阅下面的说明和使用说明。

4.使用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 = 'lib/pug.jpg'; 

_.bind()做了什么?

请注意,引用被封装在_.bind()函数中。

我使用_.bind()有以下两个原因:

  1. 我们需要将一个引用的Image对象传递给clipByName()
  2. clipTo属性传递canvas上下文,而不是对象。

基本上, _.bind()让你创build一个使用你指定的对象作为this上下文的函数版本。

来源

  1. http://lodash.com/docs#bind
  2. http://fabricjs.com/docs/fabric.Object.html#clipTo
  3. http://html5.litten.com/understanding-save-and-restore-for-the-canvas-context/

我已经通过@natchiketa调整了解决scheme,因为剪辑区域的定位没有正确定位,并且在旋转的时候都是不可靠的。 但现在看起来都好了。 看看这个修改小提琴: http : //jsfiddle.net/PromInc/ZxYCP/

在@natchiketa提供的代码的第3步的clibByName函数中做了唯一真正的更改。 这是更新的function:

 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(); } 

我发现了两个小小的捕获物:

  1. 为剪切对象添加一个笔划看起来会把这些东西丢掉几个像素。 我试图补偿定位,但是在旋转时,它会在底部和右侧增加2个像素。 所以我select完全删除它。
  2. 偶尔当你旋转图像时,它将以裁剪中随机两侧的1px间距结束。

更新到Promlnc的答案。

您需要replace上下文转换顺序才能执行正确的剪辑。

  1. 翻译
  2. 缩放
  3. 回转

否则,你会得到错误的裁剪对象 – 当你缩放不保持纵横比(只改变一个维度)。

坏

代码(69-72):

 ctx.translate( ctxLeft, ctxTop ); ctx.rotate(degToRad(this.angle * -1)); ctx.scale(scaleXTo1, scaleYTo1); 

replace为:

 ctx.translate( ctxLeft, ctxTop ); ctx.scale(scaleXTo1, scaleYTo1); ctx.rotate(degToRad(this.angle * -1)); 

看到这个: http : //jsfiddle.net/ZxYCP/185/

正确的结果:

好

更新1:

我已经开发了多边形剪辑function。 http://jsfiddle.net/ZxYCP/198/

聚

这可以更容易地完成。 结构提供了渲染方法来由另一个对象上下文剪辑。

结帐这个小提琴 。 我在这里看到这个评论。

  obj.clipTo = function(ctx) { ctx.save(); ctx.setTransform(1, 0, 0, 1, 0, 0); clippingRect.render(ctx); ctx.restore(); }; 

使用面料1.6.0-rc.1的最新更新,您可以通过按住shift键并拖动中间轴来倾斜图像。

我有麻烦,如何扭转歪斜,使剪辑区域保持不变。 我试过下面的代码尝试将其撤消,但没有奏效。

 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 ); 

现在我们可以毫无疑问地扩大裁剪区域。

当我testing上面所有的小提琴时,他们有一个错误。 当您将X和Y值一起翻转时,裁剪边界将是错误的。 另外,为了不进行将图像放置到正确位置的所有计算,您需要为它们指定originX ='center'和originY ='center'。

这里是从@natchiketa更新原始代码的剪辑function

 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(); } 

请检查更新的小提琴

Interesting Posts