首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Fabric.js画布上的多个剪贴区

Fabric.js画布上的多个剪贴区
EN

Stack Overflow用户
提问于 2013-05-08 17:57:30
回答 7查看 22.6K关注 0票数 19

为了制作Photo Collage Maker,我使用fabric js,它具有基于对象的裁剪功能。此功能很棒,但该裁剪区域内的图像不能缩放、移动或旋转。我想要一个固定位置的剪贴区,图像可以定位在固定剪贴区内的用户想要的。

我用谷歌搜索了一下,找到了非常接近的解决方案。

代码语言:javascript
运行
复制
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

其中一个剪切区域的图像已经出现在另一个剪切区域中。我如何避免这一点,或者有没有其他方法可以使用fabric js来实现这一点。

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2013-06-14 12:30:34

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

在Fabric中使用clipTo属性时,将在剪切之后应用缩放和旋转,这意味着剪切将随图像一起缩放和旋转。您必须通过应用与clipTo属性函数中的转换完全相反的方式来应对这种情况。

我的解决方案包括让一个Fabric.Rect作为剪辑区域的“占位符”(这有好处,因为你可以使用Fabric来移动对象,从而移动剪辑区域。

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

Example Fiddle

细目

1.初始化Fabric

首先,我们当然想要我们的画布:

代码语言:javascript
运行
复制
var canvas = new fabric.Canvas('c');

2.剪辑区域

代码语言:javascript
运行
复制
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函数就可以找到要裁剪的对象:

代码语言:javascript
运行
复制
clipRect1.set({
    clipFor: 'pug'
});
canvas.add(clipRect1);

剪辑区域不一定要有一个实际的对象,但它使它更容易管理,因为您可以使用Fabric移动它。

3.裁剪功能

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

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

代码语言:javascript
运行
复制
function degToRad(degrees) {
    return degrees * (Math.PI / 180);
}

findByClipName()是一个方便的函数,它使用Lo-Dash为要剪切的图像对象查找具有clipFor属性的(例如,在下图中,name将为'pug'):

代码语言:javascript
运行
复制
function findByClipName(name) {
    return _(canvas.getObjects()).where({
            clipFor: name
        }).first()
}

这是完成这项工作的部分:

代码语言:javascript
运行
复制
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.使用clipByName()fabric.Image对象

最后,可以实例化图像并使其使用clipByName函数,如下所示:

代码语言:javascript
运行
复制
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()有以下两个原因:

我们需要向clipByName()

  • The传递引用

  • 对象clipTo属性传递的是画布上下文,而不是对象。

基本上,_.bind()允许您创建一个使用您指定为this上下文的对象的函数版本。

资料来源

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

Stack Overflow用户

发布于 2014-07-23 04:55:00

我已经通过@natchiketa调整了解决方案,因为剪辑区域的定位不正确,并且在旋转时都不稳定。但现在一切似乎都很好。看看这个修改过的小提琴:https://jsfiddle.net/PromInc/ZxYCP/

唯一真正的更改是在@natchiketa提供的代码的步骤3的clibByName函数中进行的。下面是更新后的函数:

代码语言:javascript
运行
复制
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个像素。所以,我选择了在一段时间内删除它,当你旋转图像时,它将在剪辑中的随机边上以1px的间距结束。
票数 11
EN

Stack Overflow用户

发布于 2015-09-14 11:40:41

更新为@Promlnc答案。

您需要替换上下文转换的顺序,以便执行适当的裁剪。

  1. translation
  2. scaling
  3. rotation

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

代码(69-72):

代码语言:javascript
运行
复制
ctx.translate( ctxLeft, ctxTop );

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

替换为:

代码语言:javascript
运行
复制
ctx.translate( ctxLeft, ctxTop );
ctx.scale(scaleXTo1, scaleYTo1);
ctx.rotate(degToRad(this.angle * -1));

看看这个:https://jsfiddle.net/ZxYCP/185/

正确的结果:

更新1:

我开发了一个按多边形裁剪的功能:https://jsfiddle.net/ZxYCP/198/

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16437696

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档