/** * DDSort: drag and drop sorting. * Documentation: https://github.com/Barrior/DDSort */ +function ($) { var defaultOptions = { down: $.noop, move: $.noop, up: $.noop, target: 'li', delay: 100, cloneStyle: { 'background-color': '#eee' }, floatStyle: { // 用固定定位可以防止定位父级不是Body的情况的兼容处理,表示不兼容IE6,无妨 'position': 'fixed', 'box-shadow': '10px 10px 20px 0 #eee', 'webkitTransform': 'rotate(4deg)', 'mozTransform': 'rotate(4deg)', 'msTransform': 'rotate(4deg)', 'transform': 'rotate(4deg)' } }; $.fn.DDSort = function (options) { var $doc = $(document); var settings = $.extend(true, {}, defaultOptions, options); return this.each(function () { var that = $(this); var height = 'height'; var width = 'width'; if (that.css('box-sizing') == 'border-box') { height = 'outerHeight'; width = 'outerWidth'; } that.on('mousedown.DDSort touchstart.DDSort', settings.target, function (e) { var startTime = new Date().getTime(); // 桌面端只允许鼠标左键拖动 if (e.type == 'mousedown' && e.which != 1) return; // 防止表单元素,a 链接,可编辑元素失效 var tagName = e.target.tagName.toLowerCase(); if (tagName == 'input' || tagName == 'textarea' || tagName == 'select' || tagName == 'a' || $(e.target).prop('contenteditable') == 'true') { return; } var self = this; var $this = $(self); // 鼠标按下时的元素偏移 var offset = $this.offset(); // 鼠标按下时的光标坐标 // 桌面端 var pageX = e.pageX; var pageY = e.pageY; // 移动端 var targetTouches = e.originalEvent.targetTouches; if (e.type == 'touchstart' && targetTouches) { pageX = targetTouches[0].pageX; pageY = targetTouches[0].pageY; } var clone = $this.clone() .css(settings.cloneStyle) .css('height', $this[height]()) .empty(); var hasClone = 1; // 缓存计算 var thisOuterHeight = $this.outerHeight(), thisOuterWidth = $this.outerWidth(), thatOuterHeight = that.outerHeight(), thatOuterWidth = that.outerWidth(); // 滚动速度 var upSpeed = thisOuterHeight, downSpeed = thisOuterHeight, leftSpeed = thisOuterWidth, rightSpeed = thisOuterWidth, maxSpeed = thisOuterHeight * 3; settings.down.call(self); $doc.on('mousemove.DDSort touchmove.DDSort', function (e) { // 鼠标移动时的光标坐标 // 桌面端 var _pageX = e.pageX; var _pageY = e.pageY; // 移动端 var targetTouches = e.originalEvent.targetTouches; if (e.type == 'touchmove' && targetTouches) { _pageX = targetTouches[0].pageX; _pageY = targetTouches[0].pageY; } if (new Date().getTime() - startTime < settings.delay) { return; } if (hasClone) { $this.before(clone) .css('width', $this[width]()) .css(settings.floatStyle) .appendTo($this.parent()); hasClone = 0; } var disX = pageX - _pageX; var disY = pageY - _pageY; var left = offset.left - disX; var top = offset.top - disY; $this.offset({ left: left, top: top }); var $left = getLeft(clone), $right = getRight(clone, $this), $top = getTop(clone), $under = getUnder(clone, $this); if ($top && $top.length && top < $top.offset().top + $top.outerHeight(true) / 2) { // 向上排序 $top.before(clone); } else if ($under && $under.length && top + thisOuterHeight > $under.offset().top + $under.outerHeight(true) / 2) { // 向下排序 $under.after(clone); } else if($left && $left.length && left < $left.offset().left + $left.outerWidth(true) / 2) { //向左排序 $left.before(clone); } else if($right && $right.length && left + thisOuterWidth > $right.offset().left + $right.outerWidth(true) / 2) { //向右排序 $right.after(clone); } // 处理滚动条,that 是带着滚动条的元素,这里默认以为 that 元素是这样的元素(正常情况就是这样), // 如果使用者事件委托的元素不是这样的元素,那么需要提供接口出来 var thatScrollTop = that.scrollTop(); var thatOffsetTop = that.offset().top; if (top < thatOffsetTop) { // 向上滚动 downSpeed = thisOuterHeight; upSpeed = ++upSpeed > maxSpeed ? maxSpeed : upSpeed; var scrollVal = thatScrollTop - upSpeed; that.scrollTop(scrollVal); } else if (top + thisOuterHeight - thatOffsetTop > thatOuterHeight) { // 向下滚动 upSpeed = thisOuterHeight; downSpeed = ++downSpeed > maxSpeed ? maxSpeed : downSpeed; var scrollVal = thatScrollTop + downSpeed; that.scrollTop(scrollVal); } var thatScrollLeft = that.scrollLeft(); var thatOffsetLeft = that.offset().left; if (left < that.offset().left) { // 向左滚动 rightSpeed = thisOuterWidth; leftSpeed = ++leftSpeed > maxSpeed ? maxSpeed : leftSpeed; var scrollVal = thatScrollLeft - leftSpeed; that.scrollLeft(scrollVal); } else if (left + thisOuterWidth - thatOffsetLeft > thatOuterWidth) { // 向右滚动 leftSpeed = thisOuterWidth; rightSpeed = ++rightSpeed > maxSpeed ? maxSpeed : rightSpeed; var scrollVal = thatScrollLeft + rightSpeed; that.scrollLeft(scrollVal); } settings.move.call(self, left, top); }) .on('mouseup.DDSort touchend.DDSort', function () { $doc.off('mousemove.DDSort mouseup.DDSort touchmove.DDSort touchend.DDSort'); // click 的时候也会触发 mouseup 事件,加上判断阻止这种情况 if (!hasClone) { clone.before($this.removeAttr('style')).remove(); settings.up.call(self); } }); return false; }); }); }; //允许计算误差 var deviation = 5; var getLeft = function (clone) { var left = clone.prev(); if(left.length && clone.offset().top==left.offset().top) { var _dev = Math.abs(clone.offset().left - (left.offset().left + left.outerWidth(true))); if(_dev <= deviation) { return left; } } return undefined; } var getTop = function (clone, prev) { if(!prev){ prev = clone.prev(); } if(!prev.length) { return undefined; } if(clone.offset().left==prev.offset().left) { var _dev = Math.abs(clone.offset().top - (prev.offset().top+prev.outerHeight(true))); if(_dev <= deviation) { return prev; } } return getTop(clone, prev.prev()); } var getRight = function (clone, $this) { var rigth = clone.next().not($this); if(rigth.length && clone.offset().top==rigth.offset().top) { var _dev = Math.abs(clone.offset().left - (rigth.offset().left-clone.outerWidth(true))); if(_dev <= deviation) { return rigth; } } return undefined; } var getUnder = function (clone, $this, next) { if(!next){ next = clone.next().not($this); } if(!next.length) { return undefined; } if(clone.offset().left==next.offset().left) { var _dev = Math.abs(clone.offset().top - (next.offset().top-clone.outerHeight(true))); if(_dev <= deviation) { return next; } } return getUnder(clone, $this, next.next().not($this)); } }(jQuery);