drag.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. <template>
  2. <div class="nut-drag" @touchstart="touchStart($event)">
  3. <slot></slot>
  4. </div>
  5. </template>
  6. <script>
  7. import requestAniFrame from '../../utils/raf.js';
  8. /**
  9. * @module drag
  10. * @description 拖拽组件,用于页面中需要拖拽的元素
  11. * @vue-prop {Boolean} [attract=false] - 拖拽元素是否需要自动吸边
  12. * @vue-prop {String} [direction='all'] - 拖拽元素的拖拽方向
  13. * @vue-prop {Number | String} [zIndex=11] - 拖拽元素的堆叠顺序
  14. * @vue-prop {Object} [boundary={top: 0,left: 0,right: 0,bottom: 0}] - 拖拽元素的拖拽边界
  15. * @vue-data {Number} elWidth 拖拽元素的宽度
  16. * @vue-data {Number} elHeight 拖拽元素的高度
  17. * @vue-data {Number} screenWidth 屏幕的宽度
  18. * @vue-data {Number} screenHeight 屏幕的高度
  19. * @vue-data {Number} startTop 拖拽元素距离顶部的距离
  20. * @vue-data {Number} startLeft 拖拽元素距离左侧的距离
  21. * @vue-data {Object} position 鼠标点击的位置,包含距离x轴和y轴的距离
  22. */
  23. export default {
  24. name: 'nut-drag',
  25. props: {
  26. attract: {
  27. type: Boolean,
  28. default: false
  29. },
  30. direction: {
  31. type: String,
  32. default: 'all'
  33. },
  34. zIndex: {
  35. type: [Number, String],
  36. default: 11
  37. },
  38. boundary: {
  39. type: Object,
  40. default: function() {
  41. return {
  42. top: 0,
  43. left: 0,
  44. right: 0,
  45. bottom: 0
  46. };
  47. }
  48. }
  49. },
  50. data() {
  51. return {
  52. elWidth: 0,
  53. elHeight: 0,
  54. screenWidth: 0,
  55. screenHeight: 0,
  56. startTop: 0,
  57. startLeft: 0,
  58. position: { x: 0, y: 0 }
  59. };
  60. },
  61. methods: {
  62. /**
  63. * 获取拖拽元素的属性和屏幕的宽高,初始化拖拽元素的位置
  64. */
  65. getElementInfo() {
  66. const el = this.$el;
  67. const domElem = document.documentElement;
  68. this.elWidth = el.offsetWidth;
  69. this.elHeight = el.offsetHeight;
  70. this.screenWidth = domElem.clientWidth;
  71. this.screenHeight = domElem.clientHeight;
  72. el.style.zIndex = this.zIndex;
  73. if (this.boundary.left && !el.style.left) {
  74. el.style.left = this.boundary.left + 'px';
  75. } else if (this.boundary.right && !el.style.right) {
  76. el.style.right = this.boundary.right + 'px';
  77. }
  78. if (this.boundary.top && !el.style.top) {
  79. el.style.top = this.boundary.top + 'px';
  80. } else if (this.boundary.bottom && !el.style.bottom) {
  81. el.style.bottom = this.boundary.bottom + 'px';
  82. }
  83. },
  84. touchStart(e) {
  85. const target = e.currentTarget;
  86. this.startTop = target.offsetTop; // 元素距离顶部的距离
  87. this.startLeft = target.offsetLeft; // 元素距离左侧的距离
  88. this.position.x = e.touches[0].clientX; // 鼠标点击的x轴的距离
  89. this.position.y = e.touches[0].clientY; // 鼠标点击的y轴的距离
  90. this.$el.addEventListener('touchmove', this.touchMove, false);
  91. this.$el.addEventListener('touchend', this.touchEnd, false);
  92. },
  93. touchMove(e) {
  94. e.preventDefault();
  95. const target = e.currentTarget;
  96. if (e.targetTouches.length == 1) {
  97. const touch = e.targetTouches[0];
  98. this.nx = touch.clientX - this.position.x;
  99. this.ny = touch.clientY - this.position.y;
  100. this.xPum = this.startLeft + this.nx;
  101. this.yPum = this.startTop + this.ny;
  102. const rightLocation = this.screenWidth - this.elWidth - this.boundary.right;
  103. // 限制左右拖拽边界
  104. if (Math.abs(this.xPum) > rightLocation) {
  105. this.xPum = rightLocation;
  106. } else if (this.xPum <= this.boundary.left) {
  107. this.xPum = this.boundary.left;
  108. }
  109. // 限制上下拖拽边界
  110. if (this.yPum < this.boundary.top) {
  111. this.yPum = this.boundary.top;
  112. } else if (this.yPum > this.screenHeight - this.elHeight - this.boundary.bottom) {
  113. this.yPum = this.screenHeight - this.elHeight - this.boundary.bottom;
  114. }
  115. if (this.direction != 'y') {
  116. target.style.left = this.xPum + 'px';
  117. }
  118. if (this.direction != 'x') {
  119. target.style.top = this.yPum + 'px';
  120. }
  121. }
  122. },
  123. touchEnd(e) {
  124. const target = e.currentTarget;
  125. const touch = e.changedTouches[0];
  126. let currX = touch.clientX;
  127. const rightLocation = this.screenWidth - this.elWidth - this.boundary.right;
  128. if (currX > rightLocation) {
  129. currX = rightLocation;
  130. // console.log('往右划出边界');
  131. } else if (currX < this.boundary.left) {
  132. currX = this.boundary.left;
  133. // console.log('往左划出边界');
  134. } else {
  135. currX = currX < this.screenWidth / 2 ? this.boundary.left : rightLocation;
  136. // console.log('在边界内滑动');
  137. }
  138. if (this.direction != 'y' && this.attract) {
  139. if (currX < this.screenWidth / 2) {
  140. this.goLeft(target);
  141. } else {
  142. this.goRight(target, rightLocation);
  143. }
  144. }
  145. if (this.direction != 'x') {
  146. target.style.top = this.yPum + 'px';
  147. }
  148. },
  149. goLeft(target) {
  150. if (this.boundary.left) {
  151. if (target.style.left.split('px')[0] > this.boundary.left) {
  152. target.style.left = target.style.left.split('px')[0] - 10 + 'px';
  153. requestAniFrame(() => {
  154. this.goLeft(target);
  155. });
  156. } else {
  157. target.style.left = `${this.boundary.left}px`;
  158. }
  159. } else {
  160. if (target.style.left.split('px')[0] > 10) {
  161. target.style.left = target.style.left.split('px')[0] - 10 + 'px';
  162. requestAniFrame(() => {
  163. this.goLeft(target);
  164. });
  165. } else {
  166. target.style.left = '0px';
  167. }
  168. }
  169. },
  170. goRight(target, rightLocation) {
  171. if (rightLocation - parseInt(target.style.left.split('px')[0]) > 10) {
  172. target.style.left = parseInt(target.style.left.split('px')[0]) + 10 + 'px';
  173. requestAniFrame(() => {
  174. this.goRight(target, rightLocation);
  175. });
  176. } else {
  177. target.style.left = rightLocation + 'px';
  178. }
  179. }
  180. },
  181. mounted() {
  182. this.getElementInfo();
  183. },
  184. activated() {
  185. if (this.keepAlive) {
  186. this.keepAlive = false;
  187. }
  188. },
  189. deactivated() {
  190. this.keepAlive = true;
  191. this.$el.removeEventListener('touchmove', this.handleScroll, false);
  192. this.$el.removeEventListener('touchend', this.handleScroll, false);
  193. },
  194. destroyed() {
  195. this.$el.removeEventListener('touchmove', this.handleScroll, false);
  196. this.$el.removeEventListener('touchend', this.handleScroll, false);
  197. }
  198. };
  199. </script>