当前位置:  开发笔记 > 编程语言 > 正文

Fabric.js画布上的多个剪裁区域

如何解决《Fabric.js画布上的多个剪裁区域》经验,为你挑选了3个好方法。

为了制作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实现此目的的方法.



1> natchiketa..:

这可以使用Fabric使用clipTo属性来完成,但是您必须在clipTo函数中"反转"转换(缩放和旋转).

clipTo在Fabric中使用该属性时,剪切会应用缩放和旋转,这意味着剪裁会随图像缩放并旋转.您必须通过在属性函数中应用完全相反的转换来对此进行反击clipTo.

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

请注意,我的解决方案使用Lo-Dash实用程序库,尤其适用于_.bind()(请参阅上下文代码).

示例小提琴
分解

1.初始化Fabric

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

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对象提供了一个name属性,clipFor因此clipTo函数可以找到它们想要剪切的对象:

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

剪辑区域不必具有实际对象,但它使管理更容易,因为您可以使用Fabric移动它.

3.剪切功能

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

由于angleImage对象的属性以度为单位存储,我们将使用它将其转换为弧度.

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上述功能的使用说明.

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 = '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/


小提琴需要更新(因为fabricjs的位置已经改变):http://jsfiddle.net/32Acb/

2> PromInc..:

我已经调整了@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的间距结束.



3> l00k..:

更新到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/

聚

推荐阅读
wurtjq
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有