如何水平对齐 fabric.Image 个对象?

How to align fabric.Image objects horizontally?

我只是想加载三个矩形图像并希望它们彼此水平对齐。我认为在 fabric.Image.fromURL 方法中设置左 属性 可以实现此目的,但加载的树图像彼此堆叠。此外,即使我包含 selectable:true,图像也无法选择。我使用 fabric.Image.fromURL 不正确吗?

 <!DOCTYPE html>
    <html>
    <head>
        <title></title>
        <meta charset="utf-8" />
        <!-- Get version 1.1.0 of Fabric.js from CDN -->
        <script src="js/fabric.js"></script>

        <!-- Get the highest 1.X version of jQuery from CDN. Required for ready() function. -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
        <script>


            $(function () {

            var canvas = new fabric.Canvas('canvas', {
                backgroundColor: 'black',
                selectionColor: 'blue',
                selectionLineWidth: 0
                // ...
            });
            debugger;
            var tiles = [
            "images/Green.png",
            "images/Red.png",
            "images/Yellow.png"
            ];
            var offset = [
            "0",
            "200",
            "400"
            ];
            debugger;
            for (i = 0; i < tiles.length; i++) {
                fabric.Image.fromURL(tiles[i], function (img) {
                    img.scale(1.0).set({
                        left: offset[i],
                        top: 0,
                        selectable:true,
                    });
                    canvas.add(img).setActiveObject(img);
                });
            }


            function handleDragStart(e) {
                [].forEach.call(images, function (img) {
                    img.classList.remove('img_dragging');
                });
                this.classList.add('img_dragging');
            }

            function handleDragOver(e) {
                if (e.preventDefault) {
                    e.preventDefault(); // Necessary. Allows us to drop.
                }

                e.dataTransfer.dropEffect = 'copy'; // See the section on the DataTransfer object.
                // NOTE: comment above refers to the article (see top) -natchiketa

                return false;
            }

            function handleDragEnter(e) {
                // this / e.target is the current hover target.
                this.classList.add('over');
            }

            function handleDragLeave(e) {
                this.classList.remove('over'); // this / e.target is previous target element.
            }

            function handleDrop(e) {
                // this / e.target is current target element.

                if (e.stopPropagation) {
                    e.stopPropagation(); // stops the browser from redirecting.
                }

                var img = document.querySelector('#images img.img_dragging');

                console.log('event: ', e);

                var newImage = new fabric.Image(img, {
                    width: img.width,
                    height: img.height,
                    // Set the center of the new object based on the event coordinates relative
                    // to the canvas container.
                    left: e.layerX,
                    top: e.layerY
                });
                canvas.add(newImage);

                return false;
            }

            function handleDragEnd(e) {
                // this/e.target is the source node.
                [].forEach.call(images, function (img) {
                    img.classList.remove('img_dragging');
                });
            }



                // Bind the event listeners for the image elements
                var images = document.querySelectorAll('#images img');
                [].forEach.call(images, function (img) {
                    img.addEventListener('dragstart', handleDragStart, false);
                    img.addEventListener('dragend', handleDragEnd, false);
                });
                // Bind the event listeners for the canvas
                var canvasContainer = document.getElementById('canvas-container');
                canvasContainer.addEventListener('dragenter', handleDragEnter, false);
                canvasContainer.addEventListener('dragover', handleDragOver, false);
                canvasContainer.addEventListener('dragleave', handleDragLeave, false);
                canvasContainer.addEventListener('drop', handleDrop, false);


            });
        </script>
    </head>
    <body>



        <div id="canvas-container">
            <canvas id="canvas"  width="600" height="200"></canvas>
        </div>
        <div id="images">
            <img draggable="true" src="images/Red.png" width="50" height="50"></img>
            <img draggable="true" src="images/Yellow.png" width="50" height="50"></img>
            <img draggable="true" src="images/Green.png" width="50" height="50"></img>
        </div>



    </body>
    </html>

您的 fabric.Image.fromURL 回调与 for 循环的执行之间存在竞争条件,这会阻止图像显示。

如果您在回调中打印 ioffset[i]

fabric.Image.fromURL(tiles[i], function (img) {
    console.log(i, offset[i]);
    ...                
});

您会注意到 i3offset[i]undefined 在所有回调中。

发生这种情况是因为每个回调都保留对 i 变量的同一实例的引用,并在它们执行时使用其最新值。 for 循环在其循环中循环,每次递增 i,然后再执行任何回调,使 i 的最终值为 3。当您的回调执行时,它们会尝试将 left: 值设置为 offset[3],这是未定义的,并且 fabric 回退到 0.

解决方案

ES6 之前的变量 javascript(与大多数语言不同)仅作用于声明它们的函数,代码块不影响作用域。

将您的图像构建逻辑放入它自己的函数中,将 i 的当前值作为每个循环周期中的参数传递。这会将值放入新范围,并保留该值。

此外,不要忘记 i 前面的 var 关键字。否则你会影响全局命名空间,你想不惜一切代价避免它。

您的代码应如下所示:

for (var i = 0; i < tiles.length; i++) {
    buildImage(i);
}

// creates a new scope for the passed in value of i when called:
function buildImage(i) {
    fabric.Image.fromURL(tiles[i], function (img) {
        img.scale(1.0).set({
            left: offset[i],
            top: 0,
            selectable: true,
        });
        canvas.add(img).setActiveObject(img);
    });
}

最后,您可能需要考虑设置图像的 originX and originY 属性,如下所示:

originX: 'left',
originY: 'top'

默认情况下,fabric 在放置它们时会以图像的中心作为原点,并且您的图像不会在 canvas.

的全视图中

更新

这里是fully working example。依赖fabric,需要放在你的js/文件夹下。