Javascript拖放 – 插画风格’智能指南’

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

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

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

然而,我想要的是某种线(或某种类型的引导线)出现,如果它另一个对象的边缘对齐,那么我可以连续捕捉东西而不将它们直接放在每一行的旁边其他。

有人知道这是否可能,或者我该怎么做呢?

我开始乱搞jsFiddle 。 它并不完美,但它应该让你开始。

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

 function (event, ui) { // You'll want to debounce this function so that it doesn't run every mouse move (eg 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, eg 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('
'); } else { $('#' + id).remove(); } id = 'topOther' + idx; if (closeToTop) { console.debug(idx, 'top'); $('.parent').not(':has(#' + id + ')').append('
'); } else { $('#' + id).remove(); } }); // End of 'other-object' loop }, debounceTime); // End of setTimeout } // End of drag function

如果我以后有一段时间我会回来再给它一点思考,但是你觉得你很欣赏半答案所以现在就开始吧=)

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

 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":ld-4,"display":"block"}); } if(rs) { ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left; $(".objectx").css({"left":rd-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":td-4,"display":"block"}); } if(bs) { ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top; $(".objecty").css({"top":bd-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/

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

 $.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":ld-4,"display":"block"}); } if(rs) { ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left; $(".objectx").css({"left":rd-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":td-4,"display":"block"}); } if(bs) { ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top; $(".objecty").css({"top":bd-4,"display":"block"}); } }; } }); 

并像这样使用它

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

http://jsfiddle.net/Vd5X6/

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

HTML:

 

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 }); 

我为可拖动和可resize的插件添加了智能指南function。

 $('.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

是什么问题我的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":ld-4,"display":"block"}); } if(rs) { ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left; $(".objectx").css({"left":rd-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":td-4,"display":"block"}); } if(bs) { ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top; $(".objecty").css({"top":bd-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 }); 
Interesting Posts