如何在 Fabric.js 中的画布之间拖放

2024-01-05

据我所知,Fabric.js 内置了对同一画布内拖放操作的支持。

我们怎样才能让它适用于多个画布?

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


在 Fabric.js 中可以在画布之间进行拖放,但涉及对私有属性的一些操作。为此原因不保证它可以与 Fabric.js 的未来版本一起使用.

工作演示: https://jsfiddle.net/mmalex/kdbu9f3y/ https://jsfiddle.net/mmalex/kdbu9f3y/

视频截取: https://youtu.be/nXZgCmIrpqQ https://youtu.be/nXZgCmIrpqQ

主要特征:

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

✓ 可以在画布之间来回拖动而不中断,

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


Step 1– 准备画布,加载图像,安排演示的一切:

    //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- 订阅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("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– 解决方案的核心是在画布之间迁移对象而不中断鼠标操作。很难解释,直接看代码中的注释就可以了。

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

玩得开心!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 Fabric.js 中的画布之间拖放 的相关文章

  • 如何使用 jQuery 将各种元素包装在 div 标签中?

    我有一个 html 结构 如下所示 h5 Title h5 p Content p ul li Item li li Item li ul p Content p h5 Title h5 p Content p ul li Item li
  • Protractor - 等待多个元素

    我正在尝试等待页面上的多个元素 我不知道可能有多少个 但至少会有一个 我知道使用以下命令等待单个元素 效果很好 var EC protractor ExpectedConditions browser wait EC presenceOf
  • javascript获取网页中选定文本的段落

    突出显示文本后 我想获取所选文本所在的段落 var select window content document getSelection 请问有什么指点吗 这实际上很难做到 因为你必须考虑六种情况 所选内容不在一个段落内 简单 整个选择都
  • 更改 Angular 模型以更新 Kendo

    我一直在一个项目中使用 Angular 最近才发现 Kendo Angular 项目位于http kendo labs github io angular kendo http kendo labs github io angular ke
  • 如何使链接悬停时的背景图像模糊?

    当您用鼠标光标悬停链接时 我想让我的背景图像模糊 5 像素 有什么简单的方法可以实现这一点吗 我有点纠结于类和 id 在这里 pic background url http www metalinjection net wp content
  • 如何使用ckeditore通过ajax发送数据?

    我在 django 中有一个表格 这是 撰写邮件 形式 我将此表单从视图发送到我的模板 然后应用 ckeditor 来更改正文样式 我希望通过 ajax 发布此表单 当使用 ckeditor 时 body 字段的值不会随 request P
  • Javascript/Node 中从不执行用户代码的隐藏线程:是否可能,如果可能,是否会导致竞争条件的神秘可能性?

    根据评论 答案 请参阅问题底部的更新 这个问题实际上是关于可能性的hidden不执行回调的线程 我有一个关于潜在的神秘场景的问题 涉及节点请求模块 https www npmjs com package request其中 A 构建完整的
  • 在 JavaScript/ActionScript 中重新定义 Math.constructor 有任何实际用途吗?

    Math 对象没有原型属性 但有构造函数属性 在任何情况下重新定义构造函数会有用吗 The Math对象 准确地说 由初始值引用的对象MathECMAScript 全局对象的属性 not have a constructor属性 请参阅EC
  • 有什么简单的方法可以清理 Google Maps v3 API 上的所有标记、折线和其他叠加层吗?

    我想获得一张新地图 而不是使用刷新网页 thanks 并有简单的方法来获取地图上的所有叠加层 在 v2 API 中 有clearOverlays http code google com apis maps documentation ja
  • 如何使用 JavaScript 禁用滚动条?

    当我仅在 Internet Explorer 7 中显示代表模式窗口的 div 时 我需要锁定浏览器滚动条 谷歌搜索我发现我可以使用document body style overflow hidden 但这不适用于 IE7 我也尝试过do
  • 解析 PHP 响应:未捕获的语法错误:意外的标记 <

    我正在使用 AJAX 来调用 PHP 脚本 我唯一需要从响应中解析的是脚本生成的随机 ID 问题是 PHP 脚本会引发许多错误 这些错误实际上很好 不会妨碍程序功能 唯一的问题是当我跑步时 parseJSON response I get
  • 谷歌地图API v3如何获取所有形状的坐标

    我有这个谷歌脚本 可以创建形状和删除形状 但没有太多关于保存形状的信息 我查了一下互联网 知道我可以通过 overlaycomplete 中的 getpaths 访问路径坐标 而且我还可以将坐标推入一个收集所有形状的数组中 但是 如果用户删
  • 变量值的 swap() 函数[重复]

    这个问题在这里已经有答案了 我无法达到下面这个交换函数的预期结果 我希望将值打印为 3 2 function swap x y var t x x y y t console log swap 2 3 任何线索将不胜感激 您的函数正在内部交
  • 带有子节点的拖放区域

    我有一个带有多个子节点的拖放区域 主要元素有dropenter and dropleave事件 但是 如果您将文件拖动到主元素内部和子节点上方 则dropleave被触发 如何处理 以便dropleave仅当拖动的元素和鼠标位于主元素之外时
  • 创建响应式眼睛焦点图标

    我一直在尝试制作响应式彩色眼睛焦点图标 但到目前为止我所尝试的一切均不成功 我试图在某种程度上复制真眼的颜色 我使用边框 框阴影来获取颜色 但该部分没有缩放 也尝试过轮廓 但也失败了 那个甚至不是圆的 div 的高度当前是静态的 但我希望它
  • Electron Auth0Lock“原始文件://不允许”

    尝试让 auth0 与我的电子应用程序一起使用 当我按照默认教程并尝试使用用户名 密码 身份验证进行身份验证时 锁定失败并显示 403 错误 并响应 不允许使用 Origin file 我还在 auth0 仪表板中客户端设置的允许来源 CO
  • HTML 布局:向现有网站添加侧边栏

    我有一个网站 其正文如下所示 div div div div div div 这些中没有使用绝对 相对定位技巧divs 但是有很多floats clears margins and padding这些风格divs 及其内部元素 所有这些都会
  • 保存 dat.gui 预设以动态添加控件?

    我正在向 dat gui 界面动态添加控件 但 保存设置 功能无法识别它们 var mygui new dat GUI mygui remember mygui standard way of adding a control mygui
  • 从另一台服务器读取 Node.js 中的大文件

    我有两台相互通信的服务器 Server1 向 Server2 请求文件的部分内容 并将收到的数据存储到一个文件中 Server2 应该接收每个请求并创建一个流管道传输数据 假设服务器2中存储的文件 目录 如下 bigfile gz bigf
  • jQuery可排序发布数据,但没有数据

    谁能告诉我我在这里缺少什么 我的数据似乎总是空的 我做错了什么 document ready function nav sortable connectWith nav axis y update function event ui var

随机推荐