vue-dragging.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. class DragData {
  2. constructor () {
  3. this.data = {}
  4. }
  5. new (key) {
  6. if (!this.data[key]) {
  7. this.data[key] = {
  8. className: '',
  9. List: [],
  10. KEY_MAP: {}
  11. }
  12. }
  13. return this.data[key]
  14. }
  15. get (key) {
  16. return this.data[key]
  17. }
  18. }
  19. const $dragging = {
  20. listeners: {
  21. },
  22. $on (event, func) {
  23. const events = this.listeners[event]
  24. if (!events) {
  25. this.listeners[event] = []
  26. }
  27. this.listeners[event].push(func)
  28. },
  29. $once (event, func) {
  30. const vm = this
  31. function on (...args) {
  32. vm.$off(event, on)
  33. func.apply(vm, args)
  34. }
  35. this.$on(event, on)
  36. },
  37. $off (event, func) {
  38. const events = this.listeners[event]
  39. if (!func || !events) {
  40. this.listeners[event] = []
  41. return
  42. }
  43. this.listeners[event] = this.listeners[event].filter(i => i !== func)
  44. },
  45. $emit (event, context) {
  46. const events = this.listeners[event]
  47. if (events && events.length > 0) {
  48. events.forEach(func => {
  49. func(context)
  50. })
  51. }
  52. }
  53. }
  54. const _ = {
  55. on (el, type, fn) {
  56. el.addEventListener(type, fn)
  57. },
  58. off (el, type, fn) {
  59. el.removeEventListener(type, fn)
  60. },
  61. addClass (el, cls) {
  62. if(arguments.length < 2) {
  63. el.classList.add(cls)
  64. } else {
  65. for (let i = 1, len = arguments.length; i < len; i++) {
  66. el.classList.add(arguments[i])
  67. }
  68. }
  69. },
  70. removeClass (el, cls) {
  71. if(arguments.length < 2) {
  72. el.classList.remove(cls)
  73. } else {
  74. for (let i = 1, len = arguments.length; i < len; i++) {
  75. el.classList.remove(arguments[i])
  76. }
  77. }
  78. }
  79. }
  80. export default function (Vue, options) {
  81. const isPreVue = Vue.version.split('.')[0] === '1'
  82. const dragData = new DragData()
  83. let isSwap = false
  84. let Current = null
  85. function handleDragStart(e) {
  86. const el = getBlockEl(e.target)
  87. const key = el.getAttribute('drag_group')
  88. const drag_key = el.getAttribute('drag_key')
  89. const comb = el.getAttribute('comb')
  90. const DDD = dragData.new(key)
  91. const item = DDD.KEY_MAP[drag_key]
  92. const index = DDD.List.indexOf(item)
  93. const groupArr = DDD.List.filter(item => item[comb])
  94. _.addClass(el, 'dragging')
  95. if (e.dataTransfer) {
  96. e.dataTransfer.effectAllowed = 'move'
  97. e.dataTransfer.setData('text', JSON.stringify(item))
  98. }
  99. Current = {
  100. index,
  101. item,
  102. el,
  103. group: key,
  104. groupArr
  105. }
  106. }
  107. function handleDragOver(e) {
  108. if (e.preventDefault) {
  109. e.preventDefault()
  110. }
  111. return false
  112. }
  113. function handleDragEnter(e) {
  114. let el
  115. if (e.type === 'touchmove') {
  116. e.stopPropagation()
  117. e.preventDefault()
  118. el = getOverElementFromTouch(e)
  119. el = getBlockEl(el)
  120. } else {
  121. el = getBlockEl(e.target)
  122. }
  123. if (!el || !Current) return
  124. const key = el.getAttribute('drag_group')
  125. if (key !== Current.group || !Current.el || !Current.item || el === Current.el) return
  126. const drag_key = el.getAttribute('drag_key')
  127. const DDD = dragData.new(key)
  128. const item = DDD.KEY_MAP[drag_key]
  129. if (item === Current.item) return
  130. const indexTo = DDD.List.indexOf(item)
  131. const indexFrom = DDD.List.indexOf(Current.item)
  132. swapArrayElements(DDD.List, indexFrom, indexTo)
  133. Current.groupArr.forEach(item => {
  134. if (item != Current.item) {
  135. DDD.List.splice(DDD.List.indexOf(item), 1)
  136. }
  137. })
  138. let targetIndex = DDD.List.indexOf(Current.item)
  139. if (Current.groupArr.length) {
  140. DDD.List.splice(targetIndex, 1, ...Current.groupArr)
  141. }
  142. Current.index = indexTo
  143. isSwap = true
  144. $dragging.$emit('dragged', {
  145. draged: Current.item,
  146. to: item,
  147. value: DDD.value,
  148. group: key
  149. })
  150. }
  151. function handleDragLeave(e) {
  152. _.removeClass(getBlockEl(e.target), 'drag-over', 'drag-enter')
  153. }
  154. function handleDrag (e) {
  155. }
  156. function handleDragEnd (e) {
  157. const el = getBlockEl(e.target)
  158. _.removeClass(el, 'dragging', 'drag-over', 'drag-enter')
  159. Current = null
  160. // if (isSwap) {
  161. isSwap = false
  162. const group = el.getAttribute('drag_group')
  163. $dragging.$emit('dragend', { group })
  164. // }
  165. }
  166. function handleDrop(e) {
  167. e.preventDefault()
  168. if (e.stopPropagation) {
  169. e.stopPropagation()
  170. }
  171. return false
  172. }
  173. function getBlockEl (el) {
  174. if (!el) return
  175. while (el.parentNode) {
  176. if (el.getAttribute && el.getAttribute('drag_block')) {
  177. return el
  178. break
  179. } else {
  180. el = el.parentNode
  181. }
  182. }
  183. }
  184. function swapArrayElements (items, indexFrom, indexTo) {
  185. let item = items[indexTo]
  186. if (isPreVue) {
  187. items.$set(indexTo, items[indexFrom])
  188. items.$set(indexFrom, item)
  189. } else {
  190. Vue.set(items, indexTo, items[indexFrom])
  191. Vue.set(items, indexFrom, item)
  192. }
  193. return items
  194. }
  195. function getOverElementFromTouch (e) {
  196. const touch = e.touches[0]
  197. const el = document.elementFromPoint(touch.clientX, touch.clientY)
  198. return el
  199. }
  200. function addDragItem (el, binding, vnode) {
  201. const item = binding.value.item
  202. const list = binding.value.list
  203. const DDD = dragData.new(binding.value.group)
  204. const drag_key = isPreVue? binding.value.key : vnode.key
  205. DDD.value = binding.value
  206. DDD.className = binding.value.className
  207. DDD.KEY_MAP[drag_key] = item
  208. if (list && DDD.List !== list) {
  209. DDD.List = list
  210. }
  211. el.setAttribute('draggable', 'true')
  212. el.setAttribute('drag_group', binding.value.group)
  213. el.setAttribute('drag_block', binding.value.group)
  214. el.setAttribute('drag_key', drag_key)
  215. el.setAttribute('comb', binding.value.comb)
  216. _.on(el, 'dragstart', handleDragStart)
  217. _.on(el, 'dragenter', handleDragEnter)
  218. _.on(el, 'dragover', handleDragOver)
  219. _.on(el, 'drag', handleDrag)
  220. _.on(el, 'dragleave', handleDragLeave)
  221. _.on(el, 'dragend', handleDragEnd)
  222. _.on(el, 'drop', handleDrop)
  223. _.on(el, 'touchstart', handleDragStart)
  224. _.on(el, 'touchmove', handleDragEnter)
  225. _.on(el, 'touchend', handleDragEnd)
  226. }
  227. function removeDragItem (el, binding, vnode) {
  228. const DDD = dragData.new(binding.value.group)
  229. const drag_key = isPreVue? binding.value.key : vnode.key
  230. DDD.KEY_MAP[drag_key] = undefined
  231. _.off(el, 'dragstart', handleDragStart)
  232. _.off(el, 'dragenter', handleDragEnter)
  233. _.off(el, 'dragover', handleDragOver)
  234. _.off(el, 'drag', handleDrag)
  235. _.off(el, 'dragleave', handleDragLeave)
  236. _.off(el, 'dragend', handleDragEnd)
  237. _.off(el, 'drop', handleDrop)
  238. _.off(el, 'touchstart', handleDragStart)
  239. _.off(el, 'touchmove', handleDragEnter)
  240. _.off(el, 'touchend', handleDragEnd)
  241. }
  242. Vue.prototype.$dragging = $dragging
  243. if (!isPreVue) {
  244. Vue.directive('dragging', {
  245. bind: addDragItem,
  246. update(el, binding, vnode) {
  247. const DDD = dragData.new(binding.value.group)
  248. const item = binding.value.item
  249. const list = binding.value.list
  250. const drag_key = vnode.key
  251. const old_item = DDD.KEY_MAP[drag_key]
  252. if (item && old_item !== item) {
  253. DDD.KEY_MAP[drag_key] = item
  254. }
  255. if (list && DDD.List !== list) {
  256. DDD.List = list
  257. }
  258. },
  259. unbind : removeDragItem
  260. })
  261. } else {
  262. Vue.directive('dragging', {
  263. update (newValue, oldValue) {
  264. addDragItem(this.el, {
  265. modifiers: this.modifiers,
  266. arg: this.arg,
  267. value: newValue,
  268. oldValue: oldValue
  269. })
  270. },
  271. unbind (newValue, oldValue) {
  272. removeDragItem(this.el, {
  273. modifiers: this.modifiers,
  274. arg: this.arg,
  275. value: newValue?newValue:{group: this.el.getAttribute('drag_group')},
  276. oldValue: oldValue
  277. })
  278. }
  279. })
  280. }
  281. }