ddsort.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /**
  2. * DDSort: drag and drop sorting.
  3. * Documentation: https://github.com/Barrior/DDSort
  4. */
  5. +function ($) {
  6. var defaultOptions = {
  7. down: $.noop,
  8. move: $.noop,
  9. up: $.noop,
  10. target: 'li',
  11. delay: 100,
  12. cloneStyle: {
  13. 'background-color': '#eee'
  14. },
  15. floatStyle: {
  16. // 用固定定位可以防止定位父级不是Body的情况的兼容处理,表示不兼容IE6,无妨
  17. 'position': 'fixed',
  18. 'box-shadow': '10px 10px 20px 0 #eee',
  19. 'webkitTransform': 'rotate(4deg)',
  20. 'mozTransform': 'rotate(4deg)',
  21. 'msTransform': 'rotate(4deg)',
  22. 'transform': 'rotate(4deg)'
  23. }
  24. };
  25. $.fn.DDSort = function (options) {
  26. var $doc = $(document);
  27. var settings = $.extend(true, {}, defaultOptions, options);
  28. return this.each(function () {
  29. var that = $(this);
  30. var height = 'height';
  31. var width = 'width';
  32. if (that.css('box-sizing') == 'border-box') {
  33. height = 'outerHeight';
  34. width = 'outerWidth';
  35. }
  36. that.on('mousedown.DDSort touchstart.DDSort', settings.target, function (e) {
  37. var startTime = new Date().getTime();
  38. // 桌面端只允许鼠标左键拖动
  39. if (e.type == 'mousedown' && e.which != 1) return;
  40. // 防止表单元素,a 链接,可编辑元素失效
  41. var tagName = e.target.tagName.toLowerCase();
  42. if (tagName == 'input' || tagName == 'textarea' || tagName == 'select' ||
  43. tagName == 'a' || $(e.target).prop('contenteditable') == 'true') {
  44. return;
  45. }
  46. var self = this;
  47. var $this = $(self);
  48. // 鼠标按下时的元素偏移
  49. var offset = $this.offset();
  50. // 鼠标按下时的光标坐标
  51. // 桌面端
  52. var pageX = e.pageX;
  53. var pageY = e.pageY;
  54. // 移动端
  55. var targetTouches = e.originalEvent.targetTouches;
  56. if (e.type == 'touchstart' && targetTouches) {
  57. pageX = targetTouches[0].pageX;
  58. pageY = targetTouches[0].pageY;
  59. }
  60. var clone = $this.clone()
  61. .css(settings.cloneStyle)
  62. .css('height', $this[height]())
  63. .empty();
  64. var hasClone = 1;
  65. // 缓存计算
  66. var thisOuterHeight = $this.outerHeight(),
  67. thisOuterWidth = $this.outerWidth(),
  68. thatOuterHeight = that.outerHeight(),
  69. thatOuterWidth = that.outerWidth();
  70. // 滚动速度
  71. var upSpeed = thisOuterHeight,
  72. downSpeed = thisOuterHeight,
  73. leftSpeed = thisOuterWidth,
  74. rightSpeed = thisOuterWidth,
  75. maxSpeed = thisOuterHeight * 3;
  76. settings.down.call(self);
  77. $doc.on('mousemove.DDSort touchmove.DDSort', function (e) {
  78. // 鼠标移动时的光标坐标
  79. // 桌面端
  80. var _pageX = e.pageX;
  81. var _pageY = e.pageY;
  82. // 移动端
  83. var targetTouches = e.originalEvent.targetTouches;
  84. if (e.type == 'touchmove' && targetTouches) {
  85. _pageX = targetTouches[0].pageX;
  86. _pageY = targetTouches[0].pageY;
  87. }
  88. if (new Date().getTime() - startTime < settings.delay) {
  89. return;
  90. }
  91. if (hasClone) {
  92. $this.before(clone)
  93. .css('width', $this[width]())
  94. .css(settings.floatStyle)
  95. .appendTo($this.parent());
  96. hasClone = 0;
  97. }
  98. var disX = pageX - _pageX;
  99. var disY = pageY - _pageY;
  100. var left = offset.left - disX;
  101. var top = offset.top - disY;
  102. $this.offset({
  103. left: left,
  104. top: top
  105. });
  106. var $left = getLeft(clone),
  107. $right = getRight(clone, $this),
  108. $top = getTop(clone),
  109. $under = getUnder(clone, $this);
  110. if ($top && $top.length && top < $top.offset().top + $top.outerHeight(true) / 2) {
  111. // 向上排序
  112. $top.before(clone);
  113. } else if ($under && $under.length && top + thisOuterHeight > $under.offset().top + $under.outerHeight(true) / 2) {
  114. // 向下排序
  115. $under.after(clone);
  116. } else if($left && $left.length && left < $left.offset().left + $left.outerWidth(true) / 2) {
  117. //向左排序
  118. $left.before(clone);
  119. } else if($right && $right.length && left + thisOuterWidth > $right.offset().left + $right.outerWidth(true) / 2) {
  120. //向右排序
  121. $right.after(clone);
  122. }
  123. // 处理滚动条,that 是带着滚动条的元素,这里默认以为 that 元素是这样的元素(正常情况就是这样),
  124. // 如果使用者事件委托的元素不是这样的元素,那么需要提供接口出来
  125. var thatScrollTop = that.scrollTop();
  126. var thatOffsetTop = that.offset().top;
  127. if (top < thatOffsetTop) {
  128. // 向上滚动
  129. downSpeed = thisOuterHeight;
  130. upSpeed = ++upSpeed > maxSpeed ? maxSpeed : upSpeed;
  131. var scrollVal = thatScrollTop - upSpeed;
  132. that.scrollTop(scrollVal);
  133. } else if (top + thisOuterHeight - thatOffsetTop > thatOuterHeight) {
  134. // 向下滚动
  135. upSpeed = thisOuterHeight;
  136. downSpeed = ++downSpeed > maxSpeed ? maxSpeed : downSpeed;
  137. var scrollVal = thatScrollTop + downSpeed;
  138. that.scrollTop(scrollVal);
  139. }
  140. var thatScrollLeft = that.scrollLeft();
  141. var thatOffsetLeft = that.offset().left;
  142. if (left < that.offset().left) {
  143. // 向左滚动
  144. rightSpeed = thisOuterWidth;
  145. leftSpeed = ++leftSpeed > maxSpeed ? maxSpeed : leftSpeed;
  146. var scrollVal = thatScrollLeft - leftSpeed;
  147. that.scrollLeft(scrollVal);
  148. } else if (left + thisOuterWidth - thatOffsetLeft > thatOuterWidth) {
  149. // 向右滚动
  150. leftSpeed = thisOuterWidth;
  151. rightSpeed = ++rightSpeed > maxSpeed ? maxSpeed : rightSpeed;
  152. var scrollVal = thatScrollLeft + rightSpeed;
  153. that.scrollLeft(scrollVal);
  154. }
  155. settings.move.call(self, left, top);
  156. })
  157. .on('mouseup.DDSort touchend.DDSort', function () {
  158. $doc.off('mousemove.DDSort mouseup.DDSort touchmove.DDSort touchend.DDSort');
  159. // click 的时候也会触发 mouseup 事件,加上判断阻止这种情况
  160. if (!hasClone) {
  161. clone.before($this.removeAttr('style')).remove();
  162. settings.up.call(self);
  163. }
  164. });
  165. return false;
  166. });
  167. });
  168. };
  169. //允许计算误差
  170. var deviation = 5;
  171. var getLeft = function (clone) {
  172. var left = clone.prev();
  173. if(left.length && clone.offset().top==left.offset().top) {
  174. var _dev = Math.abs(clone.offset().left - (left.offset().left + left.outerWidth(true)));
  175. if(_dev <= deviation) {
  176. return left;
  177. }
  178. }
  179. return undefined;
  180. }
  181. var getTop = function (clone, prev) {
  182. if(!prev){
  183. prev = clone.prev();
  184. }
  185. if(!prev.length) {
  186. return undefined;
  187. }
  188. if(clone.offset().left==prev.offset().left) {
  189. var _dev = Math.abs(clone.offset().top - (prev.offset().top+prev.outerHeight(true)));
  190. if(_dev <= deviation) {
  191. return prev;
  192. }
  193. }
  194. return getTop(clone, prev.prev());
  195. }
  196. var getRight = function (clone, $this) {
  197. var rigth = clone.next().not($this);
  198. if(rigth.length && clone.offset().top==rigth.offset().top) {
  199. var _dev = Math.abs(clone.offset().left - (rigth.offset().left-clone.outerWidth(true)));
  200. if(_dev <= deviation) {
  201. return rigth;
  202. }
  203. }
  204. return undefined;
  205. }
  206. var getUnder = function (clone, $this, next) {
  207. if(!next){
  208. next = clone.next().not($this);
  209. }
  210. if(!next.length) {
  211. return undefined;
  212. }
  213. if(clone.offset().left==next.offset().left) {
  214. var _dev = Math.abs(clone.offset().top - (next.offset().top-clone.outerHeight(true)));
  215. if(_dev <= deviation) {
  216. return next;
  217. }
  218. }
  219. return getUnder(clone, $this, next.next().not($this));
  220. }
  221. }(jQuery);