Fabric.jsを知ろう。 パート3

これは、オープンJavascriptキャンバスライブラリFabric.jsに関する一連の記事の第3部の翻訳です。



このシリーズの第1部と第2部では、基本的なFabric素材の大部分を取り上げました。 この記事では、より詳細な資料を提供します。



グループ



画像



最初に話すのはグループです。 オブジェクトのグループ化は、Fabricで最も強力なツールの1つです。 オブジェクトをグループ化する必要があるのはなぜですか? 全体として多くのオブジェクトを扱うために言うまでもない。



マウスでオブジェクトをいくつグループ化したか覚えていますか? グループ化すると、色、透明度、フレームなどの外観プロパティを同時に移動、拡大縮小、回転、さらには変更することができます。



このためにグループがあります。 キャンバス上で選択範囲を表示するたびに(上の図のように)、Fabricはグループ内で暗黙的にグループを作成するため、プログラムでグループを操作できます。 これがfabric.Group



の主要なポイントです。



円とテキストの2つのオブジェクトのグループを作成します。



 var circle = new fabric.Circle({ radius: 100, fill: '#eef', scaleY: 0.5, originX: 'center', originY: 'center' }); var text = new fabric.Text('hello world', { fontSize: 30, originX: 'center', originY: 'center' }); var group = new fabric.Group([ circle, text ], { left: 150, top: 100, angle: -10 }); canvas.add(group);
      
      





最初に、テキストオブジェクト「hello world」を作成しました。 originX



およびoriginY



'center'



originX



ます。これにより、このオブジェクトがグループ内で'center'



ます。 初期設定では、グループメンバーはグループの左上隅を基準にして配置されます。 次に、半径100pxの円を「#eef」の色で塗りつぶし、垂直方向に圧縮しました(圧縮率-0.5)。 次に、2つのパラメーターを持つfabric.Group



オブジェクトを作成しました。 最初のパラメーターは、2つのオブジェクトの配列です。 2番目のパラメーターは、グループの位置150/100と角度-10を設定します。 最後に、他のオブジェクトと同様に、 canvas.add()



メソッドを使用して追加しました。



そして出来上がり! キャンバス上に、碑文のある楕円のようなオブジェクトが表示されます。 グループのプロパティを変更することで簡単に変更できることに注意してください。 このオブジェクト全体で作業できます。



画像



キャンバスにグループができました。 少し変えましょう。



 group.item(0).setFill('red'); group.item(1).set({ text: 'trololo', fill: 'white' }); canvas.renderAll();
      
      





ここで何が起こっていますか? item()



メソッドを使用してグループ内のオブジェクトにアクセスし、プロパティを変更しました。 最初のオブジェクトは圧縮された円で、2番目のオブジェクトはテキストです。 何が起こったのか見てみましょう:



画像



あなたがおそらく気づいた重要なことは、グループ内のオブジェクトがグループの中心に揃えられていることです。 テキストオブジェクトのラベルを変更した場合、幅を変更した後でも中央に配置されたままでした。 この動作は、オブジェクトの座標(左\上)を設定することでキャンセルできます。



3つの円を作成してグループ化し、次々に水平に配置します。



 var circle1 = new fabric.Circle({ radius: 50, fill: 'red', left: 0 }); var circle2 = new fabric.Circle({ radius: 50, fill: 'green', left: 100 }); var circle3 = new fabric.Circle({ radius: 50, fill: 'blue', left: 200 }); var group = new fabric.Group([ circle1, circle2, circle3 ], { left: 200, top: 100 }); canvas.add(group);
      
      







画像



グループで作業する場合、オブジェクト状態に注意を払う必要があります 。 たとえば、イメージのグループを形成する場合、それらが完全にロードされていることを確認する必要があります。 幸いなことに、Fabricには既製のソリューションがあります。



 fabric.Image.fromURL('/assets/pug.jpg', function(img) { var img1 = img.scale(0.1).set({ left: 100, top: 100 }); fabric.Image.fromURL('/assets/pug.jpg', function(img) { var img2 = img.scale(0.1).set({ left: 175, top: 175 }); fabric.Image.fromURL('/assets/pug.jpg', function(img) { var img3 = img.scale(0.1).set({ left: 250, top: 250 }); canvas.add(new fabric.Group([ img1, img2, img3], { left: 200, top: 200 })) }); }); });
      
      





画像



グループにはさらにいくつかの重要な方法があります。 fabric.Canvas#getObjects()



と同じようにfabric.Canvas#getObjects()



し、グループ内のすべてのオブジェクトの配列を返すgetObjects()



メソッド。 グループ内のすべてのオブジェクトの数を示すsize()



メソッドがあります。 グループ内の特定のオブジェクトの存在を確認するcontains()



メソッドもありcontains()



。 前述のitem()



メソッド。グループから特定のオブジェクトを取得できます。 forEachObject()



メソッドfabric.Canvas#forEachObject



と同様にfabric.Canvas#forEachObject



し、グループのみで機能します。 最後に、 add()



およびremove()



メソッドは、それぞれグループにオブジェクトを追加および削除します。



グループにオブジェクトを追加/削除するには、2つの方法があります。 グループの位置/サイズの更新ありとなし。

グループの中心に長方形を追加します。



 group.add(new fabric.Rect({ ... originX: 'center', originY: 'center' }));
      
      





グループの中心から100pxの長方形を追加します。



 group.add(new fabric.Rect({ ... left: 100, top: 100, originX: 'center', originY: 'center' }));
      
      





グループの中心に長方形を追加し、グループサイズ更新します。



 group.addWithUpdate(new fabric.Rect({ ... left: group.getLeft(), top: group.getTop(), originX: 'center', originY: 'center' }));
      
      





グループの中心から100pxの長方形を追加し、グループのサイズを更新します。



 group.addWithUpdate(new fabric.Rect({ ... left: group.getLeft() + 100, top: group.getTop() + 100, originX: 'center', originY: 'center' }));
      
      





既にキャンバス上にあるオブジェクトのグループを作成するには、それらを複製してからグループ化する必要があります。



 //     2-  . var group = new fabric.Group([ canvas.item(0).clone(), canvas.item(1).clone() ]); //     . canvas.clear().renderAll(); //    canvas. canvas.add(group);
      
      





連載



たとえば、ユーザーがキャンバスのコンテンツを保存できるようにしたり、別のクライアントにブロードキャストしたりする機能を実装する場合は、キャンバスをシリアル化する必要があります 。 キャンバスコンテンツを送信するにはどうすればよいですか? もちろん、キャンバス全体を画像にエクスポートすることもできますが、サーバーに画像をアップロードするのは面倒で不便です。 Fabricが私たちをとても幸せにしてくれるよりも、コンテンツをテキストに翻訳する方がはるかに簡単です。



メソッドtoObject、toJSON



Fabrisでのシリアル化の基礎は、 fabric.Canvas#toObject()



およびfabric.Canvas#toJSON()



です。

空のキャンバスをシリアル化する例を見てみましょう。



 var canvas = new fabric.Canvas('c'); JSON.stringify(canvas); // '{"objects":[],"background":"rgba(0, 0, 0, 0)"}'
      
      





ES5 JSON.stringify()



メソッドを使用します。このメソッドが存在する場合、オブジェクトのtoJSON



メソッドを呼び出します。 Fabricのキャンバスオブジェクトにはこのメソッドがあり、 JSON.stringify(canvas.toJSON())



を呼び出すのと同等です。



空のキャンバスを表す戻り文字列を考えます。 JSON形式であり、プロパティ ''オブジェクト ''および ''バックグラウンド ''で構成されます。 キャンバスには何もないため、「オブジェクト」プロパティは現在空です。「背景」には初期透明値があります(「rgba(0、0、0、0)」)。



キャンバスを別の背景に設定し、どのような変化があるかを確認します。



 canvas.backgroundColor = 'red'; JSON.stringify(canvas); // '{"objects":[],"background":"red"}'
      
      





予想どおり、キャンバスビューには別の背景が含まれるようになりました。 それでは、いくつかのオブジェクトを追加してみましょう。



 canvas.add(new fabric.Rect({ left: 50, top: 50, height: 20, width: 20, fill: 'green' })); console.log(JSON.stringify(canvas));
      
      





...コンソールに出力されます:

 '{"objects":[{"type":"rect","left":50,"top":50,"width":20,"height":20,"fill":"green","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0}],"background":"rgba(0, 0, 0, 0)"}'
      
      





わあ! 一見、多くのことが変更されていますが、詳しく見てみると、この新しいオブジェクトがJSONでシリアル化された「オブジェクト」配列の一部になっていることがわかります。 その説明には、座標、幅、高さ、塗りつぶしなどのすべての視覚的なコンポーネントが含まれていることに注意してください。 赤い円などの別のオブジェクトを追加し、長方形の後ろに配置すると、それに応じて結果が変わります。



 canvas.add(new fabric.Circle({ left: 100, top: 100, radius: 50, fill: 'red' })); console.log(JSON.stringify(canvas));
      
      





...コンソールに出力されます:

 '{"objects":[{() "type":"rect","left":50,"top":50,"width":20,"height":20,"fill":"green","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0},{() "type":"circle","left":100,"top":100,"width":100,"height":100,"fill":"red","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"radius":50}],"background":"rgba(0, 0, 0, 0)"}'
      
      





これらのオブジェクトの開始位置を示すために、 "type":"rect"



および"type":"circle"



を強調表示しました。 線が長すぎるように見えるかもしれませんが、画像をシリアル化するのに比べて、これらはまだ花です。 比較のために、 canvas.toDataURL('png')



メソッドが返す1/10(!)行を見てください。



 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAK8CAYAAAAXo9vkAAAgAElEQVR4Xu3dP4xtBbnG4WPAQOQ2YBCLK1qpoQE1/m+NVlCDwUACicRCEuysrOwkwcJgAglEItRQaWz9HxEaolSKtxCJ0FwMRIj32zqFcjm8e868s2fNWo/Jygl+e397rWetk5xf5pyZd13wPwIECBAgQIAAAQIECBxI4F0H++Qb134R/U2fevC8q+5esGWESBAgAABAgQIEFiOwPL/MC5AlvO0OBMCBAgQIECAAAECJxQQICcE9HYCBAgQIECAAAECBPYXECD7W3klAQIECBAgQIAAAQInFBAgJwT0dgIECBAgQIAAAQIE9hcQIPtbeSUBAgQIECBAgAABAicUECAnBPR2AgQIECBAgAABAgT2FxAg+1t5JQECBAgQIECAAAECJxQQICcE9HYCBAgQIECAAAECBPYXECD7W3klAQIECBAgQIAAAQInFBAgJwTc9+3z49yvmNd+dI7PzPHJOW6Y4wNzXD3HlXNc9pZdb85/vzbHK3P8aY7n5vj1HL+Y43dz417f97O9jgABAgQIECBAgMBSBATIKd2JCY5dWNwyx5fn+PwcV5U/6tXZ99M5fjjHk3Mjd6HifwQIECBAgAABAgQWLSBAirdnouP6WXfvHHfOcU1x9T6rXp4XPTLHA3NTX9jnDV5DgAABAgQIECBA4NACAuSE4hMdl8+Kr83xzTmuO+G61ttfnEXfnuN7c4PfaC21hwABAgQIECBAgMBJBQTIJQpOeFw7b71/jtsvccWh3vbYfNB9c6NfOtQH+hwCBAgQIECAAAECFxMQIMd8No7C4+F5283HfOtZv/ypOYG7hMhZ3wafT4AAAQIECBDYtoAA2fP+H/1Vqwd3f4jf8y1Lfdkunu7xV7OWenucFwECBAgQIEBg3QICZI/7O/Fxx7xs9wf3t36r3D3evciX7L7F7+6rIY8u8uycFAECBAgQIE'
      
      





さらに〜17,000文字



一見、 fabric.Canvas#toObject



メソッドがもう1つfabric.Canvas#toObject



理由は明らかではありません。 簡単ですtoObject



は、オブジェクトとしてのみtoJSON



と同じ表現を返します。 たとえば、コンテンツが緑色の長方形のキャンバスを使用します。 canvas.toObject()



はコンソールに出力します:



 { "background" : "rgba(0, 0, 0, 0)", "objects" : [ { "angle" : 0, "fill" : "green", "flipX" : false, "flipY" : false, "hasBorders" : true, "hasControls" : true, "hasRotatingPoint" : false, "height" : 20, "left" : 50, "opacity" : 1, "overlayFill" : null, "perPixelTargetFind" : false, "scaleX" : 1, "scaleY" : 1, "selectable" : true, "stroke" : null, "strokeDashArray" : null, "strokeWidth" : 1, "top" : 50, "transparentCorners" : true, "type" : "rect", "width" : 20 } ] }
      
      





ご覧のとおり、 toJSON



の出力は、 toObject



文字列の翻訳にすぎません。 toObject



メソッドtoObject



、スマートで怠toObject



という点toObject



興味深く、便利です。 配列に表示されるのは、すべてのキャンバスオブジェクトを反復処理し、 toObject



メソッドをtoObject



委任したtoObject



です。 fabric.Path



クラスfabric.Path



は独自のtoObject



、これは ''ポイント ''の配列を返します。 また、 fabric.Image



もこのメソッドがあり、画像の `` src ''プロパティを返します。 OOPのパターンに従って、各オブジェクトは自身をシリアル化する方法を知っています。



つまり、独自の「クラス」を作成する場合、またはオブジェクトのシリアル化された表現を変更する場合は、その機能で上書きまたは拡張できるtoObject



メソッドが必要です。

例を見てみましょう:



 var rect = new fabric.Rect(); rect.toObject = function() { return { name: 'trololo' }; }; canvas.add(rect); console.log(JSON.stringify(canvas));
      
      





...コンソールに出力されます:



 '{"objects":[{"name":"trololo"}],"background":"rgba(0, 0, 0, 0)"}'
      
      





ご覧のとおり、「オブジェクト」配列には、四角形の表現が変更されています。 ただし、この方法で書き換えることはtoObject



追加のプロパティでtoObject



機能を拡張するのとは異なり、あまり有用ではないことがよくあります。



 var rect = new fabric.Rect(); rect.toObject = (function(toObject) { return function() { return fabric.util.object.extend(toObject.call(this), { name: this.name }); }; })(rect.toObject); canvas.add(rect); rect.name = 'trololo'; console.log(JSON.stringify(canvas));
      
      





...コンソールに出力されます:



 '{"objects":[{"type":"rect","left":0,"top":0,"width":0,"height":0,"fill":"rgb(0,0,0)","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0,"name":"trololo"}],"background":"rgba(0, 0, 0, 0)"}'
      
      





追加のプロパティ '' name ''を使用して、オブジェクトの既存のtoObject



メソッドを拡張しました。 現在、メソッド呼び出しの結果として存在しています。 このように機能を拡張する場合、オブジェクト(この場合はfabric.Rect



)の「クラス」には、配列 '' stateProperties ''に新しく追加されたプロパティが含まれている必要があることに注意してください。 この場合のみ、すべてが正しく機能します。



ToSVGメソッド



キャンバスのもう1つのテキスト表現は、SVG形式です。 FabricはSVGの解析とキャンバス上の表示を専門としています。 これにより、キャンバスからSVGに、またはその逆に変換する機能が提供されます。 同じ長方形をキャンバスに追加し、 toSVG



メソッドの動作を確認します。



 canvas.add(new fabric.Rect({ left: 50, top: 50, height: 20, width: 20, fill: 'green' })); console.log(canvas.toSVG());
      
      





...コンソールに出力されます:



 '<?xml version="1.0" standalone="no" ?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800" height="700" xml:space="preserve"><desc>Created with Fabric.js 0.9.21</desc><rect x="-10" y="-10" rx="0" ry="0" width="20" height="20" style="stroke: none; stroke-width: 1; stroke-dasharray: ; fill: green; opacity: 1;" transform="translate(50 50)" /></svg>'
      
      





toJSON



toObject



と同様に、 toSVG



はキャンバスで呼び出されると、そのロジックを各オブジェクトに委任し、各オブジェクトは各タイプのオブジェクトに固有の独自のtoSVG



メソッドを持ちます。 toSVG



メソッドを変更または拡張する必要がある場合は、 toSVG



メソッドと同じ方法で変更できます。



toObject/toJSON



と比較したSVGプレゼンテーションの利点は、SVGレンダリングが可能な任意のデバイス(ブラウザー、アプリケーション、プリンター、カメラなど)と連携できることです。 toObject / toJSONメソッドでは、最初にビューをキャンバスにロードする必要があります。 キャンバスへの読み込みといえば。 canvasのコンテンツをテキストにシリアル化できますが、どのようにロードし直すことができますか?



逆シリアル化、SVGパーサー



シリアル化の場合と同様に、文字列からキャンバスをロードするには、JSONビューとSVGビューからの2つの方法があります。 JSONにはfabric.Canvas#loadFromJSON



およびfabric.Canvas#loadFromDatalessJSON



メソッドがあります。 SVGの場合-メソッドfabric.loadSVGFromURL



およびfabric.loadSVGFromString





最初の2つのメソッドはキャンバスで呼び出され、他の2つのメソッドはファブリックで直接呼び出されることに注意してください。



これらの方法について特別なことはありません。 それらは期待どおりに機能します。 たとえば、前のJSON出力を空のキャンバスに配置します。



 var canvas = new fabric.Canvas(); canvas.loadFromJSON('{"objects":[{"type":"rect","left":50,"top":50,"width":20,"height":20,"fill":"green","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0},{"type":"circle","left":100,"top":100,"width":100,"height":100,"fill":"red","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"radius":50}],"background":"rgba(0, 0, 0, 0)"}');
      
      





...両方のオブジェクトがキャンバスに表示されます。



画像



さて、文字列からキャンバスをロードすることは非常に簡単ですが、一見理解できないloadFromDatalessJSON



メソッドはどうでしょうか? 先ほど使用したloadFromJSON



との基本的な違いは何ですか? このメソッドの目的を理解するには、次のような、多少複雑なパスオブジェクトを調べる必要があります。



画像



...そしてJSON.stringify(canvas)



は以下を出力します:



 '{"objects":[{"type":"path","left":184,"top":177,"width":175,"height":151,"fill":"#231F20","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":-19,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"path":[["M",39.502,61.823],["c",-1.235,-0.902,-3.038,-3.605,-3.038,-3.605],["s",0.702,0.4,3.907,1.203],["c",3.205,0.8,7.444,-0.668,10.114,-1.97],["c",2.671,-1.302,7.11,-1.436,9.448,-1.336],["c",2.336,0.101,4.707,0.602,4.373,2.036],["c",-0.334,1.437,-5.742,3.94,-5.742,3.94],["s",0.4,0.334,1.236,0.334],["c",0.833,0,6.075,-1.403,6.542,-4.173],["s",-1.802,-8.377,-3.272,-9.013],["c",-1.468,-0.633,-4.172,0,-4.172,0],["c",4.039,1.438,4.941,6.176,4.941,6.176],["c",-2.604,-1.504,-9.279,-1.234,-12.619,0.501],["c",-3.337,1.736,-8.379,2.67,-10.083,2.503],["c",-1.701,-0.167,-3.571,-1.036,-3.571,-1.036],["c",1.837,0.034,3.239,-2.669,3.239,-2.669],["s",-2.068,2.269,-5.542,0.434],["c",-3.47,-1.837,-1.704,-8.18,-1.704,-8.18],["s",-2.937,5.909,-1,9.816],["C",34.496,60.688,39.502,61.823,39.502,61.823],["z"],["M",77.002,40.772],["c",0,0,-1.78,-5.03,-2.804,-8.546],["l",-1.557,8.411],["l",1.646,1.602],["c",0,0,0,-0.622,-0.668,-1.691],["C",72.952,39.48,76.513,40.371,77.002,40.772],["z"],["M",102.989,86.943],["M",102.396,86.424],["c",0.25,0.22,0.447,0.391,0.594,0.519],["C",102.796,86.774,102.571,86.578,102.396,86.424],["z"],["M",169.407,119.374],["c",-0.09,-5.429,-3.917,-3.914,-3.917,-2.402],["c",0,0,-11.396,1.603,-13.086,-6.677],["c",0,0,3.56,-5.43,1.69,-12.461],["c",-0.575,-2.163,-1.691,-5.337,-3.637,-8.605],["c",11.104,2.121,21.701,-5.08,19.038,-15.519],["c",-3.34,-13.087,-19.63,-9.481,-24.437,-9.349],["c",-4.809,0.135,-13.486,-2.002,-8.011,-11.618],["c",5.473,-9.613,18.024,-5.874,18.024,-5.874],["c",-2.136,0.668,-4.674,4.807,-4.674,4.807],["c",9.748,-6.811,22.301,4.541,22.301,4.541],["c",-3.097,-13.678,-23.153,-14.636,-30.041,-12.635],["c",-4.286,-0.377,-5.241,-3.391,-3.073,-6.637],["c",2.314,-3.473,10.503,-13.976,10.503,-13.976],["s",-2.048,2.046,-6.231,4.005],["c",-4.184,1.96,-6.321,-2.227,-4.362,-6.854],["c",1.96,-4.627,8.191,-16.559,8.191,-16.559],["c",-1.96,3.207,-24.571,31.247,-21.723,26.707],["c",2.85,-4.541,5.253,-11.93,5.253,-11.93],["c",-2.849,6.943,-22.434,25.283,-30.713,34.274],["s",-5.786,19.583,-4.005,21.987],["c",0.43,0.58,0.601,0.972,0.62,1.232],["c",-4.868,-3.052,-3.884,-13.936,-0.264,-19.66],["c",3.829,-6.053,18.427,-20.207,18.427,-20.207],["v",-1.336],["c",0,0,0.444,-1.513,-0.089,-0.444],["c",-0.535,1.068,-3.65,1.245,-3.384,-0.889],["c",0.268,-2.137,-0.356,-8.549,-0.356,-8.549],["s",-1.157,5.789,-2.758,5.61],["c",-1.603,-0.179,-2.493,-2.672,-2.405,-5.432],["c",0.089,-2.758,-1.157,-9.702,-1.157,-9.702],["c",-0.8,11.75,-8.277,8.011,-8.277,3.74],["c",0,-4.274,-4.541,-12.82,-4.541,-12.82],["s",2.403,14.421,-1.336,14.421],["c",-3.737,0,-6.944,-5.074,-9.879,-9.882],["C",78.161,5.874,68.279,0,68.279,0],["c",13.428,16.088,17.656,32.111,18.397,44.512],["c",-1.793,0.422,-2.908,2.224,-2.908,2.224],["c",0.356,-2.847,-0.624,-7.745,-1.245,-9.882],["c",-0.624,-2.137,-1.159,-9.168,-1.159,-9.168],["c",0,2.67,-0.979,5.253,-2.048,9.079],["c",-1.068,3.828,-0.801,6.054,-0.801,6.054],["c",-1.068,-2.227,-4.271,-2.137,-4.271,-2.137],["c",1.336,1.783,0.177,2.493,0.177,2.493],["s",0,0,-1.424,-1.601],["c",-1.424,-1.603,-3.473,-0.981,-3.384,0.265],["c",0.089,1.247,0,1.959,-2.849,1.959],["c",-2.846,0,-5.874,-3.47,-9.078,-3.116],["c",-3.206,0.356,-5.521,2.137,-5.698,6.678],["c",-0.179,4.541,1.869,5.251,1.869,5.251],["c",-0.801,-0.443,-0.891,-1.067,-0.891,-3.473]'...
      
      





...そして、これはテキスト全体の5番目(!)の部分です。



ここで何が起こっていますか? Pathオブジェクトは、このオブジェクトの表示方法を示す数百のベジェ曲線シンボルで構成されています。 JSON形式のこれらすべてのパーツ["c",0,2.67,-0.979,5.253,-2.048,9.079]



は、いずれかの曲線の座標です。 そして、これらの曲線が数百または数千ある場合、弦の大きさを推測することは難しくありません。



どうする?



fabric.Canvas#toDatalessJSON



メソッドがfabric.Canvas#toDatalessJSON



助けになりfabric.Canvas#toDatalessJSON



。 試してみましょう:



 canvas.item(0).sourcePath = '/assets/dragon.svg'; console.log(JSON.stringify(canvas.toDatalessJSON()));
      
      





...コンソールに出力されます:



 {"objects":[{"type":"path","left":143,"top":143,"width":175,"height":151,"fill":"#231F20","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":-19,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"path":"/assets/dragon.svg"}],"background":"rgba(0, 0, 0, 0)"}
      
      





まあ、はるかに少ない! 私たちは何をしましたか? toDatalessJSON



メソッドを呼び出す前に、パス(ドラゴンシェイプ)の「sourcePath」プロパティを「/assets/dragon.svg」に設定することに注意してください。 次にtoDatalessJSON



メソッドを呼び出し、この巨大なパス文字列全体が単純な文字列「/assets/dragon.svg」に変わりました。



多数の複雑なフォームを使用するtoDatalessJSON



メソッドを使用すると、canvasのテキスト表現を大幅に削減し、大きなパスデータを単純なSVGリンクに置き換えることができます。



loadFromDatalessJSON



メソッドに戻ると、おそらくデータのない(データレスの)ビューからcanvasをロードできると推測したでしょう。 loadFromDatalessJSON



は、「パス」文字列(「/assets/dragon.svg」など)をロードし、それらをパスオブジェクトのデータとして使用できます。



SVGをロードする方法を見てみましょう。 文字列またはURLを使用できます。



 fabric.loadSVGFromString('...', function(objects, options) { var obj = fabric.util.groupSVGElements(objects, options); canvas.add(obj).renderAll(); });
      
      





最初の引数はSVG文字列で、2番目はコールバック関数です。 この関数は、SVGが読み込まれたときに呼び出されます。 objects



options



2つの引数を取りoptions



objects



-SVGから取得したオブジェクトの配列-パス、パスオブジェクトのグループ(複雑なオブジェクト用)、画像、テキストなど これらすべてのオブジェクトを1つのコレクションにグループ化し、SVGドキュメントにあるようにfabric.util.groupSVGElements



ために、 objects



options



両方にfabric.util.groupSVGElements



を使用しoptions



。 キャンバスに追加できるfabric.Path



またはfabric.PathGroup



オブジェクトが作成されます。



fabric.loadSVGFromURL



も同様に機能しますが、SVGのコンテンツではなく、URLを含む文字列を使用します。 FabricはXMLHttpRequestを介してこのURLを取得しようとするため、SVGへのリンクはSOPルールに準拠する必要があることに注意してください。



サブクラス化



FabricはOOPの原則に基づいて構築されているため、サブクラスを簡単に作成および作成し、オブジェクトの機能を拡張できます。 シリーズの最初の部分からわかるように、Fabricにはオブジェクトの厳密な階層があります。 すべての2Dオブジェクト(パス、画像、テキストなど)はfabric.Object



から継承し、 fabric.Object



などの一部の「クラス」は3レベルの継承さえ持っています。



Fabricの既存の「クラス」をサブクラス化するのはどうですか? または、新しい「クラス」を作成しますか?



これを行うには、javascriptの通常のプロトタイプ継承を単純に抽象化したfabric.util.createClass



メソッドが必要です。 最初に、単純な「クラス」ポイントを作成します。



 var Point = fabric.util.createClass({ initialize: function(x, y) { this.x = x || 0; this.y = y || 0; }, toString: function() { return this.x + '/' + this.y; } });
      
      





createClass



はオブジェクトを受け取り、そのプロパティを新しい「クラス」のオブジェクトのプロパティとして使用します。 '' initialize ''はコンストラクターとして使用されます。 したがって、Pointを初期化するときに、プロパティ '' x ''、 '' y ''、およびメソッド '' toString ''を持つ新しいオブジェクトを作成します。



 var point = new Point(10, 20); point.x; // 10 point.y; // 20 point.toString(); // "10/20"
      
      





「クラス」ポイントの子孫、たとえば色付きのポイントを作成する場合、 createClass



を使用します。



 var ColoredPoint = fabric.util.createClass(Point, { initialize: function(x, y, color) { this.callSuper('initialize', x, y); this.color = color || '#000'; }, toString: function() { return this.callSuper('toString') + ' (color: ' + this.color + ')'; } });
      
      





継承用のオブジェクトが2番目の引数として使用されるようになりました。最初の引数は、このオブジェクトの親になる「クラス」ポイントです。 重複を避けるために、親の「クラス」でメソッドを呼び出すcallSuper



メソッドを使用します。 これは、Pointを変更すると、変更がColoredPoint



も影響することを意味します。 例を見てみましょう:



 var redPoint = new ColoredPoint(15, 33, '#f55'); redPoint.x; // 15 redPoint.y; // 33 redPoint.color; // "#f55" redPoint.toString(); "15/35 (color: #f55)"
      
      





これで、独自の「クラス」と「サブクラス」を作成する方法がわかりましたが、Fabricで既存のものを使用することもできます。 たとえば、「クラス」 LabeledRect



作成します。これは、碑文のある長方形になります。 「クラス」のインスタンスがキャンバスに表示されると、ラベルは長方形の内側に表示されます。 「グループ」の章ですでに検討した類似の内容(円とテキスト)。 ところで、Fabricで作業しているときに、グループと「クラス」の両方を使用して抽象化をここで作成できることに気付くことができます。



 var LabeledRect = fabric.util.createClass(fabric.Rect, { type: 'labeledRect', initialize: function(options) { options || (options = { }); this.callSuper('initialize', options); this.set('label', options.label || ''); }, toObject: function() { return fabric.util.object.extend(this.callSuper('toObject'), { label: this.get('label') }); }, _render: function(ctx) { this.callSuper('_render', ctx); ctx.font = '20px Helvetica'; ctx.fillStyle = '#333'; ctx.fillText(this.label, -this.width/2, -this.height/2 + 20); } });
      
      





コードは複雑に見えるかもしれませんが、非常に単純です。



最初に、表示機能を追加するために、親の「クラス」をfabric.Rect



としてfabric.Rect



します。 次に、プロパティ '' type ''を定義し、値 '' labeledRect ''に設定します。 これは、Fabricアーキテクチャに合わせるために行われます。そこにあるすべてのオブジェクトには、「type」(長方形、円、パス、テキストなど)のプロパティがあるためです。



コンストラクター( initialize



)は既におなじみで、 callSuper



を呼び出し、 callSuper



initialize



を呼び出しinitialize



。 さらに、 options



から値を取得して、オブジェクトに碑文(ラベル)を付けoptions







その結果、2つのメソッドleft- toObject



_render



ます。 toObject



、シリアル化の章から思い出すように、オブジェクトを表す責任があります。



LabeledRect



は通常のrectと同じプロパティがあるため、単純にラベルを追加することで親toObject



メソッドを拡張しました。



_render



メソッドに関しては、オブジェクトを直接レンダリングする責任があります。 これは、長方形ディスプレイ( callSuper



)と追加のテキスト表示ロジックで構成されています。



さて、そのようなオブジェクトを表示したい場合:



 var labeledRect = new LabeledRect({ width: 100, height: 50, left: 100, top: 100, label: 'test', fill: '#faa' }); canvas.add(labeledRect);
      
      





...次のようになります:



画像



プロパティ(ラベルなど)を変更すると、期待される結果が生成されます。



 labeledRect.set({ label: 'trololo', fill: '#aaf', rx: 10, ry: 10 });
      
      







画像



必要に応じて、「クラス」の動作を変更できます。 たとえば、コンストラクタでもう一度設定しないようにデフォルト値を追加します。 または、オブジェクトでカスタムプロパティを使用可能にします。 追加のプロパティをカスタムにした場合、それらをtoObject



に入れてinitialize







 ... initialize: function(options) { options || (options = { }); this.callSuper('initialize', options); //       /   100/50 this.set({ width: 100, height: 50 }); this.set('label', options.label || ''); } ... _render: function(ctx) { //        ctx.font = this.labelFont; ctx.fillStyle = this.labelFill; ctx.fillText(this.label, -this.width/2, -this.height/2 + 20); } ...
      
      





これで、シリーズの第3部は終わりです。 これで、グループ、「クラス」、「サブクラス」、および(デ)シリアル化のアイデアが得られました。 この記事で紹介した資料が、Fabricのより複雑な問題の解決に役立つことを願っています。 シリーズの第4部では、さらに多くの情報が提示されます。



成功と開発の成功!



翻訳は、著者の同意と直接の参加で行われました。 ソースへのリンク



All Articles