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

14

我开始使用fabric.js,并尝试在另一个canvas中添加一个canvas,这样顶部的canvas就保持不变,我可以在内部canvas中添加对象。

以下是将一个canvas添加到另一个canvas的代码片段。

canvas  = new fabric.Canvas('artcanvas');
innerCanvas = new fabric.Canvas("innerCanvas");
canvas.add(innerCanvas);
<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控制台中发现的

在此输入图片描述

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


你确定 fabric 允许嵌套 canvas 吗?如果目标是将 canvas 叠加在彼此上,则还可以使用 CSS 定位。 - Mr. Alien
console.log(t) 说了什么? - Praveen Kumar Purushothaman
@PraveenKumar 这是压缩版本。这是提取版本中的 obj。已更新截图。 - Suresh Atta
你试过我的答案了吗?它应该是你正在寻找的。 - AndreaBogazzi
5个回答

9

经过多次讨论和查阅互联网解决方案,目前我正在使用织物矩形作为夹子,并设置其边界,以便用户能够在特定的夹子中进行拖放/操作。

虚线红色(下图)是我的夹子,现在我可以限制拖放,以下是添加带夹子的图像的代码。

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

}

看起来,经过边界限制,在此输入图片描述

这里是我创建的一个带有一些静态图像网址的fiddle。

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

我现在只能使用这个解决方案,但感觉这种方法很差劲和不专业。寻找其他干净的解决方案。


哇,Suresh,这是一个很好的例子!感谢你的努力。 - jltrem

4
据我所知,您无法将画布添加到另一个画布中-当您尝试调用setCoords()对象时,会出现错误,但在这种情况下,它是另一个画布,而fabric.Canvas不包含该方法(请参见docs)。我认为更好的方法是使用两个画布,并使用CSS相对定位它们-请参见this simple fiddle
<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。

一张图片供他下载,同时我将一个JSON保存在数据库中(fabric有一个名为 canvas.toJSON() 的函数)。如果我按照您提供的解决方案,几乎不可能生成一个组合图像:)而且我还得处理合并JSON:( - Suresh Atta
1
所以第一部分 - 在保存时,您可以将第二个画布序列化为图像,作为图像对象添加到第一个画布中,然后保存。可能不太美观,但应该可以工作?至于合并JSON,我认为您只需要更改内部画布上每个对象的左侧和顶部属性即可。 - John M
那么它们就无法合并了吗?如果它们不同,我必须处理它们的坐标,这让我感到困扰 :./ - Suresh Atta
我已经添加了一个处理序列化的部分。 - John M
谢谢John,如果我没有得到其他干净的解决方案,我会帮忙的(已经投票支持了,不能再投了哈哈)。 - Suresh Atta
显示剩余4条评论

4

我不太明白你为什么要做这件事情,但如果想在一个画布内嵌入另一个画布,有一种简单的方法:

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

然后根据您想要实现的内容,您可能需要进行额外的调整,但这应该可以直接使用。


3

不要创建画布

在fabric中,大部分对象(根据我的有限经验)都会被转换为画布。创建另一个fabric画布来管理一组对象是毫无意义的,因为这只会增加开销并模仿fabric内置的组对象。

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

以下示例展示了fabric如何使用画布存储组的内容。该演示创建一个组并将其添加到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" 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>


0

innerCanvas.setCoords(); 是一个函数,但你只需要在设置坐标之后使用它。更准确地说,设置这四个元素:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接