【问题标题】:Javascript drag/drop - Illustrator style 'smart guides'Javascript 拖放 - Illustrator 风格的“智能指南”
【发布时间】:2013-12-03 05:02:34
【问题描述】:

我正在寻找一种在 Javascript 中拖放时实现 Adob​​e Illustrator 风格的“智能指南”的方法。我目前正在使用 jQuery UI 的 draggable:

$('.object').draggable({
    containment: 'parent',
    snap: '.other-objects',
    snapTolerance: 5
})

这完成了我想要的 90% - 我可以在它的父级中拖动 .object,当它足够接近时,它会将它的边缘捕捉到 .other-objects

然而,我想要的是出现某种线条(或某种指南),如果它另一个对象的边缘一致,那么我可以捕捉东西在没有直接相邻的情况下连续排列。

有谁知道这是否可能,或者我将如何去做?

【问题讨论】:

  • 这是一个很好的问题,但你的最后一行让你很难说出一个可接受的答案是什么样的。也许建议一个潜在的解决方案,例如“如果我创建一个在一个维度上为 1px 而在另一个维度上为 100% 的 div 会怎样,这可以作为指导吗?” (更好的是,尝试一下,然后我们可以建议改进它的方法,使其发挥作用。)

标签: javascript jquery jquery-ui draggable adobe-illustrator


【解决方案1】:

我为可拖动和可调整大小的插件添加了智能指南功能。

$('.drag')
    .draggable({
        smartGuides: '.drag:not(".selected")'
    })
    .resizable({
        handles: 'all',
        smartGuides: '.drag:not(".selected")'
    }

你可以看看它是如何工作的:

jsFiddle:https://jsfiddle.net/chukanov/bypr7Lt3/2/

github:https://github.com/aichukanov/smartguides

演示站点:https://ready2.net/smartguides.shtml

【讨论】:

    【解决方案2】:

    我从上面对 JSFiddle 进行了分叉,并通过支持 shape 的同一侧来改进它。这是 JsFiddle:http://jsfiddle.net/yusrilmaulidanraji/A6CpP/120/

    HTML:

    <div id="parent">
        <div class="object1 dropped" style="left:0px;top:300px;background:#a00;"></div>
        <div class="object2 dropped"></div>
        <div class="object3 dropped" style="left:400px;top:20px;"></div>
        <div class="objectx"></div>
        <div class="objecty"></div>
    </div>
    

    CSS:

    #parent{
        width:600px;
        height:500px;
        border:1px solid #000;
        position:relative;
    }
    .object1{
        background:#aaa;
        width:100px;
        height:100px;
        display:block;
        position:absolute;
        left:140px;
        top:50px;
    }
    .object2{
        background:#aaa;
        width:100px;
        height:150px;
        display:block;
        position:absolute;
        left:140px;
        top:50px;
    }
    .object3{
        background:#aaa;
        width:150px;
        height:100px;
        display:block;
        position:absolute;
        left:140px;
        top:50px;
    }
    .objectx{
        display:none;
        //background:#fff;
        width:0px;
        height:100%;
        position:absolute;
        top:0px;
        left:10px;
        border-left: 1px solid yellow;
    }
    .objecty{
        display:none;
        //background:#fff;
        width:100%;
        height:0px;
        position:absolute;
        top:10px;
        left:0px;
        border-bottom: 1px solid yellow;
    }
    

    JS:

    $.ui.plugin.add("draggable", "smartguides", {
        start: function(event, ui) {
            var i = $(this).data("draggable"), o = i.options;
            i.elements = [];
            $(o.smartguides.constructor != String ? ( o.smartguides.items || ':data(draggable)' ) : o.smartguides).each(function() {
                var $t = $(this); var $o = $t.offset();
                if(this != i.element[0]) i.elements.push({
                    item: this,
                    width: $t.outerWidth(), height: $t.outerHeight(),
                    top: $o.top, left: $o.left
                });
            });
        },
        stop: function(event, ui) {
            $(".objectx").css({"display":"none"});
            $(".objecty").css({"display":"none"});
        },
        drag: function(event, ui) {
            var inst = $(this).data("draggable"), o = inst.options;
            var d = o.tolerance;
            $(".objectx").css({"display":"none"});
            $(".objecty").css({"display":"none"});
                var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
                    y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height,
                    xc = (x1 + x2) / 2, yc = (y1 + y2) / 2;
                for (var i = inst.elements.length - 1; i >= 0; i--){
                    var l = inst.elements[i].left, r = l + inst.elements[i].width,
                        t = inst.elements[i].top, b = t + inst.elements[i].height,
                        hc = (l + r) / 2, vc = (t + b) / 2;
                         var lss = Math.abs(l - x1) <= d;
                                var ls = Math.abs(l - x2) <= d;
                                var rss = Math.abs(r - x2) <= d;
                                var rs = Math.abs(r - x1) <= d;
                                var tss = Math.abs(t - y1) <= d;
                                var ts = Math.abs(t - y2) <= d;
                                var bss = Math.abs(b - y2) <= d;
                                var bs = Math.abs(b - y1) <= d;
                                var hs = Math.abs(hc - xc) <= d;
                                var vs = Math.abs(vc - yc) <= d; 
                            if(lss) {
                                ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
                                $(".objectx").css({"left":ui.position.left,"display":"block"});
                            }
                            if(rss) {
                                ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
                                $(".objectx").css({"left":ui.position.left + ui.helper.width(),"display":"block"});
                            }
                            if(ls) {
                                ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
                                $(".objectx").css({"left":ui.position.left + ui.helper.width(),"display":"block"});
                            }
                            if(rs) {
                                ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
                                $(".objectx").css({"left":ui.position.left,"display":"block"});
                            }
                            if(tss) {
                                ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
                                $(".objecty").css({"top":ui.position.top,"display":"block"});
                            }
                            if(ts) {
                                ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
                                $(".objecty").css({"top":ui.position.top + ui.helper.height(),"display":"block"});
                            }
                            if(bss) {
                                ui.position.top = inst._convertPositionTo("relative", { top: b-inst.helperProportions.height, left: 0 }).top - inst.margins.top;
                                $(".objecty").css({"top":ui.position.top + ui.helper.height(),"display":"block"});
                            }
                            if(bs) {
                                ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
                                $(".objecty").css({"top":ui.position.top,"display":"block"});
                            }
                            if(hs) {
                                ui.position.left = inst._convertPositionTo("relative", { top: 0, left: hc - inst.helperProportions.width/2 }).left - inst.margins.left;
                                $(".objectx").css({"left":ui.position.left + (ui.helper.width()/2),"display":"block"});
                            }
                            if(vs) {
                                ui.position.top = inst._convertPositionTo("relative", { top: vc - inst.helperProportions.height/2, left: 0 }).top - inst.margins.top;
                                $(".objecty").css({"top":ui.position.top + (ui.helper.height()/2),"display":"block"});
                            }
    
    
                };
            }
    });
    $('.dropped').draggable({
        containment: 'parent',
        smartguides:".dropped",
        tolerance:5
    });
    

    【讨论】:

      【解决方案3】:

      我的 jsfiddle 有什么问题

      访问jsfiddle.net/datakolay/7xzTn/

      $.ui.plugin.add("draggable", "smartguides", {
      
      
      start: function(event, ui) {
          var i = $(this).data("draggable"), o = i.options;
          i.elements = [];
          $(o.smartguides.constructor != String ? ( o.smartguides.items || ':data(draggable)' ) : o.smartguides).each(function() {
              var $t = $(this); var $o = $t.offset();
              if(this != i.element[0]) i.elements.push({
                  item: this,
                  width: $t.outerWidth(), height: $t.outerHeight(),
                  top: $o.top, left: $o.left
              });
          });
      },
      stop: function(event, ui) {
          $(".objectx").css({"display":"none"});
          $(".objecty").css({"display":"none"});
      },
      drag: function(event, ui) {
          var inst = $(this).data("draggable"), o = inst.options;
          var d = o.tolerance;
          $(".objectx").css({"display":"none"});
          $(".objecty").css({"display":"none"});
              var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
                  y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height,
                  xc = (x1 + x2) / 2, yc = (y1 + y2) / 2;
              for (var i = inst.elements.length - 1; i >= 0; i--){
                  var l = inst.elements[i].left, r = l + inst.elements[i].width,
                      t = inst.elements[i].top, b = t + inst.elements[i].height,
                      hc = (l + r) / 2, vc = (t + b) / 2;
                      var ls = Math.abs(l - x2) <= d;
                      var rs = Math.abs(r - x1) <= d;
                      var ts = Math.abs(t - y2) <= d;
                      var bs = Math.abs(b - y1) <= d;
                      var hs = Math.abs(hc - xc) <= d;
                      var vs = Math.abs(vc - yc) <= d; 
                  if(ls) {
                      ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
                      $(".objectx").css({"left":l-d-4,"display":"block"});
                  }
                  if(rs) {
                      ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
                       $(".objectx").css({"left":r-d-4,"display":"block"});
                  }
      
                  if(ts) {
                      ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
                      $(".objecty").css({"top":t-d-4,"display":"block"});
                  }
                  if(bs) {
                      ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
                      $(".objecty").css({"top":b-d-4,"display":"block"});
                  }
                  if(hs) {
                      ui.position.left = inst._convertPositionTo("relative", { top: 0, left: hc - inst.helperProportions.width/2 }).left - inst.margins.left;
                       $(".objectx").css({"left":hc-d-4,"display":"block"});
                  }
                  if(vs) {
                      ui.position.top = inst._convertPositionTo("relative", { top: vc - inst.helperProportions.height/2, left: 0 }).top - inst.margins.top;
                      $(".objecty").css({"top":vc-d-4,"display":"block"});
                  }
      
      
              };
          }
      });
      $('.other-objects').draggable({
        containment: 'parent',
        smartguides:".other-objects",
        tolerance:5
      });
      

      【讨论】:

      • 如果您有新问题,请点击 按钮提出问题。如果有助于提供上下文,请包含指向此问题的链接。
      • 抱歉,我们不再接受来自此帐户的问题。请参阅帮助中心了解更多信息。有什么问题?
      【解决方案4】:

      我把上面的小提琴分叉了, 添加了对中线的支持。

      drag: function(event, ui) {
          var inst = $(this).data("draggable"), o = inst.options;
          var d = o.tolerance;
          $(".objectx").css({"display":"none"});
          $(".objecty").css({"display":"none"});
              var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
                  y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height,
                  xc = (x1 + x2) / 2, yc = (y1 + y2) / 2;
              for (var i = inst.elements.length - 1; i >= 0; i--){
                  var l = inst.elements[i].left, r = l + inst.elements[i].width,
                      t = inst.elements[i].top, b = t + inst.elements[i].height,
                      hc = (l + r) / 2, vc = (t + b) / 2;
                      var ls = Math.abs(l - x2) <= d;
                      var rs = Math.abs(r - x1) <= d;
                      var ts = Math.abs(t - y2) <= d;
                      var bs = Math.abs(b - y1) <= d;
                      var hs = Math.abs(hc - xc) <= d;
                      var vs = Math.abs(vc - yc) <= d; 
                  if(ls) {
                      ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
                      $(".objectx").css({"left":l-d-4,"display":"block"});
                  }
                  if(rs) {
                      ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
                       $(".objectx").css({"left":r-d-4,"display":"block"});
                  }
      
                  if(ts) {
                      ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
                      $(".objecty").css({"top":t-d-4,"display":"block"});
                  }
                  if(bs) {
                      ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
                      $(".objecty").css({"top":b-d-4,"display":"block"});
                  }
                  if(hs) {
                      ui.position.left = inst._convertPositionTo("relative", { top: 0, left: hc - inst.helperProportions.width/2 }).left - inst.margins.left;
                       $(".objectx").css({"left":hc-d-4,"display":"block"});
                  }
                  if(vs) {
                      ui.position.top = inst._convertPositionTo("relative", { top: vc - inst.helperProportions.height/2, left: 0 }).top - inst.margins.top;
                      $(".objecty").css({"top":vc-d-4,"display":"block"});
                  }
      
      
              };
          }
      

      这样会更容易对齐行/列中的元素。

      检查下面的小提琴:
      http://jsfiddle.net/elin/A6CpP/

      【讨论】:

      • 你为什么这样做 - 每次 4 个。例如:$(".objectx").css({"left":l-d-4,"display":"block"});
      • @Eddie 感谢您的回答,好吧,我的结构在智能指南中造成了一个小问题。也许你可以帮助我 - jsfiddle.net/A6CpP/109
      【解决方案5】:

      你可以尝试创建一个这样的插件

      $.ui.plugin.add("draggable", "smartguides", {
      start: function(event, ui) {
          var i = $(this).data("draggable"), o = i.options;
          i.elements = [];
          $(o.smartguides.constructor != String ? ( o.smartguides.items || ':data(draggable)' ) : o.smartguides).each(function() {
              var $t = $(this); var $o = $t.offset();
              if(this != i.element[0]) i.elements.push({
                  item: this,
                  width: $t.outerWidth(), height: $t.outerHeight(),
                  top: $o.top, left: $o.left
              });
          });
      },
      drag: function(event, ui) {
          var inst = $(this).data("draggable"), o = inst.options;
          var d = o.tolerance;
          $(".objectx").css({"display":"none"});
          $(".objecty").css({"display":"none"});
              var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
                  y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
              for (var i = inst.elements.length - 1; i >= 0; i--){
                  var l = inst.elements[i].left, r = l + inst.elements[i].width,
                      t = inst.elements[i].top, b = t + inst.elements[i].height;
                      var ls = Math.abs(l - x2) <= d;
                      var rs = Math.abs(r - x1) <= d;
                      var ts = Math.abs(t - y2) <= d;
                      var bs = Math.abs(b - y1) <= d;
                  if(ls) {
                      ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
                      $(".objectx").css({"left":l-d-4,"display":"block"});
                  }
                  if(rs) {
                      ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
                       $(".objectx").css({"left":r-d-4,"display":"block"});
                  }
                  if(ts) {
                      ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
                      $(".objecty").css({"top":t-d-4,"display":"block"});
                  }
                  if(bs) {
                      ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
                      $(".objecty").css({"top":b-d-4,"display":"block"});
                  }
              };
          }
      });    
      

      并像这样使用它

      $('.other-objects').draggable({
          containment: 'parent',
          smartguides:".other-objects",
          tolerance:5
      });    
      

      http://jsfiddle.net/Vd5X6/

      【讨论】:

        【解决方案6】:

        我开始搞乱jsFiddle。它并不完美,但应该可以帮助您入门。

        大部分逻辑在 jQuery UI 的 drag 事件处理程序中:

        function (event, ui) {
        
                // You'll want to debounce this function so that it doesn't run every mouse move (e.g. see Ben Alman's site @ http://tinyurl.com/37dyjug)
                var debounceTime = 200; // milliseconds
                setTimeout(function () {
        
                    // Loop through all 'other-object's and see if we're lined up
                    $(".other-object").each(function (idx, other) {
                        var $other = $(other);
        
                        // Determine whether we're "close enough" to display the line
                        var padding = 1;
                        var closeToLeft = Math.abs($other.offset().left - ui.offset.left) < padding;
                        var closeToTop = Math.abs($other.offset().top - ui.offset.top) < padding;
                        // You can add closeToRight/closeToBottom, but you may need to do some calculation, e.g. right = left + width
        
                        // If we're close, display a line, otherwise remove that same line
                        // TODO: Find a better way of tagging which 'other-object' this line belongs to, using IDs or something more stable than the index of the jQuery each() function!
                        var id = 'leftOther' + idx;
                        if (closeToLeft) {
                            console.debug(idx, 'left');
                            $('.parent').not(':has(#' + id + ')').append('<div id="' + id + '" class="line vertical" style="left: ' + $other.offset().left + 'px;"/>');
                        } else {
                            $('#' + id).remove();
                        }
        
                        id = 'topOther' + idx;
                        if (closeToTop) {
                            console.debug(idx, 'top');
                            $('.parent').not(':has(#' + id + ')').append('<div id="topOther' + idx + '" class="line horizontal" style="top: ' + $other.offset().top + 'px;"/>');
                        } else {
                            $('#' + id).remove();
                        }
                    }); // End of 'other-object' loop
        
                }, debounceTime); // End of setTimeout
            } // End of drag function
        

        如果我以后有时间我会回来再考虑一下,但我想你会喜欢一个半答案,所以现在就开始吧 =)

        【讨论】:

        • 顺便说一句,您可以在此处查看其他示例:stackoverflow.com/questions/10026234/…
        • 这很棒,给了我一个好的开始 - 谢谢!其他例子也很好。我会看看我能做什么。谢谢:)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-11-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-26
        • 2011-01-13
        • 2011-07-22
        相关资源
        最近更新 更多