Browse Source

feat(swipe): vue3

richard1015 4 years ago
parent
commit
62a15ea5db

+ 102 - 0
src/packages/__VUE/swipe/demo.vue

@@ -0,0 +1,102 @@
+<template>
+  <div class="demo full">
+    <h2>基础用法</h2>
+    <nut-swipe>
+      <nut-cell round-radius="0" desc="左滑删除" />
+      <template #right>
+        <nut-button shape="square" style="height: 100%" type="danger"
+          >删除</nut-button
+        >
+      </template>
+    </nut-swipe>
+    <h2>禁止滑动</h2>
+    <nut-swipe disabled>
+      <nut-cell round-radius="0" desc="禁止滑动" />
+      <template #right>
+        <nut-button shape="square" style="height: 100%" type="danger"
+          >删除</nut-button
+        >
+      </template>
+    </nut-swipe>
+    <h2>左右滑动</h2>
+    <nut-swipe>
+      <template #left>
+        <nut-button shape="square" style="height: 100%" type="success"
+          >选择</nut-button
+        >
+      </template>
+      <nut-cell round-radius="0" desc="左滑右滑都可以哦" />
+      <template #right>
+        <nut-button shape="square" style="height: 100%" type="danger"
+          >删除</nut-button
+        >
+        <nut-button shape="square" style="height: 100%" type="info"
+          >收藏</nut-button
+        >
+      </template>
+    </nut-swipe>
+    <h2>异步控制</h2>
+    <nut-swipe ref="refSwipe">
+      <nut-cell title="异步打开关闭">
+        <template v-slot:link>
+          <nut-switch
+            v-model="checked"
+            @change="changSwitch"
+            active-text="开"
+            inactive-text="关"
+          />
+        </template>
+      </nut-cell>
+      <template #right>
+        <nut-button shape="square" style="height: 100%" type="danger"
+          >删除</nut-button
+        >
+      </template>
+    </nut-swipe>
+    <h2>自定义</h2>
+    <nut-swipe>
+      <template #left>
+        <nut-button shape="square" style="height: 100%" type="success"
+          >选择</nut-button
+        >
+      </template>
+      <nut-cell title="商品描述">
+        <template v-slot:link>
+          <nut-inputnumber v-model="number" />
+        </template>
+      </nut-cell>
+      <template #right>
+        <nut-button shape="square" style="height: 100%" type="danger"
+          >删除</nut-button
+        >
+        <nut-button shape="square" style="height: 100%" type="info"
+          >收藏</nut-button
+        >
+      </template>
+    </nut-swipe>
+  </div>
+</template>
+
+<script lang="ts">
+import { ref } from 'vue';
+import { createComponent } from '../../utils/create';
+const { createDemo } = createComponent('swipe');
+export default createDemo({
+  props: {},
+  setup() {
+    const refSwipe = ref<HTMLElement>();
+    const checked = ref(false);
+    const number = ref(0);
+    const changSwitch = (value: boolean) => {
+      if (value) {
+        refSwipe.value?.open('left');
+      } else {
+        refSwipe.value?.close();
+      }
+    };
+    return { checked, number, changSwitch, refSwipe };
+  }
+});
+</script>
+
+<style lang="scss" scoped></style>

+ 147 - 0
src/packages/__VUE/swipe/doc.md

@@ -0,0 +1,147 @@
+#  swipe组件
+
+### 介绍
+
+常用于单元格左滑删除等手势操作
+
+### 安装
+
+``` javascript
+import { createApp } from 'vue';
+//vue
+import { Swipe } from '@nutui/nutui';
+//taro
+import { Swipe } from '@nutui/nutui-taro';
+
+const app = createApp();
+app.use(Swipe);
+```
+
+## 代码演示
+
+### 基础用法
+
+``` html
+<nut-swipe>
+    <nut-cell round-radius="0" desc="左滑删除" />
+    <template #right>
+        <nut-button shape="square" style="height:100%" type="danger">删除</nut-button>
+    </template>
+</nut-swipe>
+```
+
+
+### 禁止滑动
+
+``` html
+<nut-swipe disabled>
+    <nut-cell round-radius="0" desc="禁止滑动" />
+    <template #right>
+        <nut-button shape="square" style="height:100%" type="danger">删除</nut-button>
+    </template>
+</nut-swipe>
+```
+
+
+### 左右滑动
+
+``` html
+<nut-swipe>
+    <template #left>
+        <nut-button shape="square" style="height:100%" type="success">选择</nut-button>
+    </template>
+    <nut-cell round-radius="0" desc="左滑右滑都可以哦" />
+    <template #right>
+        <nut-button shape="square" style="height:100%" type="danger">删除</nut-button>
+        <nut-button shape="square" style="height:100%" type="info">收藏</nut-button>
+    </template>
+</nut-swipe>
+```
+
+
+### 异步控制
+
+``` html
+<nut-swipe ref="refSwipe">
+    <nut-cell title="异步打开关闭">
+    <template v-slot:link>
+        <nut-switch v-model="checked" @change="changSwitch" active-text="开" inactive-text="关" />
+    </template>
+    </nut-cell>
+    <template #right>
+        <nut-button shape="square" style="height:100%" type="danger">删除</nut-button>
+    </template>
+</nut-swipe>
+```
+``` typescript
+    setup() {
+        const refSwipe = ref<HTMLElement>();
+        const checked = ref(false);
+        const changSwitch = (value: boolean) => {
+        if (value) {
+            refSwipe.value?.open('left');
+        } else {
+            refSwipe.value?.close();
+        }
+        };
+        return { checked, changSwitch, refSwipe };
+    }
+```
+
+### 自定义
+
+``` html
+<nut-swipe>
+    <template #left>
+        <nut-button shape="square" style="height:100%" type="success">选择</nut-button>
+    </template>
+    <nut-cell title="商品描述">
+    <template v-slot:link>
+        <nut-inputnumber v-model="number" />
+    </template>
+    </nut-cell>
+    <template #right>
+        <nut-button shape="square" style="height:100%" type="danger">删除</nut-button>
+        <nut-button shape="square" style="height:100%" type="info">收藏</nut-button>
+    </template>
+</nut-swipe>
+```
+
+``` typescript
+    setup() {
+        const number = ref(0);
+        return { number };
+    }
+```
+
+
+
+### Props
+
+| 参数     | 说明         | 类型   | 默认值 |
+|----------|--------------|--------|--------|
+| name     | 唯一标识     | String | -      |
+| disabled | 是否禁用滑动 | String | false  |
+
+### Events
+
+| 事件名 | 说明       | 回调参数               |
+|--------|------------|------------------------|
+| open   | 打开时触发 | {type:'left or right'} |
+| close  | 关闭时触发 | {type:'left or right'} |
+    
+
+### Slots
+| 名称    | 说明         |
+|---------|--------------|
+| left    | 左侧滑动内容 |
+| default | 自定义内容   |
+| right   | 右侧滑动内容 |
+
+### 方法
+通过 ref 可以获取到 Swipe 实例并调用实例方法。
+
+| 方法名 | 说明             | 参数          |
+|--------|------------------|---------------|
+| open   | 打开单元格侧边栏 | left or right |
+| close  | 收起单元格侧边栏 |               |

+ 22 - 0
src/packages/__VUE/swipe/index.scss

@@ -0,0 +1,22 @@
+.nut-swipe {
+  position: relative;
+  display: block;
+  transition: all 0.3s linear;
+  &__left,
+  &__right {
+    position: absolute;
+    top: 0;
+    height: 100%;
+  }
+  &__left {
+    left: 0;
+    transform: translate3d(-100%, 0, 0);
+  }
+  &__right {
+    right: 0;
+    transform: translate3d(100%, 0, 0);
+  }
+  &__content {
+    display: inherit;
+  }
+}

+ 195 - 0
src/packages/__VUE/swipe/index.vue

@@ -0,0 +1,195 @@
+<template>
+  <view
+    ref="swipeRef"
+    :class="classes"
+    :style="touchStyle"
+    @touchstart="onTouchStart"
+    @touchmove="onTouchMove"
+    @touchend="onTouchEnd"
+    @touchcancel="onTouchEnd"
+  >
+    <view class="nut-swipe__left" ref="leftRef">
+      <slot name="left"></slot>
+    </view>
+
+    <view class="nut-swipe__content">
+      <slot name="default"></slot>
+    </view>
+
+    <view class="nut-swipe__right" ref="rightRef">
+      <slot name="right"></slot>
+    </view>
+  </view>
+</template>
+<script lang="ts">
+import { useTouch } from '@/packages/utils/useTouch';
+import { computed, reactive, Ref, ref } from 'vue';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('swipe');
+
+export type SwipePosition = 'left' | 'right' | '';
+export default create({
+  props: {
+    name: {
+      type: String,
+      default: ''
+    },
+    disabled: {
+      type: Boolean,
+      default: false
+    }
+  },
+  emits: ['open', 'close'],
+
+  setup(props, { emit }) {
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true
+      };
+    });
+
+    const getRefWidth = (ref: Ref<HTMLElement | undefined>): number => {
+      return ref.value?.clientWidth || 0;
+    };
+    const swipeRef = ref<HTMLElement>(),
+      swipeRefWidth = computed(() => {
+        return getRefWidth(swipeRef);
+      });
+    const leftRef = ref<HTMLElement>(),
+      leftRefWidth = computed(() => {
+        return getRefWidth(leftRef);
+      });
+    const rightRef = ref<HTMLElement>(),
+      rightRefWidth = computed(() => {
+        return getRefWidth(rightRef);
+      });
+    const touchMaxWidth = computed(() => {
+      return rightRefWidth.value + +rightRefWidth.value;
+    });
+
+    let opened: boolean = false;
+    let position: SwipePosition = '';
+    let oldPosition: SwipePosition = '';
+
+    const state = reactive({
+      offset: 0,
+      moving: false
+    });
+
+    const open = (position: SwipePosition = '') => {
+      opened = true;
+      if (position) {
+        state.offset =
+          position === 'left' ? -rightRefWidth.value : leftRefWidth.value;
+      }
+      emit('open', {
+        name: props.name,
+        position
+      });
+    };
+
+    const close = () => {
+      state.offset = 0;
+
+      if (opened) {
+        opened = false;
+        emit('close', {
+          name: props.name,
+          position
+        });
+      }
+    };
+
+    const touchStyle = computed(() => {
+      return {
+        transform: `translate3d(${state.offset}px, 0, 0)`
+      };
+    });
+
+    const setoffset = (deltaX: number) => {
+      position = deltaX > 0 ? 'right' : 'left';
+      let offset = deltaX;
+      switch (position) {
+        case 'left':
+          if (opened && oldPosition === position) {
+            offset = -rightRefWidth.value;
+          } else {
+            offset =
+              Math.abs(deltaX) > rightRefWidth.value
+                ? -rightRefWidth.value
+                : deltaX;
+          }
+          break;
+        case 'right':
+          if (opened && oldPosition === position) {
+            offset = leftRefWidth.value;
+          } else {
+            offset =
+              Math.abs(deltaX) > leftRefWidth.value
+                ? leftRefWidth.value
+                : deltaX;
+          }
+          break;
+      }
+      state.offset = offset;
+    };
+
+    const touch = useTouch();
+    const touchMethods = {
+      onTouchStart(event: Event) {
+        if (props.disabled) return;
+        touch.start(event);
+      },
+      onTouchMove(event: Event) {
+        if (props.disabled) return;
+        if (touch.isVertical()) return;
+        state.moving = true;
+        touch.move(event);
+        setoffset(touch.deltaX.value);
+
+        event.preventDefault();
+      },
+      onTouchEnd() {
+        if (state.moving) {
+          state.moving = false;
+          oldPosition = position;
+          switch (position) {
+            case 'left':
+              if (Math.abs(state.offset) < rightRefWidth.value / 2) {
+                close();
+              } else {
+                state.offset = -rightRefWidth.value;
+                open();
+              }
+              break;
+            case 'right':
+              if (Math.abs(state.offset) < leftRefWidth.value / 2) {
+                close();
+              } else {
+                state.offset = leftRefWidth.value;
+                open();
+              }
+              break;
+          }
+        }
+      }
+    };
+
+    return {
+      classes,
+      touchStyle,
+      ...touchMethods,
+      leftRef,
+      rightRef,
+      swipeRef,
+      open,
+      close
+    };
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>