将旋转后的对象包含在另一个旋转后的对象FabricJS中 [英] Contain a rotated object inside another rotated object FabricJS

查看:47
本文介绍了将旋转后的对象包含在另一个旋转后的对象FabricJS中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个对象,父母(红色)和孩子(蓝色).父对象是固定的,不能移动,只有子对象是可移动的,子对象始终大于父对象.无论以何种方式移动子对象,它都应始终包含在子对象中,这意味着我们永远都不会看到红色矩形.

I have two objects a parent (red) and a child (blue). The parent object is fixed and can't be moved, only the child object is movable and the child is always bigger than the parent. In whatever way the child object is moved it should always be contained inside the child, which means we should never see the red rectangle.

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

我知道有一些解决方案可以将对象包含在画布或其他对象边界内(例如,

I know there are solutions to contain an object within the canvas or other object boundaries (ex. Move object within canvas boundary limit) which mainly force the top/right/bottom/left values to not exceed the parent values, but here we have the case of two rotated objects by the same degree.

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

I have a real-life scenario when a user uploads a photo to a frame container. The photo is normally always bigger than the frame container. The user can move the photo inside the frame, but he should not be allowed to create any empty spaces, the photo should always be contained inside the photo frame.

推荐答案

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

I would go with a pure canvas (no fabricjs), do it from scratch that way you understand well the problem you are facing, then if you need it that same logic should be easily portable to any library.

您有一些规则:

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

所以我的想法是得到所有四个角,这样一来,我们就可以使用这些坐标来确定是否可以将其移动到新位置,代码应该易于理解,但是请问是否有任何问题.

So my idea is to get all four corners, that way on the move we can use those coordinates to determine if it can be moved to the new location or not, the code should be easy to follow, but ask if you have any concerns.

我正在使用射线投射算法:
https://github.com/substack/point-in-多边形/blob/master/index.js
这样,我们要做的就是检查孩子的四角不在父母内部,而父母在孩子内部,仅此而已.

I'm using the ray-casting algorithm:
https://github.com/substack/point-in-polygon/blob/master/index.js
With that, all we need to do is check that the corners of the child are not inside the parent and that the parent is inside the child, that is all.

我不是FabricJS专家,所以我最好的表现可能不是很多...
但是下面是我尝试使您的代码运行的尝试.

I'm no expert with FabricJS so my best might not be much...
but below is my attempt to get your code going.

<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

That code is on sandbox as well:
https://codesandbox.io/s/force-contain-of-object-inside-another-object-fabric-js-dnvb5

不用担心对所有单击/按住/拖动结构进行编码就可以了,这真是太容易了...

It certainly is nice not to worry about coding all the click/hold/drag fabric makes that real easy...

我正在尝试使用FabricJS,并且画布具有不错的属性
( canvas.stateful = true; )
这样我们就可以跟踪自己去过的地方,如果超出范围,我们可以还原该运动,还可以通过动画播放来向用户提供不允许运动的视觉反馈.

I was experimenting with FabricJS and there a nice property of the canvas
(canvas.stateful = true;)
that allows us to keep track of where we've been, and if we go out of bounds we can revert that movement, also playing with animate that gives the user visual feedback that the movement is not allowed.

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

Here is another version without animation:

<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-脚本/master/HTML/canvas_contained2.html

This algorithm also opens the door for other shapes as well, here is a hexagon version:
https://raw.githack.com/heldersepu/hs-scripts/master/HTML/canvas_contained2.html

这篇关于将旋转后的对象包含在另一个旋转后的对象FabricJS中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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