index.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // 弹窗拖动
  2. import { ref, Ref, watch, nextTick, computed } from 'vue';
  3. type posType = {
  4. top: number;
  5. left: number;
  6. };
  7. /**
  8. * @params classList 可拖动地方的class值,也为唯一值
  9. * @params boxClass 容器class值必须为唯一值,这个class和useid拼接 作为缓存主键
  10. * @params dragShow 弹窗是否显示
  11. * @params userId 当前用户id
  12. */
  13. export default function useDrag(
  14. classList: string[],
  15. boxClass: string,
  16. dragShow: Ref<boolean>,
  17. userId: string
  18. ) {
  19. const pos = ref<posType>({
  20. top: -1, // -1 为初始值 代表没有缓存 默认居中
  21. left: -1
  22. });
  23. const useIdDargClass = userId + boxClass;
  24. watch(dragShow, () => {
  25. if (dragShow.value) {
  26. // 初始化pos值
  27. initPos();
  28. window.addEventListener('resize', refreshPos);
  29. nextTick(() => {
  30. const boxClassDom = document.querySelector(
  31. `.${boxClass}`
  32. ) as HTMLElement;
  33. if (!boxClassDom) {
  34. return;
  35. }
  36. classList.map((className: string) => {
  37. const classDom = document.querySelector(
  38. `.${className}`
  39. ) as HTMLElement;
  40. if (classDom) {
  41. classDom.style.cursor = 'move';
  42. drag(classDom, boxClassDom, pos);
  43. }
  44. });
  45. });
  46. } else {
  47. window.removeEventListener('resize', refreshPos);
  48. setCachePos(useIdDargClass, pos.value);
  49. }
  50. });
  51. const styleDrag = computed(() => {
  52. // 没有设置拖动的时候保持原本的
  53. return pos.value.left === -1 && pos.value.top === -1
  54. ? {}
  55. : {
  56. position: 'fixed',
  57. left: `${pos.value.left}px`,
  58. top: `${pos.value.top}px`,
  59. transform: 'initial',
  60. transformOrigin: 'initial',
  61. margin: 'initial',
  62. transition: 'initial'
  63. };
  64. });
  65. function initPos() {
  66. const posCache = getCachePos(useIdDargClass);
  67. // 有缓存 用缓存的值,没有缓存用默认
  68. if (posCache) {
  69. pos.value = posCache;
  70. nextTick(() => {
  71. refreshPos();
  72. });
  73. }
  74. }
  75. function refreshPos() {
  76. if (pos.value.left === -1 && pos.value.top === -1) {
  77. return;
  78. }
  79. const boxClassDom = document.querySelector(`.${boxClass}`) as HTMLElement;
  80. if (!boxClassDom) return;
  81. const parentElementRect = boxClassDom.getBoundingClientRect();
  82. const clientWidth = document.documentElement.clientWidth;
  83. const clientHeight = document.documentElement.clientHeight;
  84. const { top, left } = pos.value;
  85. const maxLeft = clientWidth - parentElementRect.width;
  86. const maxTop = clientHeight - parentElementRect.height;
  87. let moveX = left;
  88. let moveY = top;
  89. const minLeft = 0;
  90. const minTop = 0;
  91. moveX = moveX < minLeft ? minLeft : moveX > maxLeft ? maxLeft : moveX;
  92. moveY = moveY < minTop ? minTop : moveY > maxTop ? maxTop : moveY;
  93. pos.value = {
  94. top: moveY,
  95. left: moveX
  96. };
  97. }
  98. return {
  99. pos,
  100. styleDrag
  101. };
  102. }
  103. // 拖动
  104. function drag(el: HTMLElement, parentElement: HTMLElement, pos: Ref<posType>) {
  105. function onDown(e: MouseEvent | TouchEvent) {
  106. const isTouchEv = isTouchEvent(e);
  107. const event = isTouchEv ? e.touches[0] : e;
  108. const parentElementRect = parentElement.getBoundingClientRect();
  109. const downX = event.clientX;
  110. const downY = event.clientY;
  111. const clientWidth = document.documentElement.clientWidth;
  112. const clientHeight = document.documentElement.clientHeight;
  113. const maxLeft = clientWidth - parentElementRect.width;
  114. const maxTop = clientHeight - parentElementRect.height;
  115. const minLeft = 0;
  116. const minTop = 0;
  117. function onMove(e: MouseEvent | TouchEvent) {
  118. const event = isTouchEvent(e) ? e.touches[0] : e;
  119. let moveX = parentElementRect.left + (event.clientX - downX);
  120. let moveY = parentElementRect.top + (event.clientY - downY);
  121. moveX = moveX < minLeft ? minLeft : moveX > maxLeft ? maxLeft : moveX;
  122. moveY = moveY < minTop ? minTop : moveY > maxTop ? maxTop : moveY;
  123. pos.value = {
  124. top: moveY,
  125. left: moveX
  126. };
  127. }
  128. function onUp() {
  129. document.removeEventListener(
  130. isTouchEv ? 'touchmove' : 'mousemove',
  131. onMove
  132. );
  133. document.removeEventListener(isTouchEv ? 'touchend' : 'mouseup', onUp);
  134. }
  135. document.addEventListener(isTouchEv ? 'touchmove' : 'mousemove', onMove);
  136. document.addEventListener(isTouchEv ? 'touchend' : 'mouseup', onUp);
  137. }
  138. el.addEventListener('mousedown', onDown);
  139. el.addEventListener('touchstart', onDown);
  140. }
  141. function isTouchEvent(e: MouseEvent | TouchEvent): e is TouchEvent {
  142. return window.TouchEvent && e instanceof window.TouchEvent;
  143. }
  144. // 缓存
  145. const localStorageName = 'dragCachePos';
  146. function getCachePos(useIdDargClass: string): null | undefined | posType {
  147. const localCachePos = localStorage.getItem(localStorageName);
  148. if (localCachePos) {
  149. try {
  150. return JSON.parse(localCachePos)[useIdDargClass];
  151. } catch {
  152. return null;
  153. }
  154. }
  155. return null;
  156. }
  157. function setCachePos(useIdDargClass: string, pos: posType) {
  158. const localCachePos = localStorage.getItem(localStorageName);
  159. let cachePosObj: Record<string, any> = {};
  160. if (localCachePos) {
  161. try {
  162. cachePosObj = JSON.parse(localCachePos);
  163. } catch {
  164. //
  165. }
  166. }
  167. cachePosObj[useIdDargClass] = pos;
  168. localStorage.setItem(localStorageName, JSON.stringify(cachePosObj));
  169. }