本来,Sortablejs + css 模仿安卓桌面 是用来制作浏览器主页导航的,但是本人发现不大适合,有性能和操作性的原因,以及难以支持多级目录。反而原生的recyclerview列表控件更适合此任务,所以旁落,没有用上。
而这次打算制作一个新的浏览器扩展,正好再次捡起,用于显示标签页网格、网址收藏网格等,不过发现可以再优化一二。
sortable.js一共一百多kb。它使用插件式设计,内置插件有:
revertOnSpill
则撤销更改(对多选不完全有效),若开启removeOnSpill
则删除元素。默认的MultiDrag在点击空白区域时会清空选择。最新版本有人提出加入avoidImplicitDeselect
选项,阻止隐式清除选择。
我没有找到它的demo,但本人魔改的版本中有一个bug,那就是多选,然后微微拖拽一下选中内容(但是不触发onMove事件),再然后拖动未选中元素,就会导致选中元素消失。
多选假移紊乱:
fix:需要修改MultiDrag插件的dragStarted事件处理,当拖动的是未选中内容时,需适时重置一些变量,而不是直接返回 ——
if(!isMultiDrag)
folding = initialFolding = 0;
spill本意溢出; 泼出
。sortablejs对此的判定很奇怪:
hideGhostForTarget();
var touch = originalEvent.changedTouches && originalEvent.changedTouches.length ? originalEvent.changedTouches[0] : originalEvent;
var target = document.elementFromPoint(touch.clientX, touch.clientY);
unhideGhostForTarget();
if (toSortable && !toSortable.el.contains(target)) {
dispatchSortableEvent('spill');
this.onSpill({
dragEl: dragEl,
putSortable: putSortable
});
}
这是用el.contains
判断的。先获取鼠标位置,然后用document.elementFromPoint
获取鼠标指针下的元素,最后判断元素是否在网格容器中。
更好的做法是判断鼠标是否在包围盒内,以及窗口是否有焦点(document.hasFocus()
)。不过包围盒只有el.getBoundingClientRect()
,如果容器因为滚动等原因被遮挡,那判断的也不是很准确(见so讨论)。
如果是文件夹则更难处理,因为那是两个可拖拽网格上下层叠,onspill判定更为复杂。本人索性在元素拖拽出文件夹时,动态关闭revertOnSpill
。
拖拽到外部只要用于打开新的标签页。sortablejs可设置setData回调,为dataTransfer设置内容:
, multiDrag: true
, setData : function (dataTransfer, dragEl) {
dataTransfer.setData('text', dragEl.data.url);
}
……
当元素总数很多时,动画性能较低。
见 Very slow and laggy in google chrome with many items、 Slow in Chrome when many elements in the page
可以删除position:relative
样式,但无法完全解决问题。可以动态关闭动画,为此本人引入两个变量multiAnimaTheta=300
、window.disani=false
:
修改 MutiDrag::dragStarted
if (rootEl.childElementCount<multiAnimaTheta
&& _this2.options.animation) {
…… css(multiDragElement, 'position', 'absolute');
folding = 1; // 只在元素总数较少时开启神奇的动画聚拢效果
initialFolding = 1;
}
修改_onMove
:
if(!w.disani && fromEl!=toEl) { // 跨列表移动元素,同样要适时关闭动画
w.disani = fromEl.childElementCount>multiAnimaTheta||toEl.childElementCount>multiAnimaTheta;
}
……
(!w.disani && options.animation) 代替 options.animation
学名叫做"Squircle",我用的词典未收录。一开始记得这词类似于square,久之忘词了,wantwords也搜不出,最后还是谷歌查一下图片就出来了……
A squircle is a shape intermediate between a square and a circle
原来是方/圆的结合体。
用svg实现。
外网这种设计网站很多,春秋时代啊:Squircley | SVG Squircle Maker
蚊子再小也是肉,反过来说也是vice versa。虽然sortablejs体积很小,但能省点流量就省点流量,虽也不缺,全当是为绿色环保做贡献吧。
sortablejs可以实现懒加载:默认不加载,而是自己写el.draggble=true,处理ondragstart事件。
在ondragstart中加载并初始化Sortable,然后手动调用Sortable对象的_onTapStart
与_onDragStart
,成功交接拖拽逻辑:
var tabS;
function lazyDrag(e) {
debug('ondragstart', e);
bg.loadSortable(function() { // 加载 sortable.js
debug('loadSortable', w.Sortable); // 确认加载成功
tabS=new Sortable(tabH, { // 初始化 Sortable 对象
multiDrag: true,
swapThreshold: 0.34,
revertOnSpill:true,
invertSwap: true,
selectedClass: 'selected',
animation: 300,
ghostClass: 'blue-background-class',
group:"tabH",
root:true,
forceFallback: false,
sort:true,
……
});
// 交接拖拽逻辑!
tabS._onTapStart(e);
tabS._onDragStart(e);
});
}
function initTabs() {
for(var i=0;i<tabs.length;i++) {
var t=createTab(i);
t.draggable = true; // 模拟拖拽
t.ondragstart = lazyDrag;
}
}
此法简直浑然天成、天衣无缝。此处胜景,仿佛天生就在等待我的光临。
Sortablejs 本身支持固定/不能拖拽排序的项目,在options.onMove中返回false
即可,建议用 el.fixed
判断元素是否固定:
multiDrag: true,
onMove(evt) {
// 需要判断被拖拽元素与拖拽目标元素两个方面
if(evt.dragged.fixed||evt.related.fixed)
return false;
}
拖拽时无法滚动列表。
由于系统特性,标准拖拽的过程中无按键、鼠标事件的传递。
Drag + wheel scroll feature · Issue #935 · SortableJS/Sortable
可以开启forceFallback模式,缺点是非标准拖拽,内容无法拖拽到其他窗口。
不支持多级目录