防止画布对象发生碰撞或交叉

2024-02-26

我正在画布上绘制 n 个矩形。矩形是可拖动和可缩放的。 我想防止它们重叠或相交。最好的情况是,如果它们只是相互咬合。

我想办法检查一下十字路口。在我的示例中,我将触摸对象的不透明度设置为 0.1。

巧合的是,在我尝试解决这个问题时,我的对象在接触另一个对象时无法释放。看http://jsfiddle.net/gcollect/jZw7P/ http://jsfiddle.net/gcollect/jZw7P/这是因为第 91 行警报没有执行。alert(math.abs(distx));

实际上这是一种解决方案,但绝对不是一个优雅的解决方案。

有任何想法吗?


这是基于 gco 的答案,已更新以与 FabricJS 1.5.0 配合使用,并进行了以下改进:

  • 形状不重叠。
  • 捕捉更加灵敏。
  • 形状包含在画布内。

JSFiddle:https://jsfiddle.net/aphillips8/31qbr0vn/1/ https://jsfiddle.net/aphillips8/31qbr0vn/1/

var canvas = new fabric.Canvas('canvas'),
canvasWidth = document.getElementById('canvas').width,
canvasHeight = document.getElementById('canvas').height,
counter = 0,
rectLeft = 0,
snap = 20; //Pixels to snap

canvas.selection = false;
plusrect();
plusrect();
plusrect();

function plusrect(top, left, width, height, fill) {
    var rect = new fabric.Rect({
        top: 300,
        name: 'rectangle ' + counter,
        left: 0 + rectLeft,
        width: 100,
        height: 100,
        fill: 'rgba(' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ', 0.75)',
        lockRotation: true,
        originX: 'left',
        originY: 'top',
        cornerSize: 15,
        hasRotatingPoint: false,
        perPixelTargetFind: true,
        minScaleLimit: 1,
        maxWidth: canvasWidth,
        maxHeight: canvasHeight
    });

    rect.custom = {};
    rect.custom.counter = counter;

    canvas.add(rect);
    counter++;
    rectLeft += 200;
}

function findNewPos(distX, distY, target, obj) {
    // See whether to focus on X or Y axis
    if(Math.abs(distX) > Math.abs(distY)) {
        if (distX > 0) {
            target.setLeft(obj.getLeft() - target.getWidth());
        } else {
            target.setLeft(obj.getLeft() + obj.getWidth());
        }
    } else {
        if (distY > 0) {
            target.setTop(obj.getTop() - target.getHeight());
        } else {
            target.setTop(obj.getTop() + obj.getHeight());
        }
    }
}

canvas.on('object:moving', function (options) {
    // Sets corner position coordinates based on current angle, width and height
    options.target.setCoords();

    // Don't allow objects off the canvas
    if(options.target.getLeft() < snap) {
        options.target.setLeft(0);
    }

    if(options.target.getTop() < snap) {
        options.target.setTop(0);
    }

    if((options.target.getWidth() + options.target.getLeft()) > (canvasWidth - snap)) {
        options.target.setLeft(canvasWidth - options.target.getWidth());
    }

    if((options.target.getHeight() + options.target.getTop()) > (canvasHeight - snap)) {
        options.target.setTop(canvasHeight - options.target.getHeight());
    }

    // Loop through objects
    canvas.forEachObject(function (obj) {
        if (obj === options.target) return;

        // If objects intersect
        if (options.target.isContainedWithinObject(obj) || options.target.intersectsWithObject(obj) || obj.isContainedWithinObject(options.target)) {

            var distX = ((obj.getLeft() + obj.getWidth()) / 2) - ((options.target.getLeft() + options.target.getWidth()) / 2);
            var distY = ((obj.getTop() + obj.getHeight()) / 2) - ((options.target.getTop() + options.target.getHeight()) / 2);

            // Set new position
            findNewPos(distX, distY, options.target, obj);
        }

        // Snap objects to each other horizontally

        // If bottom points are on same Y axis
        if(Math.abs((options.target.getTop() + options.target.getHeight()) - (obj.getTop() + obj.getHeight())) < snap) {
            // Snap target BL to object BR
            if(Math.abs(options.target.getLeft() - (obj.getLeft() + obj.getWidth())) < snap) {
                options.target.setLeft(obj.getLeft() + obj.getWidth());
                options.target.setTop(obj.getTop() + obj.getHeight() - options.target.getHeight());
            }

            // Snap target BR to object BL
            if(Math.abs((options.target.getLeft() + options.target.getWidth()) - obj.getLeft()) < snap) {
                options.target.setLeft(obj.getLeft() - options.target.getWidth());
                options.target.setTop(obj.getTop() + obj.getHeight() - options.target.getHeight());
            }
        }

        // If top points are on same Y axis
        if(Math.abs(options.target.getTop() - obj.getTop()) < snap) {
            // Snap target TL to object TR
            if(Math.abs(options.target.getLeft() - (obj.getLeft() + obj.getWidth())) < snap) {
                options.target.setLeft(obj.getLeft() + obj.getWidth());
                options.target.setTop(obj.getTop());
            }

            // Snap target TR to object TL
            if(Math.abs((options.target.getLeft() + options.target.getWidth()) - obj.getLeft()) < snap) {
                options.target.setLeft(obj.getLeft() - options.target.getWidth());
                options.target.setTop(obj.getTop());
            }
        }

        // Snap objects to each other vertically

        // If right points are on same X axis
        if(Math.abs((options.target.getLeft() + options.target.getWidth()) - (obj.getLeft() + obj.getWidth())) < snap) {
            // Snap target TR to object BR
            if(Math.abs(options.target.getTop() - (obj.getTop() + obj.getHeight())) < snap) {
                options.target.setLeft(obj.getLeft() + obj.getWidth() - options.target.getWidth());
                options.target.setTop(obj.getTop() + obj.getHeight());
            }

            // Snap target BR to object TR
            if(Math.abs((options.target.getTop() + options.target.getHeight()) - obj.getTop()) < snap) {
                options.target.setLeft(obj.getLeft() + obj.getWidth() - options.target.getWidth());
                options.target.setTop(obj.getTop() - options.target.getHeight());
            }
        }

        // If left points are on same X axis
        if(Math.abs(options.target.getLeft() - obj.getLeft()) < snap) {
            // Snap target TL to object BL
            if(Math.abs(options.target.getTop() - (obj.getTop() + obj.getHeight())) < snap) {
                options.target.setLeft(obj.getLeft());
                options.target.setTop(obj.getTop() + obj.getHeight());
            }

            // Snap target BL to object TL
            if(Math.abs((options.target.getTop() + options.target.getHeight()) - obj.getTop()) < snap) {
                options.target.setLeft(obj.getLeft());
                options.target.setTop(obj.getTop() - options.target.getHeight());
            }
        }
    });

    options.target.setCoords();

    // If objects still overlap

    var outerAreaLeft = null,
    outerAreaTop = null,
    outerAreaRight = null,
    outerAreaBottom = null;

    canvas.forEachObject(function (obj) {
        if (obj === options.target) return;

        if (options.target.isContainedWithinObject(obj) || options.target.intersectsWithObject(obj) || obj.isContainedWithinObject(options.target)) {

            var intersectLeft = null,
            intersectTop = null,
            intersectWidth = null,
            intersectHeight = null,
            intersectSize = null,
            targetLeft = options.target.getLeft(),
            targetRight = targetLeft + options.target.getWidth(),
            targetTop = options.target.getTop(),
            targetBottom = targetTop + options.target.getHeight(),
            objectLeft = obj.getLeft(),
            objectRight = objectLeft + obj.getWidth(),
            objectTop = obj.getTop(),
            objectBottom = objectTop + obj.getHeight();

            // Find intersect information for X axis
            if(targetLeft >= objectLeft && targetLeft <= objectRight) {
                intersectLeft = targetLeft;
                intersectWidth = obj.getWidth() - (intersectLeft - objectLeft);

            } else if(objectLeft >= targetLeft && objectLeft <= targetRight) {
                intersectLeft = objectLeft;
                intersectWidth = options.target.getWidth() - (intersectLeft - targetLeft);
            }

            // Find intersect information for Y axis
            if(targetTop >= objectTop && targetTop <= objectBottom) {
                intersectTop = targetTop;
                intersectHeight = obj.getHeight() - (intersectTop - objectTop);

            } else if(objectTop >= targetTop && objectTop <= targetBottom) {
                intersectTop = objectTop;
                intersectHeight = options.target.getHeight() - (intersectTop - targetTop);
            }

            // Find intersect size (this will be 0 if objects are touching but not overlapping)
            if(intersectWidth > 0 && intersectHeight > 0) {
                intersectSize = intersectWidth * intersectHeight;
            }

            // Set outer snapping area
            if(obj.getLeft() < outerAreaLeft || outerAreaLeft == null) {
                outerAreaLeft = obj.getLeft();
            }

            if(obj.getTop() < outerAreaTop || outerAreaTop == null) {
                outerAreaTop = obj.getTop();
            }

            if((obj.getLeft() + obj.getWidth()) > outerAreaRight || outerAreaRight == null) {
                outerAreaRight = obj.getLeft() + obj.getWidth();
            }

            if((obj.getTop() + obj.getHeight()) > outerAreaBottom || outerAreaBottom == null) {
                outerAreaBottom = obj.getTop() + obj.getHeight();
            }

            // If objects are intersecting, reposition outside all shapes which touch
            if(intersectSize) {
                var distX = (outerAreaRight / 2) - ((options.target.getLeft() + options.target.getWidth()) / 2);
                var distY = (outerAreaBottom / 2) - ((options.target.getTop() + options.target.getHeight()) / 2);

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

防止画布对象发生碰撞或交叉 的相关文章

随机推荐

  • pch 上的 #import 是否会减慢编译时间?

    我正在读这个post http qualitycoding org import order 关于进口 我有一个问题 默认情况下 prefix pch 文件中的 import 是否会减慢编译时间 我应该删除它并仅在必要时导入吗 ifdef
  • 在 NestJS 中使用与 GraphQL 中的输入和对象类型相同的类

    我正在尝试设置我的 graphql resover 来处理对象数组 但无法配置 Args 装饰器 我创建了自己的 ArgsType import ArgsType Field Int ObjectType from nestjs graph
  • 从 github 中删除文件

    我已经在一个项目中使用 github 一段时间了 但是仅作为问题跟踪器 因此 今晚我也花了一些时间来处理代码 并犯了一个菜鸟错误 我提交了凭证文件和其他不必要的数据 我已将它们添加到 gitignore现在提交文件 它们应该不再是问题 但我
  • Docker:如何 Dockerize 并部署 LAMP 应用程序的多个实例

    我需要部署同一 LAMP 或 LEMP 应用程序的多个实例 每个实例都可以通过前端负载均衡器 代理从子域访问 每个实例必须有自己的数据库数据和文件数据 每个实例都可能受到监视 内存限制 CPU 可能会针对每个应用程序实例进行设置 轻松自动部
  • 从 Oracle 存储过程调用 os_command.exec

    我用过os command exec向 Linux shell 发送命令 我正在使用 Oracle 12c 这是一个运行良好的示例代码 select os command exec home smucha app smucha produc
  • Python 3 查找字符串中的最后一个数字

    如何找到任何大字符串中的最后一个数字 例如 在以下字符串中 我想要 47 作为输出 tr bgcolor aa77bb td gt font face verdana color white size 2 gt b gt Total b g
  • WPF Tabcontrol 获取最大选项卡的大小

    我有一个绑定到 Viewmodel 集合的 TabControl 这些 Viewmodel 被转换为适当的值以绘制到选项卡中
  • CosmosDB 存储过程 - Promise 而不是回调

    是否可以在 CosmosDB DocumentDB 存储过程 API 调用中使用 JavaScript Promise 而不是常规回调 一种用法是实现分页 例如 token getToken doSomething token functi
  • Flutter - iOS:命令 /bin/sh 失败,退出代码为 255

    下午好 我尝试在 iOS 上运行一个 Flutter 项目 之前在 Android 上成功运行过 但是 我收到一个错误 x86 64 is not an allowed value for option ios arch Run flutt
  • Delphi 获取 EXE 的句柄

    这是我现在正在做的一个例子 var Client String Handle Integer begin Client Window Name GetWindowThreadProcessId FindWindow nil PAnsiCha
  • 从node.js消息调用Windows API

    我是 Node 新手 我有这个简单的 Node js 服务器可以在 Windows 上运行 服务器代码 var ws require websocket server var server ws createServer server ad
  • 根据原始数据创建队列退出率表

    我需要帮助根据原始数据创建队列退出表 我有一个如下所示的数据集 DT lt data table id c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
  • Chrome扩展-通过后台脚本获取Active Tab的DOM信息

    我知道有很多与这个主题相关的问题 但到目前为止 没有一个问题能让我在 V3 中解决这个问题 当我运行以下background js时 我只得到未定义的结果 我的扩展的目标 至少在这个阶段 是抓取活动选项卡的 DOM 并提取所有 div 元素
  • 如何通过https访问kubernetes服务?

    这是我的集群信息 kubectl cluster info Kubernetes master is running at https 129 146 10 66 6443 Heapster is running at https 129
  • 本地运行 web-Socket 进行调试

    我正在使用 gorilla web socket 我想在本地运行它 我的意思是使用以下 chrome 客户端 或其他推荐的工具 当我进入调试模式时出现错误 I use github com gorilla websocket var upg
  • 如何使用mysql将查询结果存储在变量中

    SET v1 SELECT COUNT FROM user rating SELECT v1 当我执行此查询时set变量显示此错误 Error Code 1064 You have an error in your SQL syntax c
  • 如何简单地扩展 docker-compose 服务并将索引和计数传递给每个服务?

    我正在寻找一种扩展 docker compose 服务的方法并看到了 scale 选项 但找不到任何方法来获取每个容器内的索引和计数 这是一个简化的撰写文件 version 2 1 services my thing restart alw
  • Java OpenCV 绑定

    我正在寻找 OpenCV java 绑定 所有引用都指向处理库 我知道处理是java 但是没有独立的java库吗 或者应该只使用处理库 我刚刚找到了这个 OpenCV 的 java 包装器 https github com bytedeco
  • Visual C++:将传统 C 和 C++ 字符串代码迁移到 Unicode 世界

    我看到 Visual Studio 2008 及更高版本现在开始使用字符集设置为 Unicode 的新解决方案 我的旧 C 代码仅处理英文 ASCII 文本 并且充满了 文字字符串如 Hello World char type char 指
  • 防止画布对象发生碰撞或交叉

    我正在画布上绘制 n 个矩形 矩形是可拖动和可缩放的 我想防止它们重叠或相交 最好的情况是 如果它们只是相互咬合 我想办法检查一下十字路口 在我的示例中 我将触摸对象的不透明度设置为 0 1 巧合的是 在我尝试解决这个问题时 我的对象在接触