除了涉及文本区域调整大小句柄等一些深奥技巧之外,可拖动元素是网络上 JavaScript 的领域。例如,单击元素,按住鼠标按钮,拖动鼠标光标,元素随鼠标拖动,释放鼠标按钮以释放元素。或者触摸等效项。幸运的是,这是一个经过充分探索的领域。经过时间考验的工具(如 jQuery UI)提供了 Draggable(以及其他类似方法)来简化此操作。
但最近在尝试实现某种效果(请参阅本文标题)时,我无法让 jQuery UI 完全按照我想要的方式进行操作。但我完成了,以下是如何实现的。
我试图在 Keynote 的侧边栏中重新创建效果,您可以在其中拖动以重新排列幻灯片。以下是完成的效果

使用一些非常基本的 jQuery UI 配置,您可以非常接近。我们在这里使用 Sortable 方法。这专门用于对列表进行排序,这就是我们正在做的事情,有点像同时使用 Draggable 和 Droppable。
$(".all-slides").sortable({
axis: "y",
revert: true,
scroll: false,
placeholder: "sortable-placeholder",
cursor: "move"
});
在类似这样的基本 HTML 上
<div class='all-slides'>
<div class='slide'>Slide</div>
<div class='slide'>Slide</div>
<div class='slide'>Slide</div>
<!-- etc -->
配置中的“占位符”是一个元素(具有提供的类名),它会插入幻灯片之间,如果您现在释放鼠标按钮,该幻灯片将在该位置放置。您可以使用 CSS 以任何方式对其进行样式设置,因此我使其看起来像 Keynote 中的左侧蓝线。

这里的问题是我们没有获得我们想要的“推开”效果。占位符只是立即显示。最初,我尝试通过使用 @keyframes 动画将占位符的高度从 0 扩展到幻灯片高度,然后使用填充模式保持该高度来解决此问题。这适用于占位符的外观,但 jQuery UI 在占位符消失时会将其从 DOM 中移除,这使得无法实现优雅的退出。
因此,需要一些更深层的技巧。幸运的是,在 Twitter 上抱怨困难之后,AJ Kandy 找到一个方便的示例,正好满足我的需求。
请耐心等待,这有点复杂
- 循环遍历所有幻灯片
- 创建每个幻灯片的副本
- 将幻灯片直接放置在原件的顶部
- 隐藏它
- 保存对其复制来源幻灯片的引用
然后您仅对原始文件启动 Sortable 方法。然后,当您开始拖动幻灯片时
- 隐藏所有原始幻灯片
- 显示克隆
- 在您四处拖动时,原始文件将以不可见的方式重新排列
- 在执行此操作后,将克隆动画设置为这些新位置
拖动停止时
- 确保所有克隆都位于正确的最终位置
- 再次交换可见性,显示原始文件
花了不少时间来理解并进行调整才能使其完美。在我的示例中,我使用伪元素对幻灯片进行编号,因此还有一些代码用于此。以下是所有内容。
$(".slide").each(function(i) {
var item = $(this);
var item_clone = item.clone();
item.data("clone", item_clone);
var position = item.position();
item_clone
.css({
left: position.left,
top: position.top,
visibility: "hidden"
})
.attr("data-pos", i+1);
$("#cloned-slides").append(item_clone);
});
$(".all-slides").sortable({
axis: "y",
revert: true,
scroll: false,
placeholder: "sortable-placeholder",
cursor: "move",
start: function(e, ui) {
ui.helper.addClass("exclude-me");
$(".all-slides .slide:not(.exclude-me)")
.css("visibility", "hidden");
ui.helper.data("clone").hide();
$(".cloned-slides .slide").css("visibility", "visible");
},
stop: function(e, ui) {
$(".all-slides .slide.exclude-me").each(function() {
var item = $(this);
var clone = item.data("clone");
var position = item.position();
clone.css("left", position.left);
clone.css("top", position.top);
clone.show();
item.removeClass("exclude-me");
});
$(".all-slides .slide").each(function() {
var item = $(this);
var clone = item.data("clone");
clone.attr("data-pos", item.index());
});
$(".all-slides .slide").css("visibility", "visible");
$(".cloned-slides .slide").css("visibility", "hidden");
},
change: function(e, ui) {
$(".all-slides .slide:not(.exclude-me)").each(function() {
var item = $(this);
var clone = item.data("clone");
clone.stop(true, false);
var position = item.position();
clone.animate({
left: position.left,
top: position.top
}, 200);
});
}
});
以及演示
查看 Chris Coyier 的 CodePen 上的 类似 Keynote 的幻灯片重新排列器 (@chriscoyier)。
额外功能:幻灯片在拖动时会获得一个类,该类会使其大小脉动,就像在 Keynote 中一样。
其他选项
David DeSandro 有一些您可能听说过的项目,这些项目都与在网络上重新排列框有关,例如 Masonry 和 Isotope。他还有一个名为 Packery 的项目,它使用算法将框打包到空间中。连同他的另一个项目 Dragabilly 一起,您可以创建 相同的效果,其中拖动元素会将其他元素移开。我们演示的单轴性质对它来说很容易。
David 复制了我的演示并使其与这些工具一起工作,代码量少了很多
查看 Chris Coyier 的 CodePen 上的 类似 Keynote 的幻灯片重新排列器 - 使用 Packery 和 Draggabilly (@chriscoyier)。
Luke Underwood 也尝试了一下,支持不同大小的块
查看 Luke Underwood 的 CodePen 上的 带过渡和可变高度支持的 jQuery 可排序 (@Snarkie3)。
哦,我也构建了这个 :) — Slip.JS(无 jQuery)
需要实现的烦人的事情是滚动和触摸事件的交互——如果您希望视图可滚动,则不能阻止触摸时的滚动,您必须等待一段时间来测量手指的移动。不幸的是,浏览器只允许第一个 touchmove 事件阻止滚动,因此当您知道您不想滚动时,为时已晚 :(
它看起来非常漂亮和精致,但在 FF 27 中,我在第一个示例中拖动和滚动时遇到了这种奇怪的行为。列表将保持固定,并且正在拖动的元素将滚动。不确定这是否是预期的行为,甚至如何修复它,我只是想提一下。
我在 Chrome 32 和 Firefox 23 中都尝试过,在我拖动选定的幻灯片时,所有其他幻灯片都消失了。在
sortable
哈希的start
参数中,我将第一个 css 调用更改为.css("visibility", "visible")
,现在它可以工作了(尽管没有平滑的推开动画)。将第二个 css 从“可见”更改为“隐藏”不会导致任何其他更改。这两个问题(拖动时隐藏和没有“推动”动画)是否都是由于没有使用上面显示的 CSS 造成的?我只是添加了一些快速样式来模仿您的样式。
有趣的方法
就我个人而言,我一直使用 Gridster 来做非常相似的事情,我发现他们的方法非常优雅,您应该看看(如果您想做一些它不打算做的事情,它有点有错误,但它可以工作)
简而言之,它们计算所有元素的绝对位置并在内存中创建一个“伪网格”,并生成一个包含每行/列位置的样式表。这些样式是应用于元素的。
有了它,很容易使用 CSS 动画来获得您想要的推动效果
最后一个(复制的)版本应该做什么?在 Firefox (27) 和 Chrome (32) 以及 IE11 中,它似乎都将“拖动”的项目保留在其原始位置。
这是另一个更简单的实现
演示: http://jsfiddle.net/tovic/85EpZ/
您也可以通过对“顶部”属性使用 CSS3 过渡来实现相同的效果:http://codepen.io/nuo/pen/CxLek
干得好! :)
前一段时间,我感到沮丧的是,这些平滑的可排序的东西很少。我找到的为数不多的几个都是基于 jquery 可排序的,而且它在跟踪鼠标光标以及何时决定重新排列项目方面感觉很糟糕。我真的很想获得与在 iOS 上重新排序内容时获得的坚实感觉。所以这是我的尝试:http://pumpula.net/p/smooth-sortable/
在现实世界的使用中,这与大多数其他实现之间可能没有太大区别,但我对获得恰到好处的感受很痴迷。
我不是经验丰富的编码员,所以代码有点像意大利面条,但我认为我已经很好地解决了它的“物理学”。诀窍是跟踪实际的对象边界而不是鼠标光标。(我认为 Packery 示例也是这样做的)Packery 示例在重新排列项目之前存在一些延迟。可能是为了性能?我认为在拖动开始时将列表几何测量到 js 对象,然后使用它来跟踪所有内容并将新的位置样式应用于实际的 DOM 元素,并让 css 过渡执行动画,它似乎工作得非常好。所以有一个虚拟列表在执行所有工作,还有一个根据虚拟列表进行动画处理的 DOM 列表。
@Ville Vanninen:哇,我对你的工作印象深刻。这也很流畅、平滑且响应迅速。干得好。已收藏。
使用第二个示例和手机时,您可以将幻灯片拖动到另一个幻灯片的顶部。
编辑:显然不再是了……今天早上它不起作用,现在可以了!
将此与动画内容一起使用来组装播放列表会很有趣。
我也想向Greensock Draggable致敬,它与其他工具非常相似:触摸支持等,但移动非常流畅,高级版本还提供了抛掷物理效果。
它本身不带排序逻辑,但你可以通过回调系统很容易地实现。
第二个演示在我的手机上运行了:D
但是,当向上拖动项目到其位置时,它的行为很奇怪。此外,您正在拖动的滑动动画第一次有效。但随后它就不起作用了。
这里使用 iPhone 5 测试 Chrome
第二个演示(David 的)在我的 iPhone 上运行。
最后一个示例在 Chrome 中失败。
将项目移动到最后一个位置时,它不会重新编号元素。
非常酷。我实际上刚刚构建了一个非常类似的东西,只是使用 Isotope 和 jQuery UI 而不是。基本思想是您使用可排序的
start
事件在移动拖动项目时将其从 Isotope 中移除,然后在stop
时将其添加回 Isotope 并重新加载项目(这会将它们动画到新顺序)。无需复制项目,您还可以利用 Isotope 的其他内置功能(如排序和过滤,Packery 不具备);-)
HAML、SCSS 和 jQuery JS。我忍不住大笑……我们不再编写指令,而是描述我们想象的内容。
我曾使用过 Alfresco,它使用 YUI 东西来实现这种行为。我不确定它是如何实现的,但我知道它具有拖放和这种漂亮的动画效果。它是 YUI 2.6 或类似版本,您是否曾经使用过任何这些东西?
我设法几乎完全通过 css 过渡(并且仍然使用 jQuery 可排序)来实现它。在排序期间,基本的 html 结构如下所示
因此,应该对
sortable-placeholder
之后的所有幻灯片进行过渡,但不包括ui-sortable-helper
幻灯片。CSS 如下所示这是基本的过渡设置。但是我们需要满足排序开始/结束没有过渡的要求。因此,我添加了一些小的 js 代码来切换类。这部分有点棘手。结果并不完美。
这是演示笔。
您是否曾经有过这种感觉,即所有简单的事情现在都变得困难,所有困难的事情现在都变得不可能?当我看到这些东西时,我就会在胃里产生这种下沉的感觉。或者当我过去使用它们时。
帖子和评论中人们让一个损坏的系统跳舞,简直是天才,绝对的天才。毫无疑问。
但人生不是太短了吗,以至于要如此努力地完成如此简单的动画任务?
我现在将回到我的编译器和桌面软件,为你们的灵魂祈祷。
(开玩笑)