248 lines
6.5 KiB
JavaScript
248 lines
6.5 KiB
JavaScript
;(function ($) {
|
||
/**
|
||
* Author: https://github.com/Barrior
|
||
*
|
||
* DDSort: drag and drop sorting.
|
||
* @param {Object} options
|
||
* target[string]: 可选,jQuery事件委托选择器字符串,默认'li'
|
||
* cloneStyle[object]: 可选,设置占位符元素的样式
|
||
* floatStyle[object]: 可选,设置拖动元素的样式
|
||
* down[function]: 可选,鼠标按下时执行的函数
|
||
* move[function]: 可选,鼠标移动时执行的函数
|
||
* up[function]: 可选,鼠标抬起时执行的函数
|
||
* draggableArea[string]:可选,设置可拖拽的区域
|
||
*/
|
||
$.fn.DDSort = function (options) {
|
||
var $doc = $(document),
|
||
fnEmpty = function () {
|
||
},
|
||
|
||
settings = $.extend(true, {
|
||
|
||
down: fnEmpty,
|
||
move: fnEmpty,
|
||
up: fnEmpty,
|
||
|
||
target: 'li',
|
||
cloneStyle: {
|
||
'background-color': '#f7f8fa'
|
||
},
|
||
floatStyle: {
|
||
//用固定定位可以防止定位父级不是Body的情况的兼容处理,表示不兼容IE6,无妨
|
||
'position': 'fixed',
|
||
'box-shadow': '10px 10px 20px 0 #eee',
|
||
/*'webkitTransform': 'rotate(4deg)',
|
||
'mozTransform': 'rotate(4deg)',
|
||
'msTransform': 'rotate(4deg)',
|
||
'transform': 'rotate(4deg)'*/
|
||
},
|
||
draggableArea: ''
|
||
|
||
}, options);
|
||
|
||
return this.each(function () {
|
||
|
||
var that = $(this),
|
||
height = 'height',
|
||
width = 'width';
|
||
|
||
if (that.css('box-sizing') == 'border-box') {
|
||
height = 'outerHeight';
|
||
width = 'outerWidth';
|
||
}
|
||
|
||
let time; // 监听长按时间
|
||
let progress = 0; // 记录长按时长
|
||
that.on('mousedown.DDSort', settings.target, function (e) {
|
||
//只允许鼠标左键拖动
|
||
if (e.which != 1) {
|
||
return;
|
||
}
|
||
//防止表单元素失效
|
||
var tagName = e.target.tagName.toLowerCase();
|
||
if (tagName == 'input' || tagName == 'textarea' || tagName == 'select' || $(e.target).attr('stop-ddsort')) {
|
||
return;
|
||
}
|
||
|
||
// 记录长按时长
|
||
time = setInterval(() => {
|
||
progress++
|
||
}, 125);
|
||
|
||
var THIS = this,
|
||
$this = $(THIS),
|
||
offset = $this.offset(),
|
||
disX = e.pageX - offset.left,
|
||
disY = e.pageY - offset.top,
|
||
|
||
clone = $this.clone()
|
||
.css(settings.cloneStyle)
|
||
.css('height', $this[height]())
|
||
.empty(),
|
||
|
||
hasClone = 1,
|
||
|
||
//缓存计算
|
||
thisOuterHeight = $this.outerHeight(),
|
||
thatOuterHeight = that.outerHeight(),
|
||
|
||
//滚动速度
|
||
upSpeed = thisOuterHeight,
|
||
downSpeed = thisOuterHeight,
|
||
maxSpeed = thisOuterHeight * 3;
|
||
|
||
if (settings.draggableArea != "") {
|
||
//判断当前点击的DOM是否允许拖拽
|
||
var isDraggable = recursiveQuery($(e.target), settings.draggableArea);
|
||
// 特殊处理:带有该属性的禁用
|
||
if ($(e.target).parent().attr("data-disabled") || $(e.target).attr("data-disabled")) {
|
||
return;
|
||
}
|
||
if (!isDraggable) {
|
||
return;
|
||
}
|
||
}
|
||
|
||
var downIndex = $(THIS).index();
|
||
settings.down.call(THIS, downIndex);
|
||
|
||
$doc.on('mousemove.DDSort', function (e) {
|
||
if (progress === 0) return; // 如果没有长按则不能拖拽
|
||
if (hasClone) {
|
||
$this.before(clone)
|
||
.css('width', $this[width]())
|
||
.css(settings.floatStyle)
|
||
.appendTo($this.parent());
|
||
|
||
hasClone = 0;
|
||
}
|
||
|
||
var left = e.pageX - disX,
|
||
top = e.pageY - disY,
|
||
|
||
prev = clone.prev(),
|
||
next = clone.next().not($this);
|
||
|
||
var gap = $(window).scrollTop();
|
||
var calculate = top - gap;
|
||
|
||
//检测是否滚动了
|
||
top = ((top - $(window).scrollTop()) != top) ? calculate : top;
|
||
|
||
$this.css({
|
||
left: left,
|
||
top: top,
|
||
zIndex: 999
|
||
});
|
||
|
||
//向上排序
|
||
if (prev.length && top < (prev.offset().top - gap) + prev.outerHeight() / 2) {
|
||
|
||
clone.after(prev);
|
||
|
||
//向下排序
|
||
} else if (next.length && top + thisOuterHeight > (next.offset().top - gap) + next.outerHeight() / 2) {
|
||
|
||
clone.before(next);
|
||
|
||
}
|
||
|
||
/**
|
||
* 处理滚动条
|
||
* that是带着滚动条的元素,这里默认以为that元素是这样的元素(正常情况就是这样),如果使用者事件委托的元素不是这样的元素,那么需要提供接口出来
|
||
*/
|
||
var thatScrollTop = that.scrollTop(),
|
||
thatOffsetTop = that.offset().top,
|
||
scrollVal;
|
||
|
||
//向上滚动
|
||
if (top < thatOffsetTop) {
|
||
|
||
downSpeed = thisOuterHeight;
|
||
upSpeed = ++upSpeed > maxSpeed ? maxSpeed : upSpeed;
|
||
scrollVal = thatScrollTop - upSpeed;
|
||
|
||
//向下滚动
|
||
} else if (top + thisOuterHeight - thatOffsetTop > thatOuterHeight) {
|
||
|
||
upSpeed = thisOuterHeight;
|
||
downSpeed = ++downSpeed > maxSpeed ? maxSpeed : downSpeed;
|
||
scrollVal = thatScrollTop + downSpeed;
|
||
}
|
||
|
||
that.scrollTop(scrollVal);
|
||
|
||
var index = recursiveQueryIndex($(THIS));
|
||
settings.currentIndex = index; // 当前拖拽元素
|
||
settings.move.call(THIS, index);
|
||
|
||
})
|
||
.on('mouseup.DDSort', function () {
|
||
clearInterval(time);
|
||
progress = 0;
|
||
$doc.off('mousemove.DDSort mouseup.DDSort');
|
||
|
||
//click的时候也会触发mouseup事件,加上判断阻止这种情况
|
||
if (!hasClone) {
|
||
clone.before($this.removeAttr('style')).remove();
|
||
settings.up.call(THIS, settings.currentIndex);
|
||
}
|
||
});
|
||
|
||
return false;
|
||
});
|
||
});
|
||
};
|
||
|
||
//当前递归次数
|
||
var currentRecursiveCount = 0;
|
||
|
||
//最大递归次数
|
||
var recursiveMaxCount = 20;
|
||
|
||
/**
|
||
* 递归查询当前区域是否允许拖拽
|
||
* 创建时间:2018年7月3日18:18:01
|
||
*/
|
||
function recursiveQuery(o, draggableArea) {
|
||
if (o.hasClass(draggableArea)) {
|
||
//允许拖拽,清空递归次数
|
||
currentRecursiveCount = 0;
|
||
// console.log($(o));
|
||
return true;
|
||
} else {
|
||
if (currentRecursiveCount <= recursiveMaxCount) {
|
||
currentRecursiveCount++;
|
||
return recursiveQuery(o.parent(), draggableArea);
|
||
} else {
|
||
//清空递归次数
|
||
// console.log("清空递归次数");
|
||
currentRecursiveCount = 0;
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 递归查询当前拖拽的下标
|
||
* 创建时间:2018年7月3日18:18:01
|
||
*/
|
||
function recursiveQueryIndex(o) {
|
||
if (o.hasClass("draggable-element")) {
|
||
//允许拖拽,清空递归次数
|
||
currentRecursiveCount = 0;
|
||
return $(o).attr("data-index");
|
||
} else {
|
||
if (currentRecursiveCount <= recursiveMaxCount) {
|
||
currentRecursiveCount++;
|
||
return recursiveQueryIndex(o.parent());
|
||
} else {
|
||
//清空递归次数
|
||
// console.log("清空递归次数");
|
||
currentRecursiveCount = 0;
|
||
return -1;
|
||
}
|
||
}
|
||
}
|
||
|
||
})(jQuery); |