ソースを参照

feat: backtop taro

suzigang 4 年 前
コミット
e5ee7aface

+ 16 - 9
src/packages/__VUE/backtop/doc.taro.md

@@ -9,7 +9,7 @@
 ```javascript
 
 import { createApp } from 'vue';
-import { BackTop } from '@nutui/nutui';
+import { BackTop } from '@nutui/nutui@taro';
 
 const app = createApp();
 app.use(BackTop);
@@ -21,19 +21,29 @@ app.use(BackTop);
 ### 基本用法
 
 ```html
-<nut-backtop  el-id="elId" ></nut-backtop>
+<nut-backtop height="100vh">
+  <template v-slot:content>
+    <div class="text-data">我是测试数据1</div>
+    <div class="text-data">我是测试数据2</div>
+    <div class="text-data">我是测试数据3</div>
+    <div class="text-data">我是测试数据4</div>
+    <div class="text-data">我是测试数据5</div>
+    <div class="text-data">我是测试数据6</div>
+    ...
+  </template>
+</nut-backtop>
 ```
 
 ### 设置出现位置
 
 ```html
-<nut-backtop :distance="200" ></nut-backtop>
+<nut-backtop :distance="200"></nut-backtop>
 ```
 
 ### 自定义样式
 
 ```html
-<nut-backtop @click="handleClick" el-id="elId" :distance="100" :bottom="90" ><div>无</div></nut-backtop>
+<nut-backtop :distance="100" :bottom="90"><div>自定义内容</div></nut-backtop>
 ```
 
 ### click事件
@@ -58,20 +68,17 @@ export default createDemo({
 </script>
 ```
 
-
 ### API
 
 ### Prop  
 
 | 字段            | 说明                 | 类型    | 默认值  |
 |-----------------|------------------------------------------|---------|---------|
-| el-id           | 获取监听元素的父级元素         | String | -       |
+| height           | 滚动区域的高度         | String | `100vh`       |
 | bottom         | 距离页面底部距离    | Number  | `20`       |
 | right        | 距离页面右侧距离      | Number |  `10`  |
 | distance     | 页面垂直滚动多高后出现   | Number  | `200`      |
-| z-index         | 设置组件页面层级   | Number  | `10`       |  
-| is-animation         | 是否有动画,和duration参数互斥   | Boolean  | `true`       |  
-| duration         | 设置动画持续时间   | Number  | `1000`       |                                          
+| z-index         | 设置组件页面层级   | Number  | `10`       |                                          
 
 ### Event
 | 名称  | 说明     | 回调参数    |

+ 91 - 0
src/packages/__VUE/backtop/index.taro.vue

@@ -0,0 +1,91 @@
+<template>
+  <view>
+    <scroll-view
+      :scroll-y="true"
+      :style="{ height }"
+      @scroll="scroll"
+      :scroll-top="scrollTop"
+      scroll-with-animation="true"
+    >
+      <slot name="content"></slot>
+    </scroll-view>
+    <view :class="classes" :style="style" @click.stop="click">
+      <slot name="icon">
+        <nut-icon size="19px" class="nut-backtop-main" name="top"></nut-icon>
+      </slot>
+    </view>
+  </view>
+</template>
+
+<script lang="ts">
+import { reactive, computed, toRefs } from 'vue';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('backtop');
+export default create({
+  props: {
+    height: {
+      type: String,
+      default: '100vh'
+    },
+    bottom: {
+      type: Number,
+      default: 20
+    },
+    right: {
+      type: Number,
+      default: 10
+    },
+    zIndex: {
+      type: Number,
+      default: 10
+    },
+    distance: {
+      type: Number,
+      default: 200
+    }
+  },
+  emits: ['click'],
+  setup(props, { emit }) {
+    const state = reactive({
+      backTop: false,
+      scrollTop: 1
+    });
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true,
+        show: state.backTop
+      };
+    });
+    const style = computed(() => {
+      return {
+        right: `${props.right}px`,
+        bottom: `${props.bottom}px`,
+        zIndex: props.zIndex
+      };
+    });
+
+    const scroll = (e) => {
+      state.scrollTop = 2;
+      state.backTop = e.detail.scrollTop >= props.distance;
+    };
+
+    const click = (e: MouseEvent) => {
+      state.scrollTop = 1;
+      emit('click', e);
+    };
+
+    return {
+      ...toRefs(state),
+      classes,
+      style,
+      scroll,
+      click
+    };
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

+ 215 - 0
src/packages/__VUE/drag/index.taro.vue

@@ -0,0 +1,215 @@
+<template>
+  <view
+    :class="classes"
+    ref="myDrag"
+    @touchstart="touchStart($event)"
+    @touchmove="touchMove($event)"
+    @touchend="touchEnd($event)"
+  >
+    <slot></slot>
+  </view>
+</template>
+
+<script lang="ts">
+import {
+  onMounted,
+  onDeactivated,
+  onActivated,
+  reactive,
+  ref,
+  computed
+} from 'vue';
+import { createComponent } from '../../utils/create';
+import requestAniFrame from '../../utils/raf';
+const { componentName, create } = createComponent('drag');
+export default create({
+  props: {
+    attract: {
+      type: Boolean,
+      default: false
+    },
+    direction: {
+      type: String,
+      default: 'all'
+    },
+    boundary: {
+      type: Object,
+      default: () => {
+        return {
+          top: 0,
+          left: 0,
+          right: 0,
+          bottom: 0
+        };
+      }
+    }
+  },
+  setup(props, { emit }) {
+    const myDrag = ref();
+    const state = reactive({
+      keepAlive: false,
+      elWidth: 0,
+      elHeight: 0,
+      screenWidth: 0,
+      screenHeight: 0,
+      startTop: 0,
+      startLeft: 0,
+      nx: 0,
+      ny: 0,
+      xPum: 0,
+      yPum: 0,
+      position: { x: 0, y: 0 },
+      boundary: {
+        top: 0,
+        left: 0,
+        right: 0,
+        bottom: 0
+      } as Record<string, any>
+    });
+
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true
+      };
+    });
+
+    function getInfo() {
+      const domElem = document.documentElement;
+      state.elWidth = myDrag.value.offsetWidth;
+      state.elHeight = myDrag.value.offsetHeight;
+      state.screenWidth = domElem.clientWidth;
+      state.screenHeight = domElem.clientHeight;
+    }
+
+    function goLeft(target: HTMLElement) {
+      if (state.boundary.left) {
+        if (+target.style.left.split('px')[0] > state.boundary.left) {
+          target.style.left = +target.style.left.split('px')[0] - 10 + 'px';
+          requestAniFrame(() => {
+            goLeft(target);
+          });
+        } else {
+          target.style.left = `${state.boundary.left}px`;
+        }
+      } else {
+        if (+target.style.left.split('px')[0] > 10) {
+          target.style.left = +target.style.left.split('px')[0] - 10 + 'px';
+          requestAniFrame(() => {
+            goLeft(target);
+          });
+        } else {
+          target.style.left = '0px';
+        }
+      }
+    }
+    function goRight(target: HTMLElement, rightLocation: number) {
+      if (rightLocation - parseInt(target.style.left.split('px')[0]) > 10) {
+        target.style.left =
+          parseInt(target.style.left.split('px')[0]) + 10 + 'px';
+        requestAniFrame(() => {
+          goRight(target, rightLocation);
+        });
+      } else {
+        target.style.left = rightLocation + 'px';
+      }
+    }
+    function touchMove(e: TouchEvent) {
+      e.preventDefault();
+      const target = e.currentTarget as HTMLElement;
+      if (e.targetTouches.length === 1) {
+        const touch = e.targetTouches[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;
+        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') {
+          target.style.left = state.xPum + 'px';
+        }
+        if (props.direction != 'x') {
+          target.style.top = state.yPum + 'px';
+        }
+      }
+    }
+    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(target);
+          });
+        } else {
+          requestAniFrame(() => {
+            goRight(target, rightLocation);
+          });
+        }
+      }
+      if (props.direction !== 'x') {
+        target.style.top = state.yPum + 'px';
+      }
+    }
+    function touchStart(e: TouchEvent) {
+      const target = e.currentTarget as HTMLElement;
+      const touches = e.touches[0];
+      state.startTop = target.offsetTop;
+      state.startLeft = target.offsetLeft;
+      state.position.x = touches.clientX;
+      state.position.y = touches.clientY;
+    }
+    onMounted(() => {
+      getInfo();
+      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 {
+      classes,
+      myDrag,
+      touchStart,
+      touchMove,
+      touchEnd
+    };
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

+ 1 - 1
src/sites/mobile-taro/vue/project.config.json

@@ -23,7 +23,7 @@
     "compileHotReLoad": false,
     "useMultiFrameRuntime": true,
     "useApiHook": true,
-    "useApiHostProcess": false,
+    "useApiHostProcess": true,
     "babelSetting": {
       "ignore": [],
       "disablePlugins": [],

+ 1 - 0
src/sites/mobile-taro/vue/src/app.config.ts

@@ -1,5 +1,6 @@
 export default {
   pages: [
+    'pages/backtop/index',
     'pages/toast/index',
     'pages/notify/index',
     'pages/dialog/index',

+ 3 - 0
src/sites/mobile-taro/vue/src/pages/backtop/index.config.ts

@@ -0,0 +1,3 @@
+export default {
+  navigationBarTitleText: 'Backtop'
+};

+ 67 - 0
src/sites/mobile-taro/vue/src/pages/backtop/index.vue

@@ -0,0 +1,67 @@
+<template>
+  <view class="demo">
+    <nut-backtop @click="click">
+      <template v-slot:content>
+        <div class="text-data">我是测试数据1</div>
+        <div class="text-data">我是测试数据2</div>
+        <div class="text-data">我是测试数据3</div>
+        <div class="text-data">我是测试数据4</div>
+        <div class="text-data">我是测试数据5</div>
+        <div class="text-data">我是测试数据6</div>
+        <div class="text-data">我是测试数据7</div>
+        <div class="text-data">我是测试数据8</div>
+        <div class="text-data">我是测试数据9</div>
+        <div class="text-data">我是测试数据10</div>
+        <div class="text-data">我是测试数据11</div>
+        <div class="text-data">我是测试数据12</div>
+        <div class="text-data">我是测试数据13</div>
+        <div class="text-data">我是测试数据14</div>
+        <div class="text-data">我是测试数据15</div>
+        <div class="text-data">我是测试数据16</div>
+        <div class="text-data">我是测试数据17</div>
+        <div class="text-data">我是测试数据18</div>
+        <div class="text-data">我是测试数据19</div>
+        <div class="text-data">我是测试数据20</div>
+        <div class="text-data">我是测试数据21</div>
+        <div class="text-data">我是测试数据22</div>
+        <div class="text-data">我是测试数据23</div>
+        <div class="text-data">我是测试数据24</div>
+      </template>
+    </nut-backtop>
+  </view>
+</template>
+
+<script>
+export default {
+  setup() {
+    const click = () => {
+      console.log('click');
+    };
+
+    return {
+      click
+    };
+  }
+};
+</script>
+
+<style lang="scss">
+.demo {
+  .text-data {
+    margin: 0 auto;
+    margin-top: 15px;
+    margin-bottom: 20px;
+    padding-left: 16px;
+    display: flex;
+    align-items: center;
+    width: 100%;
+    height: 46px;
+    background: rgba(255, 255, 255, 1);
+    border-radius: 7px;
+    box-shadow: 0px 1px 7px 0px rgba(237, 238, 241, 1);
+    line-height: 19px;
+    font-size: 13px;
+    color: rgba(102, 102, 102, 1);
+  }
+}
+</style>

+ 1 - 2
src/sites/mobile/components/IndexTaro.vue

@@ -9,7 +9,7 @@
     </div>
     <div class="index-wxcode">
       <img
-        src="https://img12.360buyimg.com/imagetools/jfs/t1/162421/39/13392/9425/6052ea60E592310a9/264bdff23ef5fe95.png"
+        src="https://img12.360buyimg.com/imagetools/jfs/t1/174054/4/15968/66201/60d0028dE590f0aa8/752ecef62e4f1cbe.jpg"
         width="200"
       />
     </div>
@@ -62,7 +62,6 @@ export default defineComponent({
   &-wxcode {
     display: flex;
     height: calc(100% - 117px);
-    background: #f7f8fa;
     border-radius: 30px 30px 0 0;
     overflow: hidden;
     padding: 30px 25px;