【问题标题】:Adding canvas inside another canvas: obj.setCoords is not a function( fabric js)在另一个画布中添加画布:obj.setCoords 不是函数(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" 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 控制台中找到的内容

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

【问题讨论】:

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

标签: javascript html canvas html5-canvas fabricjs


【解决方案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" 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>

    【讨论】:

      【解决方案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"  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

        【讨论】:

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