Photo Collage Maker を作成するために、オブジェクトベースのクリッピング機能を備えたファブリック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でclipTo
プロパティを使用して実行できますが、clipTo
関数で変換(スケールと回転)を「逆」にする必要があります。
FabricでclipTo
プロパティを使用すると、スケーリングと回転がクリッピングの後に適用されます。つまり、クリッピングは画像に合わせてスケーリングおよび回転されます。 clipTo
プロパティ関数の変換の正確なreverseを適用することにより、これに対抗する必要があります。
私の解決策は、_Fabric.Rect
_をクリップ領域の「プレースホルダー」として機能させることです(これには、Fabricを使用してオブジェクトを移動し、クリップ領域を移動できるため、利点があります。
私のソリューションでは、特に _.bind()
の場合に Lo-Dash ユーティリティライブラリを使用していることに注意してください(コンテキストについてはコードを参照してください)。
もちろん、最初にキャンバスが必要です。
_var canvas = new fabric.Canvas('c');
_
_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
関数は、クリップするオブジェクトを見つけることができます。
_clipRect1.set({
clipFor: 'pug'
});
canvas.add(clipRect1);
_
クリップ領域の実際のオブジェクトとなるhaveはありませんが、移動できるため、管理が容易になります。ファブリックを使用します。
コードの重複を避けるために、画像の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();
}
_
[〜#〜] note [〜#〜]:でのthis
の使用と説明については、以下を参照してください。上記の関数。
clipByName()
を使用した_fabric.Image
_オブジェクト最後に、イメージをインスタンス化して、次のように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 = 'http://fabricjs.com/lib/pug.jpg';
_
_.bind()
は何をしますか?参照は _.bind()
関数でラップされていることに注意してください。
次の2つの理由で_.bind()
を使用しています。
Image
オブジェクトをclipByName()
に渡す必要がありますclipTo
プロパティには、オブジェクトではなく、キャンバスコンテキストが渡されます。基本的に、_.bind()
を使用すると、this
コンテキストとして指定したオブジェクトを使用する関数のバージョンを作成できます。
クリップ領域の配置が正しく配置されておらず、回転するとすべてが不安定になったため、@ 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つのマイナーなキャッチ:
Promlncの回答に更新します。
適切なクリッピングを実行するには、コンテキスト変換の順序を置き換える必要があります。
そうしないと、アスペクト比を維持せずに(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));
これを参照してください: http://jsfiddle.net/ZxYCP/185/
適切な結果:
更新1:
ポリゴンごとにクリップする機能を開発しました。 http://jsfiddle.net/ZxYCP/198/
上記のすべてのフィドルをテストしたところ、バグが1つあります。 X値とY値を一緒に反転すると、境界のクリッピングが間違ってしまいます。また、画像を正しい位置に配置するためのすべての計算を実行しないようにするには、originX = 'center'およびoriginY = 'center'を指定する必要があります。
これは@natchiketaからの元のコードへのクリッピング関数の更新です
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();
}
更新を確認してください フィドル
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));
以前の人の答えに更新します。
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
);
これで、間違いなくクリッピング領域を拡大縮小できます。