Browse Source

feat: picker

yangxiaolu3 3 years ago
parent
commit
28ce45a5ce
2 changed files with 52 additions and 18 deletions
  1. 47 18
      src/packages/picker/picker-slot.vue
  2. 5 0
      src/packages/picker/picker.vue

+ 47 - 18
src/packages/picker/picker-slot.vue

@@ -1,6 +1,6 @@
 <template>
-  <div class="nut-picker-list">
-    <div class="nut-picker-roller" ref="roller">
+  <div class="nut-picker-list" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd">
+    <div class="nut-picker-roller" ref="roller" @transitionend="stopMomentum">
       <div
         class="nut-picker-roller-item"
         :class="{ 'nut-picker-roller-item-hidden': isHidden(index + 1) }"
@@ -24,6 +24,14 @@
   </div>
 </template>
 <script>
+const DEFAULT_DURATION = 200;
+
+// 触发惯性滑动条件:
+// 在手指离开屏幕时,如果和上一次 move 时的间隔小于 `MOMENTUM_TIME` 且 move
+// 距离大于 `MOMENTUM_DISTANCE` 时,执行惯性滑动
+const INERTIA_TIME = 300;
+const INERTIA_DISTANCE = 15;
+
 export default {
   name: 'nut-picker-slot',
   props: {
@@ -61,6 +69,8 @@ export default {
       lineSpacing: 36,
       rotation: 20,
       timer: null,
+
+      touchTime: 0,
     };
   },
   watch: {
@@ -95,7 +105,7 @@ export default {
       }
     },
 
-    setTransform(translateY = 0, type, time = 1000, deg) {
+    setTransform(translateY = 0, type, time = DEFAULT_DURATION, deg) {
       if (type === 'end') {
         this.$refs.list.style.webkitTransition = `transform ${time}ms cubic-bezier(0.19, 1, 0.22, 1)`;
         this.$refs.roller.style.webkitTransition = `transform ${time}ms cubic-bezier(0.19, 1, 0.22, 1)`;
@@ -103,9 +113,12 @@ export default {
         this.$refs.list.style.webkitTransition = '';
         this.$refs.roller.style.webkitTransition = '';
       }
-      this.$refs.list.style.webkitTransform = `translate3d(0, ${translateY}px, 0)`;
-      this.$refs.roller.style.webkitTransform = `rotate3d(1, 0, 0, ${deg})`;
-      this.scrollDistance = translateY;
+
+      if (translateY != null) {
+        this.$refs.list.style.webkitTransform = `translate3d(0, ${translateY}px, 0)`;
+        this.$refs.roller.style.webkitTransform = `rotate3d(1, 0, 0, ${deg})`;
+        this.scrollDistance = translateY;
+      }
     },
 
     setMove(move, type, time) {
@@ -145,7 +158,7 @@ export default {
       this.$emit('chooseItem', this.listData[Math.round(-move / this.lineSpacing)], this.keyIndex);
     },
 
-    touchStart(event) {
+    onTouchStart(event) {
       event.preventDefault();
 
       let changedTouches = event.changedTouches[0];
@@ -154,7 +167,7 @@ export default {
       this.transformY = this.scrollDistance;
     },
 
-    touchMove(event) {
+    onTouchMove(event) {
       event.preventDefault();
 
       let changedTouches = event.changedTouches[0];
@@ -165,7 +178,7 @@ export default {
       this.setMove(move);
     },
 
-    touchEnd(event) {
+    onTouchEnd(event) {
       event.preventDefault();
 
       let changedTouches = event.changedTouches[0];
@@ -174,9 +187,9 @@ export default {
       let move = this.touchParams.lastY - this.touchParams.startY;
 
       let moveTime = this.touchParams.lastTime - this.touchParams.startTime;
-      if (moveTime <= 300) {
-        move = move * 2;
-        this.setMove(move, 'end', +this.swipeDuration);
+      if (moveTime <= INERTIA_TIME && Math.abs(move) > INERTIA_DISTANCE) {
+        const distance = this.momentum(move, moveTime);
+        this.setMove(distance, 'end', +this.swipeDuration);
       } else {
         this.setMove(move, 'end');
       }
@@ -200,22 +213,38 @@ export default {
       type && this.setChooseValue(-move);
       this.setMove(-move);
     },
+
+    // 惯性滚动结束
+    stopMomentum() {
+      console.log('惯性滚动结束');
+      this.setChooseValue(this.scrollDistance);
+      this.setTransform(null, 'end');
+    },
+
+    // 惯性滚动 距离
+    momentum(distance, duration) {
+      // 惯性滚动的速度
+      const speed = Math.abs(distance / duration);
+      // 惯性滚动的距离
+      distance = (speed / 0.003) * (distance < 0 ? -1 : 1);
+      return distance;
+    },
   },
 
   mounted() {
     this.$nextTick(() => {
       this.modifyStatus(true);
       // 监听
-      this.$el.addEventListener('touchstart', this.touchStart);
-      this.$el.addEventListener('touchmove', this.touchMove);
-      this.$el.addEventListener('touchend', this.touchEnd);
+      // this.$el.addEventListener('touchstart', this.touchStart);
+      // this.$el.addEventListener('touchmove', this.touchMove);
+      // this.$el.addEventListener('touchend', this.touchEnd);
     });
   },
   beforeDestroy() {
     // 移除监听
-    this.$el.removeEventListener('touchstart', this.touchStart);
-    this.$el.removeEventListener('touchmove', this.touchMove);
-    this.$el.removeEventListener('touchend', this.touchEnd);
+    // this.$el.removeEventListener('touchstart', this.touchStart);
+    // this.$el.removeEventListener('touchmove', this.touchMove);
+    // this.$el.removeEventListener('touchend', this.touchEnd);
     clearTimeout(this.timer);
   },
 };

+ 5 - 0
src/packages/picker/picker.vue

@@ -93,6 +93,11 @@ export default {
     },
 
     confirm() {
+      this.listData.map((tag, i) => {
+        let ref = `picker-slot-${i}`;
+        this.$refs[ref] && this.$refs[ref][0].stopMomentum();
+      });
+
       this.$emit('confirm', this.cacheValueData);
       this.chooseValueData = [...this.cacheValueData];
       this.$emit('close');