浏览代码

fix(drag): taro h5 touch bug (#549)

Drjingfubo 4 年之前
父节点
当前提交
3e0faf5004

+ 14 - 19
src/packages/__VUE/drag/doc.taro.md

@@ -7,39 +7,34 @@
 
 ``` javascript
 import { createApp } from 'vue';
-import { Drag } from '@nutui/nutui-taro';
+import { Drag } from '@nutui/nutui';
 
 const app = createApp();
 app.use(Drag);
 ```
 
-## 限制拖拽边界
+## 基本用法
 ```html
-<nut-drag style="width: 90%; height: 200px; border: 1px solid red" direction="all" width="120"height="40">
-    <nut-button style="width: 120px; height: 40px" type="primary">触摸移动</nut-button>
+<nut-drag>
+  <div class="touch-dom">可点击,可拖拽</div>
 </nut-drag>
 ```
-
 ## 限制拖拽方向
 ```html
- <nut-drag direction="x" width="120" height="40">
-      <nut-button style="width: 120px; height: 40px" type="primary">只能X轴拖拽</nut-button>
- </nut-drag>
- <nut-drag direction="y" width="120" height="40">
-    <nut-button style="width: 120px; height: 40px" type="primary">只能X轴拖拽</nut-button>
- </nut-drag>
+<nut-drag direction="x">
+  <div class="touch-dom">只能在X轴拖动</div>
+</nut-drag>
 ```
-## 不限制边界
+
+## 限制拖拽方向
 ```html
- <nut-drag direction="all" width="120" height="40">
-    <nut-button style="width: 120px; height: 40px" type="primary" >触摸移动</nut-button>
- </nut-drag>
+<nut-drag direction="y">
+  <div class="touch-dom">只能在Y轴拖动</div>
+</nut-drag>
 ```
+
 ## Prop
 
 | 字段      | 说明                                              | 类型           | 默认值                              |
 | :-------- | :------------------------------------------------ | :------------- | :---------------------------------- |
-| width   | 子元素宽度                                  | string        |              ''                   |
-| height   | 子元素高度                                  | string        |             ''                    |
-| direction | 拖拽元素的拖拽方向限制,**x**/**y**/**all**三选一 | String         | 'all'                               |
-| boundary  | 拖拽元素的拖拽边界                                | Object         | {top: 0,left: 0,right: 0,bottom: 0} |
+| direction | 拖拽元素的拖拽方向限制,**x**/**y**/**all**三选一 | String         | 'all'                               |

+ 7 - 0
src/packages/__VUE/drag/index.scss

@@ -5,3 +5,10 @@
   width: fit-content;
   height: fit-content;
 }
+.nut-taro-drag {
+  // position: fixed;
+  display: inline-block;
+  z-index: 9997 !important;
+  width: fit-content;
+  height: fit-content;
+}

+ 230 - 25
src/packages/__VUE/drag/index.taro.vue

@@ -1,50 +1,255 @@
 <template>
-  <movable-area>
-    <movable-view
-      :style="{ width: width + 'px', height: height + 'px' }"
-      :direction="direction"
-    >
-      <slot></slot>
-    </movable-view>
-  </movable-area>
+  <view
+    :class="classes"
+    ref="myDrag"
+    class="myDrag"
+    @touchstart="touchStart($event)"
+    @touchmove.prevent="touchMove($event)"
+    catchtouchmove="true"
+    :style="{
+      transform: ` translate(${state.left + 'px'}, ${state.top + 'px'})`,
+      top: state.top + 'px',
+      left: state.left + 'px'
+    }"
+  >
+    <slot></slot>
+  </view>
 </template>
+
 <script lang="ts">
-import { ref, computed, watch, onMounted } from 'vue';
+import {
+  onMounted,
+  onDeactivated,
+  onActivated,
+  reactive,
+  ref,
+  computed
+} from 'vue';
 import { createComponent } from '../../utils/create';
 import requestAniFrame from '../../utils/raf';
 const { componentName, create } = createComponent('drag');
+import Taro, { eventCenter, getCurrentInstance } from '@tarojs/taro';
 export default create({
   props: {
+    attract: {
+      type: Boolean,
+      default: false
+    },
     direction: {
       type: String,
       default: 'all'
     },
-    width: {
-      type: String,
-      default: ''
-    },
-    height: {
-      type: String,
-      default: ''
+    boundary: {
+      type: Object,
+      default: () => {
+        return {
+          top: 0,
+          left: 0,
+          right: 0,
+          bottom: 0
+        };
+      }
     }
   },
-
   setup(props, { emit }) {
-    const direction = ref('all');
+    const myDrag = ref();
+    const state: any = reactive({
+      keepAlive: false,
+      elWidth: 0,
+      elHeight: 0,
+      screenWidth: 0,
+      screenHeight: 0,
+      startTop: 0,
+      startLeft: 0,
+      initTop: 0,
+      nx: 0,
+      ny: 0,
+      xPum: 0,
+      yPum: 0,
+      top: 0,
+      left: 0,
+      position: { x: 0, y: 0 },
+      boundary: {
+        top: 0,
+        left: 0,
+        right: 0,
+        bottom: 0
+      } as Record<string, any>
+    });
+
+    const classes = computed(() => {
+      const prefixCls = 'nut-taro-drag';
+      return {
+        [prefixCls]: true
+      };
+    });
+    const domElem = Taro.getSystemInfoSync();
+    function getInfo() {
+      const query = Taro.createSelectorQuery();
+      query
+        .select('.myDrag')
+        .boundingClientRect((rec: any) => {
+          state.elWidth = rec.width;
+          state.elHeight = rec.height;
+          state.initTop = rec.top;
+        })
+        .exec();
+      console.log(domElem.windowWidth);
+
+      state.screenWidth = domElem.screenWidth;
+      state.screenHeight = domElem.screenHeight;
+    }
+
+    function goLeft() {
+      if (state.boundary.left) {
+        if (+state.left.split('px')[0] > state.boundary.left) {
+          state.left = +state.left.split('px')[0] - 10 + 'px';
+          requestAniFrame(() => {
+            goLeft();
+          });
+        } else {
+          state.left = `${state.boundary.left}px`;
+        }
+      } else {
+        if (+state.left.split('px')[0] > 10) {
+          state.left = +state.left.split('px')[0] - 10 + 'px';
+          requestAniFrame(() => {
+            goLeft();
+          });
+        } else {
+          state.left = '0px';
+        }
+      }
+    }
+    function goRight(rightLocation: number) {
+      if (rightLocation - parseInt(state.left.split('px')[0]) > 10) {
+        state.left = parseInt(state.left.split('px')[0]) + 10 + 'px';
+        requestAniFrame(() => {
+          goRight(rightLocation);
+        });
+      } else {
+        state.left = rightLocation + 'px';
+      }
+    }
+    function touchMove(e: TouchEvent) {
+      e.preventDefault();
+      // const target = e.currentTarget as HTMLElement;
+      if (e.touches.length === 1) {
+        // const touch = e.targetTouches[0];
+        const touch = e.touches[0];
+        state.nx = touch.clientX - state.position.x;
+        state.ny = touch.clientY - state.position.y;
+        state.xPum = state.startLeft + state.nx;
+        state.yPum = state.startTop + state.ny;
 
-    function checkDirection() {
-      if (props.direction == 'x') {
-        direction.value = 'horizontal';
+        const rightLocation =
+          state.screenWidth - state.elWidth - state.boundary.right;
+        if (Math.abs(state.xPum) > rightLocation) {
+          state.xPum = rightLocation;
+        } else if (state.xPum <= state.boundary.left) {
+          state.xPum = state.boundary.left;
+        }
+        if (state.yPum < state.boundary.top) {
+          state.yPum = state.boundary.top;
+        } else if (
+          state.yPum >
+          state.screenHeight - state.elHeight - state.boundary.bottom
+        ) {
+          state.yPum =
+            state.screenHeight - state.elHeight - state.boundary.bottom;
+        }
+
+        if (props.direction != 'y') {
+          state.left = state.xPum;
+        }
+        if (props.direction != 'x') {
+          state.top = state.yPum;
+        }
       }
-      if (props.direction == 'y') {
-        direction.value = 'vertical';
+    }
+    function touchEnd(e: TouchEvent) {
+      // const target = e.currentTarget as HTMLElement;
+
+      const touch = e.changedTouches[0];
+      let currX = touch.clientX;
+      const rightLocation =
+        state.screenWidth - state.elWidth - state.boundary.right;
+      if (currX > rightLocation) {
+        currX = rightLocation;
+      } else if (currX < state.boundary.left) {
+        currX = state.boundary.left;
+      } else {
+        currX =
+          currX < state.screenWidth / 2 ? state.boundary.left : rightLocation;
       }
+      if (props.direction != 'y' && props.attract) {
+        if (currX < state.screenWidth / 2) {
+          requestAniFrame(() => {
+            goLeft();
+          });
+        } else {
+          requestAniFrame(() => {
+            goRight(rightLocation);
+          });
+        }
+      }
+      if (props.direction !== 'x') {
+        state.top = state.yPum;
+      }
+    }
+    function touchStart(e: TouchEvent) {
+      const query = Taro.createSelectorQuery();
+      let id = (e as any)?.mpEvent?.currentTarget.id;
+      let offsetTop = (e as any)?.currentTarget?.offsetTop;
+      let offsetLeft = (e as any)?.currentTarget?.offsetLeft;
+      const touches = e.touches[0];
+      const mobileTop = (e.touches[0]?.target as any)?.parentNode?.style.top;
+      const mobileLeft = (e.touches[0]?.target as any)?.parentNode?.style.left;
+      query
+        .selectAll('.myDrag')
+        .boundingClientRect((rec: any) => {
+          // 判断当前是要拖拽哪个元素
+          rec.forEach((element: any) => {
+            // 微信环境
+            if (id && id == element.id) {
+              state.startTop = element.top - offsetTop;
+              state.startLeft = element.left - offsetLeft;
+            } else if (mobileTop) {
+              // h5环境
+              state.startTop = Number(mobileTop.slice(0, -2));
+              state.startLeft = Number(mobileLeft.slice(0, -2));
+            }
+          });
+        })
+        .exec();
+      state.position.x = touches.clientX;
+      state.position.y = touches.clientY;
     }
     onMounted(() => {
-      checkDirection();
+      setTimeout(() => {
+        getInfo();
+      }, 200);
+      eventCenter.once((getCurrentInstance() as any).router.onReady, () => {});
+      state.boundary = props.boundary;
+    });
+    onActivated(() => {
+      if (state.keepAlive) {
+        state.keepAlive = false;
+      }
+    });
+    onDeactivated(() => {
+      state.keepAlive = true;
+      (myDrag as any).removeEventListener('touchstart', touchStart);
+      (myDrag as any).removeEventListener('touchmove', touchMove);
+      (myDrag as any).removeEventListener('touchend', touchEnd);
     });
     return {
-      direction
+      classes,
+      myDrag,
+      touchStart,
+      touchMove,
+      touchEnd,
+      state
     };
   }
 });

+ 2 - 1
src/sites/mobile-taro/vue/src/feedback/pages/drag/index.config.ts

@@ -1,3 +1,4 @@
 export default {
-  navigationBarTitleText: 'Drag'
+  navigationBarTitleText: 'Drag',
+  disableScroll: true
 };

+ 12 - 34
src/sites/mobile-taro/vue/src/feedback/pages/drag/index.vue

@@ -1,41 +1,16 @@
 <template>
-  <div class="demo full">
+  <div class="demo full dragDe">
     <h2>基础用法</h2>
-    <nut-drag
-      style="
-        width: 70%;
-        height: 200px;
-        border: 1px solid red;
-        margin: 0 0 0 20px;
-      "
-      direction="all"
-      width="120"
-      height="40"
-    >
-      <nut-button style="width: 120px; height: 40px" type="primary"
-        >触摸移动</nut-button
-      >
+    <nut-drag class="dragDemo">
+      <nut-button type="primary">触摸移动</nut-button>
     </nut-drag>
-    <h2 style="margin: 30px 0 0 0">限制拖拽方向</h2>
-    <nut-drag
-      style="width: 80%; height: 50px; margin: 10px 0 0 20px"
-      direction="x"
-      width="120"
-      height="40"
-    >
-      <nut-button style="width: 120px; height: 40px" type="primary"
-        >X轴拖拽</nut-button
-      >
+    <h2>x轴方向移动</h2>
+    <nut-drag class="dragDemo" direction="y">
+      <nut-button type="primary">Y轴移动</nut-button>
     </nut-drag>
-    <nut-drag
-      style="width: 100%; height: 200px; margin: 0 0 0 20px"
-      direction="y"
-      width="120"
-      height="40"
-    >
-      <nut-button style="width: 120px; height: 40px" type="primary"
-        >y轴拖拽</nut-button
-      >
+    <h2>y轴方向移动</h2>
+    <nut-drag class="dragDemo" direction="x">
+      <nut-button type="primary">X轴移动</nut-button>
     </nut-drag>
   </div>
 </template>
@@ -50,4 +25,7 @@ export default {
 .dragDemo {
   margin-left: 20px;
 }
+.dragDe {
+  height: 667px !important;
+}
 </style>