ddsort.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. ;(function ($) {
  2. /**
  3. * Author: https://github.com/Barrior
  4. *
  5. * DDSort: drag and drop sorting.
  6. * @param {Object} options
  7. * target[string]: 可选,jQuery事件委托选择器字符串,默认'li'
  8. * cloneStyle[object]: 可选,设置占位符元素的样式
  9. * floatStyle[object]: 可选,设置拖动元素的样式
  10. * down[function]: 可选,鼠标按下时执行的函数
  11. * move[function]: 可选,鼠标移动时执行的函数
  12. * up[function]: 可选,鼠标抬起时执行的函数
  13. * draggableArea[string]:可选,设置可拖拽的区域
  14. */
  15. $.fn.DDSort = function (options) {
  16. var $doc = $(document),
  17. fnEmpty = function () {
  18. },
  19. settings = $.extend(true, {
  20. down: fnEmpty,
  21. move: fnEmpty,
  22. up: fnEmpty,
  23. target: 'li',
  24. cloneStyle: {
  25. 'background-color': '#f7f8fa'
  26. },
  27. floatStyle: {
  28. //用固定定位可以防止定位父级不是Body的情况的兼容处理,表示不兼容IE6,无妨
  29. 'position': 'fixed',
  30. 'box-shadow': '10px 10px 20px 0 #eee',
  31. /*'webkitTransform': 'rotate(4deg)',
  32. 'mozTransform': 'rotate(4deg)',
  33. 'msTransform': 'rotate(4deg)',
  34. 'transform': 'rotate(4deg)'*/
  35. },
  36. draggableArea: ''
  37. }, options);
  38. return this.each(function () {
  39. var that = $(this),
  40. height = 'height',
  41. width = 'width';
  42. if (that.css('box-sizing') == 'border-box') {
  43. height = 'outerHeight';
  44. width = 'outerWidth';
  45. }
  46. let time; // 监听长按时间
  47. let progress = 0; // 记录长按时长
  48. that.on('mousedown.DDSort', settings.target, function (e) {
  49. //只允许鼠标左键拖动
  50. if (e.which != 1) {
  51. return;
  52. }
  53. //防止表单元素失效
  54. var tagName = e.target.tagName.toLowerCase();
  55. if (tagName == 'input' || tagName == 'textarea' || tagName == 'select' || $(e.target).attr('stop-ddsort')) {
  56. return;
  57. }
  58. // 记录长按时长
  59. time = setInterval(() => {
  60. progress++
  61. }, 125);
  62. var THIS = this,
  63. $this = $(THIS),
  64. offset = $this.offset(),
  65. disX = e.pageX - offset.left,
  66. disY = e.pageY - offset.top,
  67. clone = $this.clone()
  68. .css(settings.cloneStyle)
  69. .css('height', $this[height]())
  70. .empty(),
  71. hasClone = 1,
  72. //缓存计算
  73. thisOuterHeight = $this.outerHeight(),
  74. thatOuterHeight = that.outerHeight(),
  75. //滚动速度
  76. upSpeed = thisOuterHeight,
  77. downSpeed = thisOuterHeight,
  78. maxSpeed = thisOuterHeight * 3;
  79. if (settings.draggableArea != "") {
  80. //判断当前点击的DOM是否允许拖拽
  81. var isDraggable = recursiveQuery($(e.target), settings.draggableArea);
  82. // 特殊处理:带有该属性的禁用
  83. if ($(e.target).parent().attr("data-disabled") || $(e.target).attr("data-disabled")) {
  84. return;
  85. }
  86. if (!isDraggable) {
  87. return;
  88. }
  89. }
  90. var downIndex = $(THIS).index();
  91. settings.down.call(THIS, downIndex);
  92. $doc.on('mousemove.DDSort', function (e) {
  93. if (progress === 0) return; // 如果没有长按则不能拖拽
  94. if (hasClone) {
  95. $this.before(clone)
  96. .css('width', $this[width]())
  97. .css(settings.floatStyle)
  98. .appendTo($this.parent());
  99. hasClone = 0;
  100. }
  101. var left = e.pageX - disX,
  102. top = e.pageY - disY,
  103. prev = clone.prev(),
  104. next = clone.next().not($this);
  105. var gap = $(window).scrollTop();
  106. var calculate = top - gap;
  107. //检测是否滚动了
  108. top = ((top - $(window).scrollTop()) != top) ? calculate : top;
  109. $this.css({
  110. left: left,
  111. top: top,
  112. zIndex: 999
  113. });
  114. //向上排序
  115. if (prev.length && top < (prev.offset().top - gap) + prev.outerHeight() / 2) {
  116. clone.after(prev);
  117. //向下排序
  118. } else if (next.length && top + thisOuterHeight > (next.offset().top - gap) + next.outerHeight() / 2) {
  119. clone.before(next);
  120. }
  121. /**
  122. * 处理滚动条
  123. * that是带着滚动条的元素,这里默认以为that元素是这样的元素(正常情况就是这样),如果使用者事件委托的元素不是这样的元素,那么需要提供接口出来
  124. */
  125. var thatScrollTop = that.scrollTop(),
  126. thatOffsetTop = that.offset().top,
  127. scrollVal;
  128. //向上滚动
  129. if (top < thatOffsetTop) {
  130. downSpeed = thisOuterHeight;
  131. upSpeed = ++upSpeed > maxSpeed ? maxSpeed : upSpeed;
  132. scrollVal = thatScrollTop - upSpeed;
  133. //向下滚动
  134. } else if (top + thisOuterHeight - thatOffsetTop > thatOuterHeight) {
  135. upSpeed = thisOuterHeight;
  136. downSpeed = ++downSpeed > maxSpeed ? maxSpeed : downSpeed;
  137. scrollVal = thatScrollTop + downSpeed;
  138. }
  139. that.scrollTop(scrollVal);
  140. var index = recursiveQueryIndex($(THIS));
  141. settings.beforeIndex = index; // 当前拖拽元素
  142. settings.move.call(THIS, index);
  143. })
  144. .on('mouseup.DDSort', function () {
  145. clearInterval(time);
  146. progress = 0;
  147. $doc.off('mousemove.DDSort mouseup.DDSort');
  148. //click的时候也会触发mouseup事件,加上判断阻止这种情况
  149. if (!hasClone) {
  150. clone.before($this.removeAttr('style')).remove();
  151. settings.afterIndex = $this.index();
  152. settings.up.call(THIS, settings.beforeIndex, settings.afterIndex);
  153. }
  154. });
  155. return false;
  156. });
  157. });
  158. };
  159. //当前递归次数
  160. var currentRecursiveCount = 0;
  161. //最大递归次数
  162. var recursiveMaxCount = 20;
  163. /**
  164. * 递归查询当前区域是否允许拖拽
  165. * 创建时间:2018年7月3日18:18:01
  166. */
  167. function recursiveQuery(o, draggableArea) {
  168. if (o.hasClass(draggableArea)) {
  169. //允许拖拽,清空递归次数
  170. currentRecursiveCount = 0;
  171. // console.log($(o));
  172. return true;
  173. } else {
  174. if (currentRecursiveCount <= recursiveMaxCount) {
  175. currentRecursiveCount++;
  176. return recursiveQuery(o.parent(), draggableArea);
  177. } else {
  178. //清空递归次数
  179. // console.log("清空递归次数");
  180. currentRecursiveCount = 0;
  181. return false;
  182. }
  183. }
  184. }
  185. /**
  186. * 递归查询当前拖拽的下标
  187. * 创建时间:2018年7月3日18:18:01
  188. */
  189. function recursiveQueryIndex(o) {
  190. if (o.hasClass("draggable-element")) {
  191. //允许拖拽,清空递归次数
  192. currentRecursiveCount = 0;
  193. return $(o).attr("data-index");
  194. } else {
  195. if (currentRecursiveCount <= recursiveMaxCount) {
  196. currentRecursiveCount++;
  197. return recursiveQueryIndex(o.parent());
  198. } else {
  199. //清空递归次数
  200. // console.log("清空递归次数");
  201. currentRecursiveCount = 0;
  202. return -1;
  203. }
  204. }
  205. }
  206. })(jQuery);