如何在Fabric.js中的画布之间拖放 [英] How to drag and drop between canvases in Fabric.js

查看:264
本文介绍了如何在Fabric.js中的画布之间拖放的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我了解Fabric.js对同一画布内的拖放具有内置的支持。



我们如何使它适用于多个画布?



或来自非画布html元素,例如表中的图像?

解决方案

在Fabric.js中可以在画布之间拖放,但是涉及对私有属性的一些操作。由于这个原因,不能保证它可以在Fabric.js的未来版本中使用。



工作演示:



主要功能:



✓可以在任意数量的画布(不仅是两个)之间拖放,



✓可以在画布之间来回拖动而不会受到干扰,



✓可以在不中断操作的情况下变换(镜像)掉落的图像。






步骤1 –准备画布,加载即时消息年龄,安排一切进行演示:

  //创建两个画布
var canvas0El = document.getElementById( c0);
canvas0El.width = canvas0El.offsetWidth;
canvas0El.height = canvas0El.parentElement.offsetHeight;

var canvas1El = document.getElementById( c1);
canvas1El.width = canvas1El.offsetWidth;
canvas1El.height = canvas1El.parentElement.offsetHeight;

var canvas0 =新布料。Canvas(’c0’);
canvas0.setBackgroundColor(’rgba(19,19,19,0.25)’);
canvas0.renderAll();

var canvas1 =新布料。Canvas(’c1’);
canvas1.setBackgroundColor(’rgba(92,18,18,0.25)’);
canvas1.renderAll();

//在左画布上添加加载的图像
var onImageLoaded = function(oImg){
oImg.originX = center;
oImg.originY =中心;

oImg.left = this.x;
oImg.top = this.y;

canvas0.add(oImg);
oImg.canvas = canvas0;
imgArrow = oImg;
};

var config = {crossOrigin:‘anonymous’};

var baseUrl = http://mbnsay.com/rayys/images;
var url0 = baseUrl + /arrow-right-green.png;
var url1 = baseUrl + /arrow-right-icon.png;
var url2 = baseUrl + /arrow-right-blue.png;

//加载一些图像
fabric.Image.fromURL(url0,onImageLoaded.bind({x:56,y:96}),config);
fabric.Image.fromURL(url0,onImageLoaded.bind({x:156,y:96}),config);

fabric.Image.fromURL(url1,onImageLoaded.bind({x:56,y:2 * 96}),config);
fabric.Image.fromURL(url1,onImageLoaded.bind({x:156,y:2 * 96}),config);

fabric.Image.fromURL(url2,onImageLoaded.bind({x:56,y:3 * 96}),config);
fabric.Image.fromURL(url2,onImageLoaded.bind({x:156,y:3 * 96}),config);

第2步 –订阅 object:moving 事件在两个画布上,并观察对象中心何时跨越画布边界。当对象越过边界时,必须将其


  1. 从源画布中删除,

  2. 粘贴到目标画布,

  3. 迁移内部画布转换(将单独说明)



  var onObjectMoving = function(p){
var viewport = p.target.canvas.calcViewportBoundaries();

if(p.target.canvas === canvas0){
if(p.target.left> viewport.br.x){
console.log(迁移:左->中心);
migrateItem(canvas0,canvas1,p.target);
的回报;
}
}
if(p.target.canvas === canvas1){
if(p.target.left< viewport.tl.x){
console.log(迁移:居中->左侧);
migrateItem(canvas1,canvas0,p.target);
的回报;
}
}
};

canvas0.on( object:moving,onObjectMoving);
canvas1.on( object:moving,onObjectMoving);

第3步 –解决方案的核心是在画布之间迁移对象中断鼠标操作。很难解释,只需遵循代码中的注释即可。

  var migrateItem = function(fromCanvas,toCanvas,endingImage){
//从Canvas.remove(pendingImage);删除旧画布
中的图像

//我们将欺骗fabric.js,
//因此,我们保留源画布的内部转换,
//以便将其注入到目标画布中。
varendingTransform = fromCanvas._currentTransform;
fromCanvas._currentTransform = null;

//为fabric.util.removeListener和fabric.util.addListener创建快捷方式
var removeListener = fabric.util.removeListener;
var addListener = fabric.util.addListener;

//重新安排源画布
的订阅{
removeListener(fabric.document,'mouseup',fromCanvas._onMouseUp);
removeListener(fabric.document,'touchend',fromCanvas._onMouseUp);

removeListener(fabric.document,'mousemove',fromCanvas._onMouseMove);
removeListener(fabric.document,'touchmove',fromCanvas._onMouseMove);

addListener(fromCanvas.upperCanvasEl,'mousemove',fromCanvas._onMouseMove);
addListener(fromCanvas.upperCanvasEl,'touchmove',fromCanvas._onMouseMove,{
Passive:false
});

if(isTouchDevice){
//等待500毫秒,然后重新绑定鼠标按下以防止两次触发
//来自触摸设备
var _this = fromCanvas;
setTimeout(function(){
addListener(_this.upperCanvasEl,'mousedown',_this._onMouseDown);
},500);
}
}

//重新安排目标画布的订阅
{
addListener(fabric.document,'touchend',toCanvas._onMouseUp, {
被动:false
});
addListener(fabric.document,'touchmove',toCanvas._onMouseMove,{
Passive:false
});

removeListener(toCanvas.upperCanvasEl,'mousemove',toCanvas._onMouseMove);
removeListener(toCanvas.upperCanvasEl,'touchmove',toCanvas._onMouseMove);

if(isTouchDevice){
//取消绑定鼠标,以防止来自触摸设备的两次触发。
removeListener(toCanvas.upperCanvasEl,'mousedown',toCanvas._onMouseDown);
} else {
addListener(fabric.document,'mouseup',toCanvas._onMouseUp);
addListener(fabric.document,'mousemove',toCanvas._onMouseMove);
}
}

//我们需要这个计时器,因为我们希望Fabric.js在注入之前完成待处理的渲染
//,因为这会引起一些不愉快图像跳跃。
setTimeout(function(){
//将图像添加到目标画布,
endingImage.scaleX * = -1;
endingImage.canvas = toCanvas;
endingImage。 migrated = true;
toCanvas.add(pendingImage);

//并将转换从源画布
注入到Canvas._currentTransform = endingTransform;

/ /因为我们已经镜像了图像,所以我们也将$ b​​ $ b转换为Canvas._currentTransform.scaleX * = -1;
转换为Canvas._currentTransform.original.scaleX * = -1;

//最后不要忘记选择要粘贴的对象
到Canvas.setActiveObject(pendingImage);
},10);
};

玩得开心!


I understand Fabric.js have built-in support on drag-n-drop within the same canvas.

How can we make it work for multiple canvas?

Or from an non-canvas html element e.g. an image from a table?

解决方案

Drag and drop between canvases is possible in Fabric.js, but involves some manipulation of private properties. For this reason it is not guaranteed to be functional with future versions of Fabric.js.

Working demo: https://jsfiddle.net/mmalex/kdbu9f3y/

Video capture: https://youtu.be/nXZgCmIrpqQ

Key features:

✓ can drag and drop between any number of canvases (not only two),

✓ can drag back and forth between canvases without interruption,

✓ can transform (mirror) dropped image without interruption of manipulation.


Step 1 – prepare canvases, load images, arrange everything for demo:

    //create two canvases
    var canvas0El = document.getElementById("c0");
    canvas0El.width = canvas0El.offsetWidth;
    canvas0El.height = canvas0El.parentElement.offsetHeight;

    var canvas1El = document.getElementById("c1");
    canvas1El.width = canvas1El.offsetWidth;
    canvas1El.height = canvas1El.parentElement.offsetHeight;

    var canvas0 = new fabric.Canvas('c0');
    canvas0.setBackgroundColor('rgba(19, 19, 19, 0.25)');
    canvas0.renderAll();

    var canvas1 = new fabric.Canvas('c1');
    canvas1.setBackgroundColor('rgba(92, 18, 18, 0.25)');
    canvas1.renderAll();

    // add loaded image on left canvas
    var onImageLoaded = function(oImg) {
        oImg.originX = "center";
        oImg.originY = "center";

        oImg.left = this.x;
        oImg.top = this.y;

        canvas0.add(oImg);
        oImg.canvas = canvas0;
        imgArrow = oImg;
    };

    var config = { crossOrigin: 'anonymous' };

    var baseUrl = "http://mbnsay.com/rayys/images";
    var url0 = baseUrl + "/arrow-right-green.png";
    var url1 = baseUrl + "/arrow-right-icon.png";
    var url2 = baseUrl + "/arrow-right-blue.png";

    // load some images
    fabric.Image.fromURL(url0, onImageLoaded.bind({ x: 56,  y: 96 }), config);
    fabric.Image.fromURL(url0, onImageLoaded.bind({ x: 156, y: 96 }), config);

    fabric.Image.fromURL(url1, onImageLoaded.bind({ x: 56,  y: 2*96 }), config);
    fabric.Image.fromURL(url1, onImageLoaded.bind({ x: 156, y: 2*96 }), config);

    fabric.Image.fromURL(url2, onImageLoaded.bind({ x: 56,  y: 3*96 }), config);
    fabric.Image.fromURL(url2, onImageLoaded.bind({ x: 156, y: 3*96 }), config);

Step 2 – subscribe object:moving events on both canvases, and watch when object center crossing the canvas border. When object crosses the border, it has to be

  1. remove from source canvas,
  2. paste into destination canvas,
  3. migrate internal canvas transformations (will be explained separately)

    var onObjectMoving = function(p) {
        var viewport = p.target.canvas.calcViewportBoundaries();

        if (p.target.canvas === canvas0) {
            if (p.target.left > viewport.br.x) {
                console.log("Migrate: left -> center");
                migrateItem(canvas0, canvas1, p.target);
                return;
            }
        }
        if (p.target.canvas === canvas1) {
            if (p.target.left < viewport.tl.x) {
                console.log("Migrate: center -> left");
                migrateItem(canvas1, canvas0, p.target);
                return;
            }
        }
    };

    canvas0.on("object:moving", onObjectMoving);
    canvas1.on("object:moving", onObjectMoving);

Step 3 – The core of the solution, migrate object between canvases not interrupting mouse manipulations. Hard to explain, just follow the comments in code.

    var migrateItem = function(fromCanvas, toCanvas, pendingImage) {
        // Just drop image from old canvas
        fromCanvas.remove(pendingImage);

        // We're going to trick fabric.js,
        // so we keep internal transforms of the source canvas, 
        // in order to inject it into destination canvas.
        var pendingTransform = fromCanvas._currentTransform;
        fromCanvas._currentTransform = null;

        // Make shortcuts for fabric.util.removeListener and fabric.util.addListener
        var removeListener = fabric.util.removeListener;
        var addListener = fabric.util.addListener;

        // Re-arrange subscriptions for source canvas
        {
            removeListener(fabric.document, 'mouseup', fromCanvas._onMouseUp);
            removeListener(fabric.document, 'touchend', fromCanvas._onMouseUp);

            removeListener(fabric.document, 'mousemove', fromCanvas._onMouseMove);
            removeListener(fabric.document, 'touchmove', fromCanvas._onMouseMove);

            addListener(fromCanvas.upperCanvasEl, 'mousemove', fromCanvas._onMouseMove);
            addListener(fromCanvas.upperCanvasEl, 'touchmove', fromCanvas._onMouseMove, {
                passive: false
            });

            if (isTouchDevice) {
                // Wait 500ms before rebinding mousedown to prevent double triggers
                // from touch devices
                var _this = fromCanvas;
                setTimeout(function() {
                    addListener(_this.upperCanvasEl, 'mousedown', _this._onMouseDown);
                }, 500);
            }
        }

        // Re-arrange subscriptions for destination canvas
        {
            addListener(fabric.document, 'touchend', toCanvas._onMouseUp, {
                passive: false
            });
            addListener(fabric.document, 'touchmove', toCanvas._onMouseMove, {
                passive: false
            });

            removeListener(toCanvas.upperCanvasEl, 'mousemove', toCanvas._onMouseMove);
            removeListener(toCanvas.upperCanvasEl, 'touchmove', toCanvas._onMouseMove);

            if (isTouchDevice) {
                // Unbind mousedown to prevent double triggers from touch devices
                removeListener(toCanvas.upperCanvasEl, 'mousedown', toCanvas._onMouseDown);
            } else {
                addListener(fabric.document, 'mouseup', toCanvas._onMouseUp);
                addListener(fabric.document, 'mousemove', toCanvas._onMouseMove);
            }
        }

        // We need this timer, because we want Fabric.js to complete pending render
        // before we inject, because it causes some unpleasant image jumping.
        setTimeout(function() {
            // Add image to destination canvas,
            pendingImage.scaleX *= -1;
            pendingImage.canvas = toCanvas;
            pendingImage.migrated = true;
            toCanvas.add(pendingImage);

            // and inject transforms from source canvas
            toCanvas._currentTransform = pendingTransform;

            // as we have mirrored the image, we mirror transforms too
            toCanvas._currentTransform.scaleX *= -1;
            toCanvas._currentTransform.original.scaleX *= -1;

            // finally don't forget to make pasted object selected
            toCanvas.setActiveObject(pendingImage);
        }, 10);
    };

Have fun!

这篇关于如何在Fabric.js中的画布之间拖放的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆