将一个旋转对象包含在另一个旋转对象中 FabricJS

2024-01-07

我有两个对象,一个父对象(红色)和一个子对象(蓝色)。父对象是固定的,不能移动,只有子对象是可移动的,并且子对象总是比父对象大。无论以何种方式移动子对象,它都应该始终包含在子对象内部,这意味着我们永远不应该看到红色矩形。

Demo: https://codesandbox.io/s/force-contain-of-object-inside-another-object-fabric-js-7nt7q https://codesandbox.io/s/force-contain-of-object-inside-another-object-fabric-js-7nt7q

我知道有一些解决方案可以将对象包含在画布或其他对象边界内(例如。在画布边界限制内移动对象 https://stackoverflow.com/questions/22910496/move-object-within-canvas-boundary-limit)主要强制顶部/右侧/底部/左侧值不超过父值,但这里我们有两个物体旋转相同角度的情况.

当用户将照片上传到框架容器时,我有一个现实场景。照片通常总是比相框容器大。用户可以在相框内移动照片,但不应允许他创建任何空白空间,照片应始终包含在相框内。


我会选择纯画布(没有fabricjs),从头开始做,这样你就可以很好地理解你所面临的问题,那么如果你需要它,相同的逻辑应该可以轻松移植到任何库。

你有一些规则:

  • 父对象是固定的,不能移动,
  • 子对象是可移动的。
  • 孩子永远比父母大。
  • 子对象始终受到父对象的约束。

所以我的想法是获取所有四个角,这样在移动时我们可以使用这些坐标来确定它是否可以移动到新位置,代码应该很容易遵循,但是询问您是否有任何顾虑。

我正在使用光线投射算法:
https://github.com/substack/point-in-polygon/blob/master/index.js https://github.com/substack/point-in-polygon/blob/master/index.js
这样,我们需要做的就是检查子项的角是否不在父项的内部,并且父项是否在子项的内部,仅此而已。

我不是 FabricJS 专家,所以我最好的可能不是很多......
但下面是我尝试让你的代码运行的尝试。

<canvas id="canvas" width="500" height="350"></canvas>

<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js"></script>
<script>
    var canvas = new fabric.Canvas("canvas");
    canvas.stateful = true;

    function getCoords(rect) {
        var x = rect.left;
        var y = rect.top;
        var angle = (rect.angle * Math.PI) / 180;

        var coords = [{ x, y }];
        x += rect.width * Math.cos(angle);
        y += rect.width * Math.sin(angle);
        coords.push({ x, y });

        angle += Math.PI / 2;
        x += rect.height * Math.cos(angle);
        y += rect.height * Math.sin(angle);
        coords.push({ x, y });

        angle += Math.PI / 2;
        x += rect.width * Math.cos(angle);
        y += rect.width * Math.sin(angle);
        coords.push({ x, y });
        return coords;
    }

    function inside(p, vs) {
        var inside = false;
        for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {
            var xi = vs[i].x, yi = vs[i].y;
            var xj = vs[j].x, yj = vs[j].y;
            var intersect =
                yi > p.y !== yj > p.y && p.x < ((xj - xi) * (p.y - yi)) / (yj - yi) + xi;
            if (intersect) inside = !inside;
        }
        return inside;
    }

    var parent = new fabric.Rect({
        width: 150, height: 100, left: 200, top: 50, angle: 25, selectable: false, fill: "red"
    });
    var pCoords = getCoords(parent);

    var child = new fabric.Rect({
        width: 250, height: 175, left: 180, top: 10, angle: 25, hasControls: false, fill: "rgba(0,0,255,0.9)"
    });

    canvas.add(parent);
    canvas.add(child);

    canvas.on("object:moving", function (e) {
        var cCoords = getCoords(e.target);
        var inBounds = true;
        cCoords.forEach(c => { if (inside(c, pCoords)) inBounds = false; });
        pCoords.forEach(c => { if (!inside(c, cCoords)) inBounds = false; });
        if (inBounds) {
            e.target.setCoords();
            e.target.saveState();
            e.target.set("fill", "rgba(0,0,255,0.9)");            
        } else {
            e.target.set("fill", "black");
            e.target.animate({
                left: e.target._stateProperties.left,
                top: e.target._stateProperties.top
              },{
                duration: 500,
                onChange: canvas.renderAll.bind(canvas),
                easing: fabric.util.ease["easeInBounce"],
                onComplete: function() {
                  e.target.set("fill", "rgba(0,0,255,0.9)");
                }
            });
        }
    });
</script>

该代码也在沙箱上:
https://codesandbox.io/s/force-contain-of-object-inside-another-object-fabric-js-dnvb5 https://codesandbox.io/s/force-contain-of-object-inside-another-object-fabric-js-dnvb5

不用担心对所有点击/按住/拖动结构进行编码,这确实是件好事,这使得这一切变得非常容易......

我正在尝试 FabricJS,画布有一个很好的属性
(canvas.stateful = true;)
这使我们能够跟踪我们去过的地方,如果我们超出界限,我们可以恢复该运动,同时还可以使用动画向用户提供不允许该运动的视觉反馈。

这是另一个没有动画的版本:

<canvas id="canvas" width="500" height="350"></canvas>

<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js"></script>
<script>
    var canvas = new fabric.Canvas("canvas");
    canvas.stateful = true;

    function getCoords(rect) {
        var x = rect.left;
        var y = rect.top;
        var angle = (rect.angle * Math.PI) / 180;

        var coords = [{ x, y }];
        x += rect.width * Math.cos(angle);
        y += rect.width * Math.sin(angle);
        coords.push({ x, y });

        angle += Math.PI / 2;
        x += rect.height * Math.cos(angle);
        y += rect.height * Math.sin(angle);
        coords.push({ x, y });

        angle += Math.PI / 2;
        x += rect.width * Math.cos(angle);
        y += rect.width * Math.sin(angle);
        coords.push({ x, y });
        return coords;
    }

    function inside(p, vs) {
        var inside = false;
        for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {
            var xi = vs[i].x, yi = vs[i].y;
            var xj = vs[j].x, yj = vs[j].y;
            var intersect =
                yi > p.y !== yj > p.y && p.x < ((xj - xi) * (p.y - yi)) / (yj - yi) + xi;
            if (intersect) inside = !inside;
        }
        return inside;
    }

    var parent = new fabric.Rect({
        width: 150, height: 100, left: 200, top: 50, angle: 25, selectable: false, fill: "red"
    });
    var pCoords = getCoords(parent);

    var child = new fabric.Rect({
        width: 250, height: 175, left: 180, top: 10, angle: 25, hasControls: false, fill: "rgba(0,0,255,0.9)"
    });

    canvas.add(parent);
    canvas.add(child);

    canvas.on("object:moving", function (e) {
        var cCoords = getCoords(e.target);
        var inBounds = true;
        cCoords.forEach(c => { if (inside(c, pCoords)) inBounds = false; });
        pCoords.forEach(c => { if (!inside(c, cCoords)) inBounds = false; });
        if (inBounds) {
            e.target.setCoords();
            e.target.saveState();
        } else {
            e.target.left = e.target._stateProperties.left;
            e.target.top = e.target._stateProperties.top;
        }
    });
</script>

该算法也为其他形状打开了大门,这是一个六边形版本:
https://raw.githack.com/heldersepu/hs-scripts/master/HTML/canvas_contained2.html https://raw.githack.com/heldersepu/hs-scripts/master/HTML/canvas_contained2.html

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

将一个旋转对象包含在另一个旋转对象中 FabricJS 的相关文章

随机推荐

  • 使用 Java 进行音频混合(没有 Mixer API)

    我正在尝试混合几种不同的音频流并尝试让它们同时播放代替一次一个 下面的代码一次播放一个 我无法找出不使用 Java Mixer API 的解决方案 不幸的是 我的声卡不支持使用 Mixer API 进行同步 我被迫找出一种通过代码来实现同步
  • ASP.NET 用户控件和 jQuery 对话框

    在我的页面上 我得到了以下用户控件 div class editFormDialog style display none font size 12px div
  • searchkick 索引相关模型字段

    我有一个 Rails 应用程序 我正在从 Sphinx 切换到 ElasticSearch 并使用 gem searchkick 我有一个模型教师和一个模型标签 通过 gem 其中教师可以关联多个标签 在教师模型中 我定义了这样的索引 de
  • Razor MVC 3 RC2 - 带有动态文本的 WebGrid Actionlink

    我正在 WebGrid 中输出带有动态链接文本的 Actionlink 我可以让它工作的唯一方法如下 Grid Column header Subject columnName Message Subject format item gt
  • 在 T O 重写函数中,如何取回它?

    我在 R 会话中做了一些愚蠢的事情 我写 打印 假 现在我无法打印东西 打印 1 错误 我该如何取回它 rm不会删除基础对象 因此您只需运行 rm print 有趣的是 你can打印东西 gt print lt FALSE gt print
  • 根据到期时间获取行

    http sqlfiddle com 9 406cb 2 http sqlfiddle com 9 406cb 2 我有一本分类为 1 2 3 4 的表格书 第 1 类 将停留 1 小时 第 2 类 将停留 2 小时 第 3 类 将停留 4
  • 为tableView注册nib名称

    static NSString cellIdentifier cell if tableView tableview1 ContactCustom cell1 ContactCustom tableView dequeueReusableC
  • 当我在 Unix 中调用 fork() 时会发生什么?

    我试图查找这一点 但在调用 fork 后 我很难理解父进程和子进程之间的关系 它们是完全独立的进程 仅通过 id parent id 关联吗 或者他们共享记忆吗 例如每个进程的 代码 部分 是重复的这样每个进程都有自己相同的副本 或者以某种
  • 使用python将mysql查询写入csv,需要显示字段名称

    我有以下内容 import MySQLdb as dbapi import sys import csv dbServer localhost dbPass supersecretpassword dbSchema dbTest dbUse
  • 获取具有匹配计数条件的嵌套对象的文档

    我是一个 mongo 菜鸟 正在使用 mongo 集合 其记录如下所示 cats name fluffy color red name snowball color white 我想执行一个查询来获取包含超过 1 只白猫的所有记录 MapR
  • 在windows和linux上编译

    我是c语言的新手 有些人给了我一些其他人编写的在windows上编译的源代码 尝试在 linux 上编译后出现错误 因为 linux 不支持 DWORD WORD 和 UINT32 例如我有6个文件 A h A c B h B c C h
  • 使用给定数据点在 Python 中曲线拟合指数衰减函数

    随着curve fit通过 SciPy 中的函数 我能够确定代表下图中所示曲线的系数 def func2 t tau return np exp t tau t2 np linspace 0 4 50 y2 func2 t2 1 2 y2
  • 在 OpenGL 中,仅使用 OpenGL3+ 方法创建透视图的最简单方法是什么?

    乍一看这可能听起来很懒 但我已经研究了两天了 我有一个绘制图元的 SDL GLEW 应用程序 我想以不同的视角制作一些视口 我看到了四个视口 但无法更改视角 假设你有 draw swapbuffers 在 OpenGL3 规范中 创建透视视
  • Python @property.setter

    创建装饰器的基本方法是 def my decorator f def f args kwargs do something using f pass return f my decorator def f 但这样你就不能定义像这样的装饰器
  • 如何将 ThreadPool.QueueUserWorkItem 与非静态方法一起使用?

    当我尝试编译时它给了我 错误 1 非静态字段 方法或属性 ConsoleApplication1 Program print string 需要对象引用 ConsoleApplication1 ConsoleApplication1 Pro
  • Httpclient 4.0.3 随机挂起的多个帖子

    让我解释一下情况 我有一个 servlet 将传出 GET POST 重定向到另一个域 某种代理 上的另一个项目 其工作是处理它并返回一些内容 参数和 gif 我使用 HttpClient 4 0 3 来执行此操作 我的应用程序在启动时发送
  • 仅对非空列进行唯一约束

    在MS SQL Server 2008中 是否可以定义UNIQUE CONSTRAINT在列上 但如果非空值则检查 IF 和 ONLY IF 也就是说 会有很多 NULL 应该被忽略 然而 对于非空值 它应该是唯一的 你需要创建一个过滤索引
  • 将基于 json 的日志转换为列格式,即每列一个文件

    日志文件示例 timestamp 2022 01 14T00 12 21 000 Field1 10 Field Doc f1 0 timestamp 2022 01 18T00 15 51 000 Field Doc f1 0 f2 1
  • Go:一个频道有多个听众

    我对 Go 还很陌生 如果主题错误 我很抱歉 但我希望你能理解我的问题 我想通过通道将事件处理到不同的 go 例程 这是一些示例代码 type Event struct Host string Command string Output s
  • 将一个旋转对象包含在另一个旋转对象中 FabricJS

    我有两个对象 一个父对象 红色 和一个子对象 蓝色 父对象是固定的 不能移动 只有子对象是可移动的 并且子对象总是比父对象大 无论以何种方式移动子对象 它都应该始终包含在子对象内部 这意味着我们永远不应该看到红色矩形 Demo https