Fabric.js 画布上的多个剪切区域 [英] Multiple clipping areas on Fabric.js canvas

查看:124
本文介绍了Fabric.js 画布上的多个剪切区域的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了制作Photo Collage Maker,我使用了具有基于对象的剪辑功能的fabric js.此功能很棒,但该剪切区域内的图像无法缩放、移动或旋转.我想要一个固定位置的剪切区域,并且可以根据用户的需要将图像定位在固定剪切区域内.

我用谷歌搜索并找到了非常接近的解决方案.

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

织物 js 画布上的多个剪切区域

其中一个剪辑区域的图像出现在另一个剪辑区域中.我怎样才能避免这种情况,或者是否有另一种方法可以使用fabric js来实现这一点.

解决方案

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

当您在Fabric 中使用clipTo 属性时,缩放和旋转在 剪辑之后应用,这意味着剪辑随图像一起缩放和旋转.您必须通过在 clipTo 属性函数中应用精确的 reverse 转换来解决此问题.

我的解决方案是让 Fabric.Rect 作为剪辑区域的占位符"(这样做有好处,因为您可以使用 Fabric 来移动对象,从而移动剪辑区域.p>

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

小提琴示例

<小时>

故障

1.初始化结构

首先,我们当然需要画布:

var canvas = new fabric.Canvas('c');

2.剪辑区域

var clipRect1 = new fabric.Rect({originX: '左',originY: '顶部',左:180,前10名,宽度:200,身高:200,填写:'无',中风:'黑色',行程宽度:2,可选:假});

我们给这些 Rect 对象一个名称属性,clipFor,所以 clipTo 函数可以找到它们想要被剪辑的对象:

clipRect1.set({剪辑:'哈巴狗'});canvas.add(clipRect1);

没有必须是剪辑区域的实际对象,但它更易于管理,因为您可以使用 Fabric 移动它.

3.剪辑功能

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

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

function degToRad(degrees) {返回度数 * (Math.PI/180);}

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

function findByClipName(name) {返回 _(canvas.getObjects()).where({剪辑:名称}).第一的()}

这是完成工作的部分:

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,剪辑矩形宽度,剪辑矩形高度);ctx.closePath();ctx.restore();}

注意:请参阅下文,了解在上述函数中使用 this 的说明.

4.fabric.Image 对象使用 clipByName()

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

var pugImg = new Image();pugImg.onload = 函数 (img) {var pug = new fabric.Image(pugImg, {角度:45,宽度:500,身高:500,左:230,最高:170,scaleX:0.3,比例尺:0.3,剪辑名称:'哈巴狗',剪辑到:函数(ctx){返回 _.bind(clipByName, pug)(ctx)}});canvas.add(哈巴狗);};pugImg.src = 'https://fabricjs.com/lib/pug.jpg';

_.bind() 是做什么的?

请注意,引用包含在 _.bind() 功能.

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

  1. 我们需要将一个引用 Image 对象传递给 clipByName()
  2. 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/

For making Photo Collage Maker, I use fabric js which has an object-based clipping feature. This feature is great but the image inside that clipping region cannot be scaled, moved or rotated. I want a fixed position clipping region and the image can be positioned inside the fixed clipping area as the 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 the image of one clipping region has appeared in another clipping region. How can I avoid this or is there another way of accomplishing this using fabric js.

解决方案

This can be accomplished with Fabric using the clipTo property, but you have to 'reverse' the transformations (scale and rotation), in the clipTo function.

When you use the clipTo property in Fabric, the scaling and rotation are applied after the clipping, which means that the clipping is scaled and rotated with the image. You have to counter this by applying the exact reverse of the transformations in the clipTo property function.

My solution involves having a Fabric.Rect serve as the 'placeholder' for the clip region (this has advantages because you can use Fabric to move the object around and thus the clip region.

Please note that my solution uses the Lo-Dash utility library, particularly for _.bind() (see code for context).

Example Fiddle


Breakdown

1. Initialize Fabric

First, we want our canvas, of course:

var canvas = new fabric.Canvas('c');

2. Clip Region

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

We give these Rect objects a name property, clipFor, so the clipTo functions can find the one by which they want to be clipped:

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

There doesn't have to be an actual object for the clip region, but it makes it easier to manage, as you're able to move it around using Fabric.

3. Clipping Function

We define the function which will be used by the images' clipTo properties separately to avoid code duplication:

Since the angle property of the Image object is stored in degrees, we'll use this to convert it to radians.

function degToRad(degrees) {
    return degrees * (Math.PI / 180);
}

findByClipName() is a convenience function, which is using Lo-Dash, to find the with the clipFor property for the Image object to be clipped (for example, in the image below, name will be 'pug'):

function findByClipName(name) {
    return _(canvas.getObjects()).where({
            clipFor: name
        }).first()
}

And this is the part that does the work:

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

NOTE: See below for an explanation of the use of this in the function above.

4. fabric.Image object using clipByName()

Finally, the image can be instantiated and made to use the clipByName function like this:

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

What does _.bind() do?

Note that the reference is wrapped in the _.bind() function.

I'm using _.bind() for the following two reasons:

  1. We need to pass a reference Image object to clipByName()
  2. The clipTo property is passed the canvas context, not the object.

Basically, _.bind() lets you create a version of the function that uses the object you specify as the this context.

Sources

  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/

这篇关于Fabric.js 画布上的多个剪切区域的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆