在另一个画布中添加画布:obj.setCoords 不是函数(fabric js)

Posted

技术标签:

【中文标题】在另一个画布中添加画布:obj.setCoords 不是函数(fabric js)【英文标题】:Adding canvas inside another canvas: obj.setCoords is not a function( fabric js) 【发布时间】:2017-11-10 02:57:02 【问题描述】:

开始使用 fabric.js 并尝试在另一个画布中添加一个画布,以便顶部画布保持不变,我将向内部画布添加对象。

这是将一个画布添加到另一个画布的 sn-p。

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

我的html看起来像这样

<canvas id="artcanvas"  ></canvas>
<canvas id="innerCanvas"   ></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 控制台中找到的内容

有人可以指出我的代码中的错误吗?

【问题讨论】:

你确定面料允许嵌套画布吗?如果目标是相互覆盖画布,您也可以使用 CSS 定位。 console.log(t) 说什么? @PraveenKumar t 是缩小版。它是提取版本中的 obj。更新了截图 你试过我的答案了吗?应该是你正在寻找的东西 【参考方案1】:

innerCanvas.setCoords(); ì 是一个函数,但只有在设置好坐标后才需要它。或者更准确地说,设置这四个元素:

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

【讨论】:

【参考方案2】:

不要创建画布

大多数织物中的物体(根据我有限的经验)在某些时候都会转换为画布。创建额外的织物画布来管理一组对象是毫无意义的,因为您只是在添加开销并模仿组对象中内置的织物。

Fabric 对象基本上是 DOM 画布包装器。

以下示例显示了 Fabric 如何使用画布存储组的内容。该演示创建一个组并将其添加到织物画布,然后获取组画布并将其添加到 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"  ></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"  ></canvas>

【讨论】:

【参考方案3】:

我不明白你为什么要这样做,但是要将一个画布放在另一个画布中,你有一个简单的方法:

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

然后,根据您想要做什么,您可能需要进行额外的调整,但这应该是开箱即用的。

【讨论】:

【参考方案4】:

经过大量讨论和互联网解决方案后,我暂时使用 Fabric Rectangle 作为剪裁器并设置它的边界,以便用户可以在该特定剪裁器中放置/播放。

红色虚线(image below)是我的剪辑器,现在我可以绑定拖放,下面是使用剪辑器添加图像的代码。

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

    );

现在,在将任何图像/图层附加到画布时,我将该图像/图层/文本绑定到我创建的剪辑器。

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


看起来像,在限制后,

这是我用一些静态图片 url 创建的小提琴。

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

所以我现在一直使用这个解决方案,我真的觉得这很老套和肮脏。正在寻找其他一些干净的解决方案。

【讨论】:

哇,Suresh,这是一个很好的例子!感谢您的努力【参考方案5】:

据我所知,您无法将画布添加到另一个画布 - 当它尝试在您添加的对象上调用 setCoords() 时,您会遇到该错误,但在这种情况下,它是另一个画布和织物.Canvas 不包含该方法(请参阅docs)。我认为更好的方法是拥有两个画布并使用 CSS 相对定位它们 - 请参阅 this simple fiddle

HTML

<div class="parent">
  <div class="artcanvas">
    <canvas id="artcanvas"   ></canvas>  
  </div>
  <div class="innerCanvas">
    <canvas id="innerCanvas"   ></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

【讨论】:

感谢您的帮助。不,在X时间点,如果我保存artcanvas的状态,我错过了innerCanvas的变化,我必须处理canvas的两个状态。想避免它。 好的。在那种情况下,我认为您需要更详细地解释为什么您需要两个单独的画布对象 假设我正在设计一件带有用户给定徽标和文字的 T 恤(他也可以选择型号)。所以我的 T 恤将在画布 1 和用户游戏区(我想让他有机会放置一些预定义的徽标和文本。否则他可能会将徽标放在他喜欢的任何地方。)成为用户可以放下东西的另一个画布。所以在任何时候,如果我保存一个主画布,内部画布也会保存,最后我会得到带有徽标/文本的 T 恤的最终输出。如果它们是分开的,我必须再次处理这两者可能会陷入混乱。希望它能解释一下。 我明白了。所以当你保存时,你想要生成什么?您可以对其进行进一步处理的图像或对象列表? 所以对于第一部分 - 在保存时,您可以将第二个画布序列化为图像,作为图像对象添加到第一个画布,然后保存。也许不漂亮,但它应该工作?至于结合JSON我想你只需要改变内部画布上每个对象的left和top属性?

以上是关于在另一个画布中添加画布:obj.setCoords 不是函数(fabric js)的主要内容,如果未能解决你的问题,请参考以下文章

HTML Canvas Compositing:在另一个画布下显示画布图像

将一个组件显示在另一个组件之上

Html画布视频流

如何设置画布大小?

使函数等到元素存在

如何围绕另一个圆圈移动一个点使用Javafx画布?