再战sortablejs

本来,Sortablejs + css 模仿安卓桌面 是用来制作浏览器主页导航的,但是本人发现不大适合,有性能和操作性的原因,以及难以支持多级目录。反而原生的recyclerview列表控件更适合此任务,所以旁落,没有用上。

而这次打算制作一个新的浏览器扩展,正好再次捡起,用于显示标签页网格、网址收藏网格等,不过发现可以再优化一二。

sortable.js一共一百多kb。它使用插件式设计,内置插件有:

  • AutoScroll - 拖动时自动滚动
  • MultiDrag 多选拖拽
  • OnSpill 拖拽到网格外部时,若开启revertOnSpill则撤销更改(对多选不完全有效),若开启removeOnSpill则删除元素。
  • Swap 修改拖拽排序为拖拽交换。

优化MultiDrag

默认的MultiDrag在点击空白区域时会清空选择。最新版本有人提出加入avoidImplicitDeselect选项,阻止隐式清除选择。

我没有找到它的demo,但本人魔改的版本中有一个bug,那就是多选,然后微微拖拽一下选中内容(但是不触发onMove事件),再然后拖动未选中元素,就会导致选中元素消失。

多选假移紊乱:
再战sortablejs_第1张图片
fix:需要修改MultiDrag插件的dragStarted事件处理,当拖动的是未选中内容时,需适时重置一些变量,而不是直接返回 ——

if(!isMultiDrag)
    folding = initialFolding = 0;

优化 OnSpill

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=300window.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也搜不出,最后还是谷歌查一下图片就出来了……
再战sortablejs_第2张图片

A squircle is a shape intermediate between a square and a circle

原来是方/圆的结合体。

用svg实现。

外网这种设计网站很多,春秋时代啊:Squircley | SVG Squircle Maker
再战sortablejs_第3张图片

懒加载

蚊子再小也是肉,反过来说也是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;
	}

无法解决的问题

  1. 拖拽时无法滚动列表。
    由于系统特性,标准拖拽的过程中无按键、鼠标事件的传递。
    Drag + wheel scroll feature · Issue #935 · SortableJS/Sortable
    可以开启forceFallback模式,缺点是非标准拖拽,内容无法拖拽到其他窗口。

  2. 不支持多级目录

你可能感兴趣的:(桌面GUI新技术,浏览器界面,前端开发,前端,javascript)