jQuery UI:拖动多个对象

jQuery UI: drag multiple objects

我的场景:带有一些对象 (div) 的容器 (div)。对象可以在容器内移动(选项 containment 设置为 parent)。

现在我需要一次移动多个对象。为此,我找到了 this useful plugin. Unfortunately this plugin does not handle the property containment, as reported here.

My test on JSFiddle , 关闭此功能

$(".obj").on("drag", function(ev, ui)

要激活多重拖动,请单击对象。我能够阻止拖动事件。

我的测试问题:

那时我不知道如何重新激活拖动。

备注

我应该知道拖动的方向(带有开始 - 停止事件)。但是此时我已经无法停止拖拽了。

我的解决方案

而且 K Scandrett 解决方案也非常好。在我的特殊情况下应用起来非常困难,在示例中已经简化了。

始终使用 this 插件来启用多重拖动。每次我 select 多个对象并拖动它们时,在 dragstart 事件中我这样做(根据 selected 对象的位置更改对象的 属性 包含):

//1024 * 648 is the width of the container
$(obj).unbind("dragstart").bind("dragstart" , function(ev, ui){

    var dimObjFirst = {
        x : parseInt($(this).css("left")),
        y : parseInt($(this).css("top"))
    };
    if($("blablabla > div.ui-selected").length > 1){

        var minLeft = 1024,maxRight = 0,minTop = 648,maxDown = 0;

        $("blablabla > div.ui-selected").each(function(){
            var elem = $(this);
            var dim = {
                w : parseInt(elem.css("width")),
                h : parseInt(elem.css("height")),
                l : parseInt(elem.css("left")),
                t : parseInt(elem.css("top")),
            };
            if(dim.l < minLeft) minLeft = dim.l;
            if(dim.l + dim.w > maxRight) maxRight = dim.l + dim.w;
            if(dim.t < minTop) minTop = dim.t;
            if(dim.t + dim.h > maxDown) maxDown = dim.t + dim.h;
        });

        var offsetContainer = $(".container").offset();
        $(this).draggable( "option" , "containment" , [
          (dimObjFirst.x - minLeft) + parseInt(offsetContainer.left),
          (dimObjFirst.y - minTop) + parseInt(offsetContainer.top),
          (dimObjFirst.x + (1024 - maxRight)) + parseInt(offsetContainer.left),
          (dimObjFirst.y) + (648 - maxDown) + parseInt(offsetContainer.top)
        ]);
    }           
});

$(obj).unbind("dragstop").on("dragstop", function(ev, ui) {

    if($("blablabla > div.ui-selected").length > 1) {
        $("blablabla > div.ui-selected").each(function(){
            $(this).draggable( "option" , "containment" , "parent" );
        });
    }
});

并在 jQuery UI 插件的函数 _mouseDrag 的开头添加这行代码 this._setContainment();

看起来是个有趣的项目所以....

我用边界框实现了它(类似于 Twisty 的评论)。

我认为这样做的好处是它会将所有多个选定对象限制在容器的边界内。

我为边界框涂上了颜色,这样您就可以直观地了解它是如何工作的。当然,您可能会在实践中保持透明。

代码注释是内联的,但很乐意回答有关代码的任何问题(如果有的话)。

没有使用插件(只有 jQuery 和 jQueryUI)。

var disableclick = false;

var boundingBoxTop, boundingBoxBottom, boundingBoxLeft, boundingBoxRight;
var $container = $("#container");
var containerHeight = $container.height();
var containerWidth = $container.width();
var containerTop = $container.offset().top;
var containerLeft = $container.offset().left;

// add the bounding box to the container and make it draggable
var $boundingBox = $("<div id='boundingBox' style='position:absolute;background-color:#fcf5d4'>").prependTo($container);

$boundingBox.draggable({
  grid: [10, 10],
  containment: "parent",
  stop: function( event, ui ) {
    disableclick = true; // don't want to toggle selection when dragging
    setTimeout(function(e){
     disableclick = false;
    },200);
  },
});

$(".obj").click(function(e) {

  if (!disableclick) {

    var $objClicked = $(this);
    $objClicked.toggleClass("ui-selected");

    var $selectedItems = $("#container .ui-selected");

    // move any items in bounding box back into container before we re-process them
    $boundingBox.find('*').each(function() {
      var $this = $(this);

      if ($this.parent().is($boundingBox)) {
        // adjust its positioning to be relative to the container
        $this.css("top", ($this.offset().top - containerTop) + "px");
        $this.css("left", ($this.offset().left - containerLeft) + "px");
        $container.append($this); // return it to the container
      }
    });

    // reversing co-ords to what might be expected here so that we can scale them back to what they need to be for a bounding box
    boundingBoxTop = containerHeight;
    boundingBoxBottom = 0;
    boundingBoxLeft = containerWidth;
    boundingBoxRight = 0;

    // find the bounds of the smallest rectangle that will cover all the currently selected objects
    $selectedItems.each(function() {
      var $this = $(this);

      var top = $this.offset().top - containerTop;
      var bottom = $this.offset().top - containerTop + $this.height();
      var left = $this.offset().left - containerLeft;
      var right = $this.offset().left - containerLeft + $this.width();

      boundingBoxTop = (top < boundingBoxTop) ? top : boundingBoxTop;
      boundingBoxBottom = (bottom > boundingBoxBottom) ? bottom : boundingBoxBottom;
      boundingBoxLeft = (left < boundingBoxLeft) ? left : boundingBoxLeft;
      boundingBoxRight = (right > boundingBoxRight) ? right : boundingBoxRight;
    });

    // get the height and width of bounding box
    var boundingBoxHeight = boundingBoxBottom -= boundingBoxTop;
    var boundingBoxWidth = boundingBoxRight -= boundingBoxLeft;

    if (boundingBoxBottom > 0) // will be negative when nothing is selected
    {
      // update the bounding box with its new position and size
      $boundingBox.css("top", boundingBoxTop + "px");
      $boundingBox.css("left", boundingBoxLeft + "px");
      $boundingBox.css("width", boundingBoxWidth + "px");
      $boundingBox.css("height", boundingBoxHeight + "px");

      // add each selected item to the bounding box so we can drag the box with them in it
      $selectedItems.each(function() {
        var $this = $(this);

        // correct the item's position to be relative to the bounding box
        $this.css("top", ($this.offset().top - containerTop - boundingBoxTop) + "px");
        $this.css("left", ($this.offset().left - containerLeft - boundingBoxLeft) + "px");

        $boundingBox.append($this); // add item to bounding box
      });
    }
  }
});
#container {
  position: absolute;
  width: 400px;
  height: 150px;
  background: #eee;
}

.obj {
  position: absolute;
  background: #ccc;
}

.ui-selected {
  background: #1C90F3;
}

#obj1 {
  width: 20px;
  height: 20px;
  left: 20px;
  top: 20px;
}

#obj2 {
  width: 20px;
  height: 20px;
  left: 100px;
  top: 20px;
}

#obj3 {
  width: 20px;
  height: 20px;
  left: 20px;
  top: 100px;
}

#obj4 {
  width: 20px;
  height: 20px;
  left: 100px;
  top: 100px;
}
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://code.jquery.com/jquery-3.2.1.min.js" />

<div style="margin-bottom:10px">
  Click boxes to select/deselect multiple items.<br/>Drag to move selection.
</div>

<div id="container">
  <div class="obj" id="obj1"></div>
  <div class="obj" id="obj2"></div>
  <div class="obj" id="obj3"></div>
  <div class="obj" id="obj4"></div>
</div>

我使用 jquery ui 可选择的插件

来增强答案

<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>jQuery UI Draggable - Default functionality</title>

    <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">

    <style>
        #container {
            position: absolute;
            width: 400px;
            height: 150px;
            background: #eee;
        }

        .obj {
            position: absolute;
            background: #ccc;
        }

        .ui-selected,
        .ui-selecting {
            background: #1C90F3;
        }

        #obj1 {
            width: 20px;
            height: 20px;
            left: 20px;
            top: 20px;
        }

        #obj2 {
            width: 20px;
            height: 20px;
            left: 100px;
            top: 20px;
        }

        #obj3 {
            width: 20px;
            height: 20px;
            left: 20px;
            top: 100px;
        }

        #obj4 {
            width: 20px;
            height: 20px;
            left: 100px;
            top: 100px;
        }
    </style>

    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"></script>

</head>

<body>

    <div id="container">
        <div class="obj" id="obj1"></div>
        <div class="obj" id="obj2"></div>
        <div class="obj" id="obj3"></div>
        <div class="obj" id="obj4"></div>
    </div>



    <script type="text/javascript">
        var disableclick = false;

        var boundingBoxTop, boundingBoxBottom, boundingBoxLeft, boundingBoxRight;
        var $container = $("#container");
        var containerHeight = $container.height();
        var containerWidth = $container.width();
        var containerTop = $container.offset().top;
        var containerLeft = $container.offset().left;

        // add the bounding box to the container and make it draggable
        var $boundingBox = $("<div id='boundingBox' style='position:absolute;background-color:#fcf5d4'>").prependTo($container);

        $boundingBox.draggable({
            grid: [10, 10],
            containment: "parent",
            stop: function (event, ui) {
                disableclick = true; // don't want to toggle selection when dragging
                setTimeout(function (e) {
                    disableclick = false;
                }, 200);
            },
        });


        $('.obj').draggable({
            grid: [10, 10],
            containment: "parent",
            stop: function (event, ui) {
                disableclick = true; // don't want to toggle selection when dragging
                setTimeout(function (e) {
                    disableclick = false;
                }, 200);
            },
        });

        function selectionStarted() {
            $boundingBox.find('*').each(function () {
                var $this = $(this);

                if ($this.parent().is($boundingBox)) {
                    // adjust its positioning to be relative to the container
                    $this.css("top", ($this.offset().top - containerTop) + "px");
                    $this.css("left", ($this.offset().left - containerLeft) + "px");
                    $this.draggable("enable");
                    $container.append($this); // return it to the container

                }

            });

            $boundingBox.css("top", "0px");
            $boundingBox.css("left", "0px");
            $boundingBox.css("width", "0px");
            $boundingBox.css("height", "0px");
        }

        function selectedEnded() {
            var $selectedItems = $("#container .ui-selected");

            // reversing co-ords to what might be expected here so that we can scale them back to what they need to be for a bounding box
            boundingBoxTop = containerHeight;
            boundingBoxBottom = 0;
            boundingBoxLeft = containerWidth;
            boundingBoxRight = 0;

            // find the bounds of the smallest rectangle that will cover all the currently selected objects
            $selectedItems.each(function () {
                var $this = $(this);

                var top = $this.offset().top - containerTop;
                var bottom = $this.offset().top - containerTop + $this.height();
                var left = $this.offset().left - containerLeft;
                var right = $this.offset().left - containerLeft + $this.width();

                boundingBoxTop = (top < boundingBoxTop) ? top : boundingBoxTop;
                boundingBoxBottom = (bottom > boundingBoxBottom) ? bottom : boundingBoxBottom;
                boundingBoxLeft = (left < boundingBoxLeft) ? left : boundingBoxLeft;
                boundingBoxRight = (right > boundingBoxRight) ? right : boundingBoxRight;
            });

            // get the height and width of bounding box
            var boundingBoxHeight = boundingBoxBottom -= boundingBoxTop;
            var boundingBoxWidth = boundingBoxRight -= boundingBoxLeft;

            if (boundingBoxBottom > 0) // will be negative when nothing is selected
            {
                // update the bounding box with its new position and size
                $boundingBox.css("top", boundingBoxTop + "px");
                $boundingBox.css("left", boundingBoxLeft + "px");
                $boundingBox.css("width", boundingBoxWidth + "px");
                $boundingBox.css("height", boundingBoxHeight + "px");

                // add each selected item to the bounding box so we can drag the box with them in it
                $selectedItems.each(function () {
                    var $this = $(this);

                    // correct the item's position to be relative to the bounding box
                    $this.css("top", ($this.offset().top - containerTop - boundingBoxTop) + "px");
                    $this.css("left", ($this.offset().left - containerLeft - boundingBoxLeft) + "px");

                    $this.draggable("disable");
                    $boundingBox.append($this); // add item to bounding box

                });
            }
        }

    </script>

    <script type="text/javascript">
        $("#container").selectable({
            start: selectionStarted,
            stop: selectedEnded
        });
    </script>

</body>

</html>