web-dev-qa-db-ja.com

別のキャンバス内にキャンバスを追加する:obj.setCoordsは関数ではありません(fabric js)

Fabric.jsの使用を開始し、別のキャンバス内にキャンバスを追加しようとしました。これにより、上部のキャンバスが一定に保たれ、内部のキャンバスにオブジェクトが追加されます。

これは、キャンバスを別のキャンバスに追加するスニペットです。

canvas  = new fabric.Canvas('artcanvas');
innerCanvas = new fabric.Canvas("innerCanvas");
canvas.add(innerCanvas);

そして私のhtmlはこのように見えます

<canvas id="artcanvas" width="500" height="500"></canvas>
<canvas id="innerCanvas" width="200" height="200" ></canvas>

これらを正常に追加したら、内側のキャンバスに座標を追加して、エンドユーザーに互いに見えるようにします。

ただし、試行したコードで以下のエラーが発生しました

    Uncaught TypeError: obj.setCoords is not a function
    at klass._onObjectAdded (fabric.js:6894)
    at klass.add (fabric.js:231)
    at main.js:60
    at fabric.js:19435
    at HTMLImageElement.fabric.util.loadImage.img.onload (fabric.js:754)
_onObjectAdded @ fabric.js:6894
add @ fabric.js:231
(anonymous) @ main.js:60
(anonymous) @ fabric.js:19435
fabric.util.loadImage.img.onload @ fabric.js:754

エラーメッセージを見て、エラーの行に移動しました。これがchrome consoleで見つけたものです。

enter image description here

誰かが私のコードの間違いを指摘できますか?

14
Suresh Atta

多数のディスカッションとインターネットソリューションを経た後、当面は、Fabric Rectangleをクリッパーとして使用し、ユーザーがその特定のクリッパーでドロップ/プレイできるように境界を設定します。

点線の赤(下の画像)は私のクリッパーで、これでドロップをバインドできます。下はクリッパーで画像を追加するコードです。

function addImageToCanvas(imgSrc) { 
    fabric.Object.prototype.transparentCorners = false;    
    fabric.Image.fromURL(imgSrc, function(myImg) {
        var img1 = myImg.set({
            left: 20,
            top: 20,
            width: 460,
            height: 460
        });
        img1.selectable = false;
        canvas.add(img1);

        var clipRectangle = new fabric.Rect({
            originX: 'left',
            originY: 'top',
            left: 150,
            top: 150,
            width: 200,
            height: 200,
            fill: 'transparent',
            /* use transparent for no fill */
            strokeDashArray: [10, 10],
            stroke: 'red',
            selectable: false
        });

        clipRectangle.set({
            clipFor: 'layer'
        });
        canvas.add(clipRectangle);

    });
}

enter image description here

ここで、画像/レイヤーをキャンバスに追加するときに、その画像/レイヤー/テキストを作成したクリッパーにバインドします。

function addLayerToCanvas(laImg) {

    var height = $(laImg).height();
    var width = $(laImg).width();
    var clickedImage = new Image();
    clickedImage.onload = function(img) {

        var pug = new fabric.Image(clickedImage, {

            width: width,
            height: height,
            left: 150,
            top: 150,
            clipName: 'layer',
            clipTo: function(ctx) {
                return _.bind(clipByName, pug)(ctx)
            }
        });
        canvas.add(pug);
    };
    clickedImage.src = $(laImg).attr("src");

}

そして、境界の制限の後、 enter image description here

これが私が静的画像のURLで作成したフィドルです。

https://jsfiddle.net/sureshatta/yxuoav39/

だから私は今のところこのソリューションにとどまっています、そして私はこれがハッキーで汚いように本当に感じます。他のいくつかのクリーンなソリューションを探しています。

8
Suresh Atta

私の知る限り、キャンバスを別のキャンバスに追加することはできません-追加したオブジェクトでsetCoords()を呼び出そうとするとエラーが発生しますが、この場合は別のキャンバスとファブリックです。Canvasはそのメソッドは含まれていません( docs を参照)。より良いアプローチは、2つのキャンバスを用意し、CSSを使用してそれらを相対的に配置することだと思います- この単純なフィドル

[〜#〜] html [〜#〜]

<div class="parent">
  <div class="artcanvas">
    <canvas id="artcanvas"  width="500" height="500"></canvas>  
  </div>
  <div class="innerCanvas">
    <canvas id="innerCanvas" width="200" height="200" ></canvas>  
  </div>
</div>

[〜#〜] css [〜#〜]

.parent {
  position: relative;
  background: black;
}


.artcanvas {
  position: absolute;
  left: 0;
  top: 0;
}

.innerCanvas {
  position: absolute;
  left: 150px;
  top: 150px;
}

[〜#〜] js [〜#〜]

$(document).ready(function() {
    canvas  = new fabric.Canvas('artcanvas');
  innerCanvas = new fabric.Canvas("innerCanvas");
  var rect = new fabric.Rect({
    fill: 'grey',
    width: 500,
    height: 500
  });
  canvas.add(rect);
  var rect2 = new fabric.Rect({
    fill: 'green',
    width: 200,
    height: 200
  });
  innerCanvas.add(rect2);
})

オブジェクトのシリアル化を処理するには、次のようにします。

var innerObjs = innerCanvas.toObject();
console.dir(innerObjs);
var outerObjs = canvas.toObject();
innerObjs.objects.forEach(function (obj) {
    obj.left += leftOffset; // offset of inner canvas
  obj.top += topOffset;
  outerObjs.objects.Push(obj);
    });
var json = JSON.stringify(outerObjs);

これにより、両方のキャンバス上のすべてのオブジェクトのJSONが提供されます

4
John M

なぜこのようなことをしたいのかわかりませんが、キャンバスを別のキャンバスの中に入れるには、簡単な方法が1つあります。

canvas  = new fabric.Canvas('artcanvas');
innerCanvas = new fabric.Canvas("innerCanvas");
imageContainer = new fabric.Image(innerCanvas.lowerCanvasEl);
canvas.add(imageContainer);

次に、やりたいことによっては、追加の調整が必要になる場合がありますが、これはそのままで機能するはずです。

3
AndreaBogazzi

キャンバスを作成しないでください

(私の限られた経験からの)ファブリック内のほとんどのオブジェクトは、ある時点でキャンバスに変換されます。オブジェクトのグループを管理するために追加のファブリックキャンバスを作成することは、オーバーヘッドを追加し、グループオブジェクトに組み込まれたファブリックを模倣しているだけなので、意味がありません。

ファブリックオブジェクトは基本的にDOMキャンバスラッパーです。

次の例は、ファブリックがキャンバスを使用してグループのコンテンツを格納する方法を示しています。デモでは、グループを作成してファブリックキャンバスに追加し、グループキャンバスを取得してDOMに追加します。グループのキャンバスをクリックすると、円が追加されます。新しいサークルに対応するためにどのように成長するかに注意してください。

const radius = 50;
const fill = "#0F0";
var pos = 60;

const canvas = new fabric.Canvas('fCanvas');

// create a fabric group and add two circles
const group = new fabric.Group([
    new fabric.Circle({radius, top : 5, fill,  left : 20 }),
    new fabric.Circle({radius, top : 5, fill,  left : 120 })
], { left: 0, top: 0 });

// add group to the fabric canvas;
canvas.add(group);

// get the groups canvas and add it to the DOM
document.body.appendChild(group._cacheContext.canvas);

// add event listener to add circles to the group
group._cacheContext.canvas.addEventListener("click",(e)=>{
  group.addWithUpdate(
      new fabric.Circle({radius, top : pos, fill : "blue",  left : 60 }) 
  );
  canvas.renderAll();
  pos += 60;
});
canvas {
   border : 2px solid black;
}
div {
   margin : 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.13/fabric.min.js"></script>
<div>Fabric's canvas  "canvas = new fabric.Canvas('fCanvas');"</div>
<canvas id="fCanvas" width="256" height="140"></canvas>
<div>Fabric group canvas below. Click on it to add a circle.</div>

ファブリックキャンバスの新しいインスタンスではなく、グループを使用します。

ご覧のとおり、キャンバスが生成されます。別のファブリックキャンバスを追加すると(ファブリックキャンバスはDOMキャンバスと同じではないことに注意してください)、ファブリックが実行する作業が増えるだけで、すでに多くの作業が必要になります。

グループを使用して、シャドウしたい他のファブリックオブジェクトのコンテンツを保持するのが最善です。これには、グループ内のコンテンツも含まれます。

ただの画像

また、DOMキャンバスは画像であり、他の画像と同じようにファブリックで使用できます。ファブリックを介してではなく、キャンバスに直接レンダリングする方がよい場合があります。これにより、ファブリックの操作に必要なオーバーヘッドのレンダリングを回避できます。

DOMキャンバスをファブリックに追加するには、画像として追加するだけです。境界線とテキストはファブリックオブジェクトではなく、それらをレンダリングするコードを除いて、メモリを消費せず、ファブリックキャンバスとオブジェクトを使用した場合に発生する追加のCPUオーバーヘッドもありません。

const canvas = new fabric.Canvas('fCanvas');

// create a standard DOM canvas
const myImage = document.createElement("canvas");

// size it add borders and text
myImage.width = myImage.height = 256;
const ctx = myImage.getContext("2d");
ctx.fillRect(0,0,256,256);
ctx.fillStyle = "white";
ctx.fillRect(4,4,248,248);
ctx.fillStyle = "black";
ctx.font = "32px arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("The DOM canvas!",128,128);

// use the canvas to create a fabric image and add it to fabrics canvas.
canvas.add( new fabric.Image(myImage, {
  left: (400 - 256) / 2,
  top: (400 - 256) / 2,
}));
canvas {
   border : 2px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.13/fabric.min.js"></script>
<canvas id="fCanvas" width="400" height="400"></canvas>
2
Blindman67

innerCanvas.setCoords();は関数ですが、座標を設定した後にのみ必要になります。より正確には、次の4つの要素を設定します。

innerCanvas.scaleX = 1;
innerCanvas.scaleY = 1;
innerCanvas.left = 150;
innerCanvas.top = 150;
innerCanvas.setCoords();
canvas.renderAll();
0
Geert Jan