19

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 キャンバスの複数のクリッピング エリア

あるクリッピング領域の画像が別のクリッピング領域に現れた場合。これを回避するにはどうすればよいですか、またはファブリック js を使用してこれを達成する別の方法があります。

4

7 に答える 7

32

これはプロパティを使用して Fabric で実現できますclipToが、関数で変換 (スケールと回転) を「反転」する必要がありclipToます。

clipToFabric でプロパティを使用すると、クリッピングのにスケーリングと回転が適用されます。つまり、クリッピングは画像と共にスケーリングおよび回転されます。プロパティ関数で変換の正確なを適用することで、これに対抗する必要があります。clipTo

私の解決策にはFabric.Rect、クリップ領域の「プレースホルダー」として機能することが含まれます (Fabric を使用してオブジェクトを移動し、クリップ領域を移動できるため、これには利点があります。

私のソリューションは、特に(コンテキストについてはコードを参照) のために、 Lo-Dashユーティリティ ライブラリを使用していることに注意してください。_.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これらのオブジェクトに name プロパティを与えclipForて、clipTo関数がクリッピングしたいオブジェクトを見つけられるようにします。

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

クリップ領域に実際のオブジェクトが存在する必要はありません、Fabric を使用して移動できるため、管理が容易になります。

3. クリッピング機能

clipToコードの重複を避けるために、画像のプロパティで個別に使用される関数を定義します。

Image オブジェクトのangleプロパティは度単位で格納されているため、これを使用してラジアンに変換します。

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

findByClipName()は、 Lo-Dashを使用して、切り取られる Image オブジェクトのプロパティを持つ を検索する便利な関数です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 = 'https://fabricjs.com/lib/pug.jpg';

何をし_.bind()ますか?

参照は_.bind()関数内でラップされていることに注意してください。

_.bind()以下の2つの理由で使用しています。

  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/
于 2013-06-14T04:30:34.280 に答える
11

クリップ領域の配置が正しく配置されておらず、回転時にすべて不安定だったため、@natchiketa によってソリューションを微調整しました。しかし、今はすべてが良いようです。この変更されたフィドルをチェックしてください: https://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つの小さなキャッチ:

  1. クリッピング オブジェクトにストロークを追加すると、数ピクセルずれているように見えます。位置を補正しようとしましたが、回転すると、下と右側に 2 ピクセルが追加されます。というわけで、完全に削除することにしました。
  2. たまに画像を回転させると、クリッピングのランダムな側面に 1 ピクセルの間隔ができてしまいます。
于 2014-07-22T20:54:59.550 に答える
8

@Promlnc の回答に更新します。

適切なクリッピングを実行するには、コンテキスト変換の順序を置き換える必要があります。

  1. 翻訳
  2. スケーリング
  3. 回転

そうしないと、縦横比を維持せずに (1 つの寸法のみを変更して) スケーリングすると、オブジェクトが誤って切り取られてしまいます。

悪い

コード (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));

これを参照してください: https://jsfiddle.net/ZxYCP/185/

適切な結果:

良い

更新 1:

ポリゴンでクリップする機能を開発しました: https://jsfiddle.net/ZxYCP/198/

ポリ

于 2015-09-14T03:40:41.060 に答える
1

の最新のアップデートではfabric 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));

デモ: https://jsfiddle.net/uimos/bntepzLL/5/

于 2015-10-29T16:11:03.453 に答える
-1

以前の人の回答に更新します。

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

これで、間違いなくクリッピング エリアをスケーリングできるようになりました。

于 2016-10-27T14:41:20.440 に答える