drag.vue 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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) {
  74. el.style.left = this.boundary.left + "px";
  75. } else {
  76. el.style.right = this.boundary.right + "px";
  77. }
  78. if (this.boundary.top) {
  79. el.style.top = this.boundary.top + "px";
  80. } else {
  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 =
  103. this.screenWidth - this.elWidth - this.boundary.right;
  104. // 限制左右拖拽边界
  105. if (Math.abs(this.xPum) > rightLocation) {
  106. this.xPum = rightLocation;
  107. } else if (this.xPum <= this.boundary.left) {
  108. this.xPum = this.boundary.left;
  109. }
  110. // 限制上下拖拽边界
  111. if (this.yPum < this.boundary.top) {
  112. this.yPum = this.boundary.top;
  113. } else if (
  114. this.yPum >
  115. this.screenHeight - this.elHeight - this.boundary.bottom
  116. ) {
  117. this.yPum = this.screenHeight - this.elHeight - this.boundary.bottom;
  118. }
  119. if (this.direction != "y") {
  120. target.style.left = this.xPum + "px";
  121. }
  122. if (this.direction != "x") {
  123. target.style.top = this.yPum + "px";
  124. }
  125. }
  126. },
  127. touchEnd(e) {
  128. const target = e.currentTarget;
  129. const touch = e.changedTouches[0];
  130. let currX = touch.clientX;
  131. const rightLocation =
  132. this.screenWidth - this.elWidth - this.boundary.right;
  133. if (currX > rightLocation) {
  134. currX = rightLocation;
  135. // console.log('往右划出边界');
  136. } else if (currX < this.boundary.left) {
  137. currX = this.boundary.left;
  138. // console.log('往左划出边界');
  139. } else {
  140. currX =
  141. currX < this.screenWidth / 2 ? this.boundary.left : rightLocation;
  142. // console.log('在边界内滑动');
  143. }
  144. if (this.direction != "y" && this.attract) {
  145. if (currX < this.screenWidth / 2) {
  146. this.goLeft(target);
  147. } else {
  148. this.goRight(target, rightLocation);
  149. }
  150. }
  151. if (this.direction != "x") {
  152. target.style.top = this.yPum + "px";
  153. }
  154. },
  155. goLeft(target) {
  156. if (this.boundary.left) {
  157. if (target.style.left.split("px")[0] > this.boundary.left) {
  158. target.style.left = target.style.left.split("px")[0] - 10 + "px";
  159. requestAniFrame(() => {
  160. this.goLeft(target);
  161. });
  162. } else {
  163. target.style.left = `${this.boundary.left}px`;
  164. }
  165. } else {
  166. if (target.style.left.split("px")[0] > 10) {
  167. target.style.left = target.style.left.split("px")[0] - 10 + "px";
  168. requestAniFrame(() => {
  169. this.goLeft(target);
  170. });
  171. } else {
  172. target.style.left = "0px";
  173. }
  174. }
  175. },
  176. goRight(target, rightLocation) {
  177. if (rightLocation - parseInt(target.style.left.split("px")[0]) > 10) {
  178. target.style.left =
  179. parseInt(target.style.left.split("px")[0]) + 10 + "px";
  180. requestAniFrame(() => {
  181. this.goRight(target, rightLocation);
  182. });
  183. } else {
  184. target.style.left = rightLocation + "px";
  185. }
  186. },
  187. },
  188. mounted() {
  189. this.getElementInfo();
  190. },
  191. activated() {
  192. if (this.keepAlive) {
  193. this.keepAlive = false;
  194. }
  195. },
  196. deactivated() {
  197. this.keepAlive = true;
  198. this.$el.removeEventListener("touchmove", this.handleScroll, false);
  199. this.$el.removeEventListener("touchend", this.handleScroll, false);
  200. },
  201. destroyed() {
  202. this.$el.removeEventListener("touchmove", this.handleScroll, false);
  203. this.$el.removeEventListener("touchend", this.handleScroll, false);
  204. },
  205. };
  206. </script>