拖拽并放置到 Fabric.js 画布中

16

如何将物品(如图像或其他来自外部画布的对象)放入由fabricjs管理的画布中?我找到了许多在画布内移动物品的示例,但我想从外部元素中拖放物品到画布中。


1
你可以像实现HTML5/Javascript的拖放功能一样来实现它。只需将画布用作拖放目标(并相应地将对象添加到Fabric画布上)。 - kangax
在printio.ru(它背后是fabric.js)上,编辑器允许您单击织物画布外的图像(剪贴画),然后该剪贴画片段出现在画布上,您可以对其进行编辑,并右键单击以选择删除。这与拖放到画布上不同,但提供了非常相似的功能,可能更容易维护?只是思考的食物。 - Victor Pudeyev
5个回答

21

既然您要求一个例子,而我自己还没有尝试过,那么这里就是:

演示代码


标记

<div id="images">
    <img draggable="true" src="http://i.imgur.com/8rmMZI3.jpg" width="250" height="250"></img>
    <img draggable="true" src="http://i.imgur.com/q9aLMza.png" width="252" height="295"></img>
    <img draggable="true" src="http://i.imgur.com/wMU4SFn.jpg" width="238" height="319"></img>
</div>

<div id="canvas-container">
    <canvas id="canvas" width="800" height="600"></canvas>
</div>

JS分解

1. Fabric.canvas实例

首先,我们当然需要我们的画布:

var canvas = new fabric.Canvas('c');

2. 特性检测(可选)

不确定这是否必要,因为您拥有画布意味着浏览器很可能也支持拖放功能。如果您决定使用它,可以像这样使用Modernizr进行特性检测:

if (Modernizr.draganddrop) {
    // Browser supports HTML5 DnD.

    // Bind the event listeners for the image elements

    // Bind the event listeners for the canvas
} else {
    // Replace with a fallback to a library solution.
    alert("This browser doesn't support the HTML5 Drag and Drop API.");
}

3. 事件

与下面的源文章不同,此处的源元素和目标元素是不同的(在该文章的示例中,您只需在同一父容器中移动

),因此我没有注意到一些事件是针对正在拖动的元素,但大多数事件是绑定到您要将其拖放到其中的元素。

注意:我知道这实际上是关于Fabric.js的问题,但在向添加对象的拖放上下文中,这真的是关于拖放的问题。因此,我现在会更深入地讨论DnD的内容。

对于<img>

  • dragstart(我在这里添加了一个类来降低不透明度)
  • dragend(并在此处删除了该类)

对于#canvas-container

  • dragenter(添加了一个类来给画布容器带来漂亮的虚线)
  • dragover:在这里,您可以设置event.dataTransfer.dropEffect属性以显示本机光标类型之一。默认情况下,这里应为'move',但由于我实际上没有删除<img>元素(事实上,在fiddle中,您可以创建多个McClures),因此我将其设置为'copy'
  • dragleave(在此处删除了虚线)
  • drop:此事件的处理程序创建并添加fabric.Image对象(请参见fiddle)。
if (Modernizr.draganddrop) {
    // Browser supports HTML5 DnD.

    // 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);
} else {
    // Replace with a fallback to a library solution.
    alert("This browser doesn't support the HTML5 Drag and Drop API.");
}

参考资料:


我们能否使用上述代码从桌面向画布进行拖放操作?我尝试过了,但无法实现。 - Cathy
我已经尝试了这段代码,但是我很困惑,因为从上面的代码中我们如何可以从桌面拖放到画布,因为我们必须在脚本中指定我们正在从桌面拖动。 - Cathy
很抱歉,我误读了您最初的评论。HTML5拖放API用于使DOM元素在DOM内可拖动和可放置。从桌面将文件拖入浏览器是另一回事,您需要使用类似于文件系统API的东西。有关详细信息,请参阅此不同的HTML5 Rocks文章:http://updates.html5rocks.com/2012/08/Integrating-input-type-file-with-the-Filesystem-API - natchiketa
我已经适应了这个dragnDrop代码到我的fabricjs页面上 - 在chrome中运行良好但在firefox中则不行... 我的handleDrop(e)和handleDragOver(e)函数中都有 - if(e.stopPropagation)和if(e.preventDefault)。 - mvprzy
3
抱歉打扰了一个旧的讨论串,但是小提琴为什么不再起作用了?另外,最新版fabricjs中'canvas-container'是一个类而不是id。您能否修复您的代码,以便我可以使用它?谢谢! - RepeaterCreeper
显示剩余2条评论

17

我已经查看了@natchiketa的fiddle,并解决了问题,只需检查这个fiddle即可。

http://jsfiddle.net/Ahammadalipk/w8kkc/185/

    window.onload = function () {

        var canvas = new fabric.Canvas('canvas');

        /* 
        NOTE: the start and end handlers are events for the <img> elements; the rest are bound to 
        the canvas container.
        */

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

            e.dataTransfer.dropEffect = 'copy';
            return false;
        }

        function handleDragEnter(e) {                
            this.classList.add('over');
        }

        function handleDragLeave(e) {
            this.classList.remove('over'); 
        }

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

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

            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
            });
            newImage.hasControls = newImage.hasBorders = false;
            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');
            });
        }

        if (Modernizr.draganddrop) {



            var images = document.querySelectorAll('#images img');
            [].forEach.call(images, function (img) {
                img.addEventListener('dragstart', handleDragStart, false);
                img.addEventListener('dragend', handleDragEnd, false);
            });

            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);
        } else {

            alert("This browser doesn't support the HTML5 Drag and Drop API.");
        }
    }

谢谢


3
感谢您更新 jsfiddle。在 Chrome 中它可以正常工作,但在 Firefox 中,它会将图像在浏览器中打开,而不是在画布上显示。 - wclark
2
@AhammadaliPK的回答有效,除了我需要在handleDrop函数中放置一个preventDefault调用才能使其在Firefox上正常工作。 - TexasNeo
我还没有在移动设备上测试过这个,我认为你也需要监听触摸事件。 - AhammadaliPK
你的 JS Fiddle 没有在准确的位置落下。 - Krishna Karki

3

嗯,这个问题相当古老^^

我已经更新了这个代码片段,在Firefox中也可以正常工作。

代码片段

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

    e.preventDefault(); //I've altert this line for FireFox

你能更新一下代码吗?这些解决方案都对我无效。我已经试了至少一个星期,想找到一个可行的方案。我已经完全复制了代码,但在Safari或Chrome测试时它就是不起作用。如果你能帮忙解决这个问题,我会非常感激。使用的是Fabricjs 3.4.0版本。 - user1204493

1

接受的答案已经不再适用。

这是用于从桌面拖放的dataTransfer接口。

canvas.on('drop', function(event) {
    
    // prevent the file to open in new tab
    
    event.e.stopPropagation();
    event.e.stopImmediatePropagation();
    event.e.preventDefault();

    // Use DataTransfer interface to access the file(s)
    
    if(event.e.dataTransfer.files.length > 0){

        var files = event.e.dataTransfer.files;
        
        for (var i = 0, f; f = files[i]; i++) {

            // Only process image files.
            
            if (f.type.match('image.*')) {            
                
                // Read the File objects in this FileList.
                
                var reader = new FileReader();
                
                // listener for the onload event
                
                reader.onload = function(evt) {
                    
                    // put image on canvas
                    
                    fabric.Image.fromURL(evt.target.result, function(obj) {
                        
                        obj.scaleToHeight(canvas.height);
        
                        obj.set('strokeWidth',0);
                        
                        canvas.add(obj);       
                    
                    });
                };
               
                // Read in the image file as a data URL.
               
                reader.readAsDataURL(f);
            }
        }
    }  
}); 

资源

https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop


1
太棒了,工作得很顺利,谢谢! - undefined

0
据我所知,fabricjs尚未提供拖放功能, 操作它将是非常有趣的。 我新手JavaScript和fabricJS,但我认为这个fiddle可能会有所帮助fiddle HTML代码
<div class="img_cont">
        <img class="img" id="ele1" draggable="true" ondragstart="dragElement(event)" src="https://webkit.org/wp-content/uploads/STP-300x300.png"> 
        <img class="img" id="ele2" draggable="true" ondragstart="dragElement(event)" src="https://webkit.org/wp-content/uploads/ephy-webkit-graphic.png"> 
         <img class="img" id="ele3" draggable="true" ondragstart="dragElement(event)" src="https://res.cloudinary.com/css-tricks/image/upload/w_600,q_auto,f_auto/buysellads/uu/7/112766/1646327381-MC_CSSTricks_Logo_600x600-_1_.png">
         <img class="img" id="ele4" draggable="true" ondragstart="dragElement(event)" src="https://miro.medium.com/max/1400/1*9hd_8qR0CMZ8L0pVbFLjDw.png"> 
    </div>
    <br>
    <div id="canvas_cont" ondragover="allowDrop(event)" ondrop="dropElement(event)">
        <canvas id="canvas" width="650" height="350" ></canvas>
    </div>

JavaScript 代码

// allowDrop function called on ondragover event.
function allowDrop(e) {
  e.preventDefault();
}
//dragElement function called on ondrag event.
function dragElement(e) {
  e.dataTransfer.setData("id", e.target.id); //transfer the "data" i.e. id of the target dragged.
}
//Initializing fabric canvas on window load event.
var canvas;
window.onload = function(){
    canvas = new fabric.Canvas(document.getElementById("canvas"));
}
//dropElement function called on ondrop event.
function dropElement(e) {
  e.preventDefault();
  var data = e.dataTransfer.getData("id"); //receiving the "data" i.e. id of the target dropped.
  var imag = document.getElementById(data); //getting the target image info through its id. 
  var img = new fabric.Image(imag, { //initializing the fabric image.
    left: e.layerX - 80,  //positioning the target on exact position of mouse event drop through event.layerX,Y.
    top: e.layerY - 40,
  });
  img.scaleToWidth(imag.width); //scaling the image height and width with target height and width, scaleToWidth, scaleToHeight fabric inbuilt function. 
  img.scaleToHeight(imag.height);
  canvas.add(img); 
}

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