Browse Source

refactor: infiniteloading

suzigang 4 years ago
parent
commit
cc74341fd5

+ 1 - 1
src/config.json

@@ -160,7 +160,7 @@
           "version": "3.0.0",
           "name": "Notify",
           "type": "component",
-          "cName": "methods",
+          "cName": "消息通知",
           "desc": "在页面顶部展示消息提示,支持函数调用和组件调用两种方式",
           "sort": 4,
           "show": true,

+ 16 - 22
src/packages/infiniteloading/demo.vue

@@ -4,10 +4,10 @@
     <nut-cell>
       <ul class="infiniteUl" id="scroll">
         <nut-infiniteloading
-          containerId="scroll"
-          :useWindow="false"
-          :hasMore="hasMore"
-          @loadMore="loadMore"
+          container-id="scroll"
+          :use-window="false"
+          :has-more="hasMore"
+          @load-more="loadMore"
         >
           <li
             class="infiniteLi"
@@ -23,11 +23,12 @@
     <nut-cell>
       <ul class="infiniteUl" id="refreshScroll">
         <nut-infiniteloading
-          containerId="refreshScroll"
-          :useWindow="false"
-          :isOpenRefresh="true"
-          :hasMore="refreshHasMore"
-          @loadMore="refreshLoadMore"
+          pull-icon="JD"
+          container-id="refreshScroll"
+          :use-window="false"
+          :is-open-refresh="true"
+          :has-more="refreshHasMore"
+          @load-more="refreshLoadMore"
           @refresh="refresh"
         >
           <li
@@ -44,10 +45,12 @@
     <nut-cell>
       <ul class="infiniteUl" id="customScroll">
         <nut-infiniteloading
-          containerId="customScroll"
-          :useWindow="false"
-          :hasMore="customHasMore"
-          @loadMore="customLoadMore"
+          load-txt="loading"
+          load-more-txt="没有啦~"
+          container-id="customScroll"
+          :use-window="false"
+          :has-more="customHasMore"
+          @load-more="customLoadMore"
         >
           <li
             class="infiniteLi"
@@ -55,15 +58,6 @@
             :key="index"
             >{{ item }}</li
           >
-
-          <template v-slot:loading>
-            <div class="loading">
-              <span>加载中...</span>
-            </div>
-          </template>
-          <template v-slot:unloadMore>
-            <div class="unload-more">没有数据啦 ~~</div>
-          </template>
         </nut-infiniteloading>
       </ul>
     </nut-cell>

+ 30 - 33
src/packages/infiniteloading/doc.md

@@ -7,11 +7,11 @@
 ### 安装
 
 ```javascript
-    import { createApp } from 'vue';
-    import { InfiniteLoading } from '@nutui/nutui';
+  import { createApp } from 'vue';
+  import { InfiniteLoading } from '@nutui/nutui';
 
-    const app = createApp();
-    app.use(InfiniteLoading);
+  const app = createApp();
+  app.use(InfiniteLoading);
 ```
 
 ## 代码演示
@@ -22,9 +22,9 @@
 <ul class="infiniteUl" id="scroll">
     <nut-infiniteloading
         containerId = 'scroll'
-        :useWindow='false'
-        :hasMore="hasMore"
-        @loadMore="loadMore"
+        :use-window='false'
+        :has-more="hasMore"
+        @load-more="loadMore"
     >
         <li class="infiniteLi" v-for="(item, index) in defultList" :key="index">{{item}}</li>
     </nut-infiniteloading>
@@ -62,11 +62,12 @@ setup() {
 ```html
 <ul class="infiniteUl" id="refreshScroll">
   <nut-infiniteloading
-    containerId="refreshScroll"
-    :useWindow="false"
-    :isOpenRefresh="true"
-    :hasMore="refreshHasMore"
-    @loadMore="refreshLoadMore"
+    pull-icon="JD"
+    container-id="refreshScroll"
+    :use-window="false"
+    :is-open-refresh="true"
+    :has-more="refreshHasMore"
+    @load-more="refreshLoadMore"
     @refresh="refresh"
   >
     <li
@@ -119,10 +120,10 @@ setup() {
 ```html
 <ul class="infiniteUl" id="customScroll">
     <nut-infiniteloading
-        containerId = 'customScroll'
-        :useWindow='false'
-        :hasMore="customHasMore"
-        @loadMore="customLoadMore"
+        container-id = 'customScroll'
+        :use-window='false'
+        :has-more="customHasMore"
+        @load-more="customLoadMore"
     >
         <li class="infiniteLi" v-for="(item, index) in customList" :key="index">{{item}}</li>
         <template v-slot:loading>
@@ -170,27 +171,23 @@ setup() {
 
 | 参数         | 说明                             | 类型   | 默认值           |
 |--------------|----------------------------------|--------|------------------|
-| hasMore         | 是否还有更多数据               | Boolean | true                |
-| threshold         | 距离底部多远加载 | Number | 200               |
-| useWindow | 将滚动侦听器添加到 window 否则侦听组件的父节点     | Boolean | true |
-| useCapture          | 是否使用捕获模式 true 捕获 false 冒泡                        | Boolean | false            |
-| containerId          | 在 useWindow 属性为 false 的时候,自定义设置节点ID                        | String | ''            |
-| unloadMoreTxt          | “没有更多数”据展示文案                        | String | '哎呀,这里是底部了啦'            |
-| isOpenRefresh        | 是否开启下拉刷新                         | Boolean | false                |
-
-### Slot
-
-| name | 说明           | 
-|--------|----------------|
-| loading  | 自定义“加载中”的展示形式 | 
-| unloadMore  | 自定义“没有更多数据”的展示形式 | 
-| refreshLoading  | 自定义下拉刷新中“加载中”的展示形式 | 
+| has-more         | 是否还有更多数据               | Boolean | `true`                |
+| threshold         | 距离底部多远加载 | Number | `200`               |
+| use-window | 将滚动侦听器添加到 window 否则侦听组件的父节点     | Boolean | `true` |
+| use-capture          | 是否使用捕获模式 true 捕获 false 冒泡                        | Boolean | `false`            |
+| container-id          | 在 useWindow 属性为 false 的时候,自定义设置节点ID                        | String | `''`            |
+| load-more-txt          | “没有更多数”据展示文案                        | String | `'哎呀,这里是底部了啦'`            |
+| is-open-refresh        | 是否开启下拉刷新                         | Boolean | `false`                |
+| pull-icon        | 下拉刷新[图标名称](#/icon)                        | String | `https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png`                |
+| pull-txt        | 下拉刷新提示文案                         | String | `松手刷新`                |
+| load-icon        | 上拉加载[图标名称](#/icon)                       | Boolean | `https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png`                |
+| load-txt        | 上拉加载提示文案                         | String | `加载中...`                |
 
 ### Events
 
 | 事件名 | 说明           | 回调参数     |
 |--------|----------------|--------------|
-| loadMore  | 继续加载的回调函数 | done 函数,用于关闭加载中状态 |
-| scrollChange  | 实时监听滚动高度 | 滚动高度 |
+| load-more  | 继续加载的回调函数 | done 函数,用于关闭加载中状态 |
+| scroll-change  | 实时监听滚动高度 | 滚动高度 |
 | refresh  | 下拉刷新事件回调 | done 函数,用于关闭加载中状态 |
     

+ 4 - 4
src/packages/infiniteloading/index.scss

@@ -20,7 +20,7 @@
       }
       .top-text {
         font-size: 10px;
-        color: rgba(128, 128, 128, 1);
+        color: $text-color;
       }
     }
   }
@@ -28,8 +28,8 @@
     display: block;
     width: 100%;
     padding-top: 16px;
-    font-size: 12px;
-    color: rgba(200, 200, 200, 1);
+    font-size: $font-size-small;
+    color: $infinite-bottom-color;
     text-align: center;
 
     .bottom-box {
@@ -40,7 +40,7 @@
       }
       .bottom-text {
         font-size: 10px;
-        color: rgba(128, 128, 128, 1);
+        color: $text-color;
       }
     }
   }

+ 109 - 135
src/packages/infiniteloading/index.vue

@@ -1,49 +1,37 @@
 <template>
   <view-block
-    class="nut-infiniteloading"
+    :class="classes"
     ref="scroller"
     @touchstart="touchStart"
     @touchmove="touchMove"
     @touchend="touchEnd"
   >
     <view-block class="nut-infinite-top" ref="refreshTop" :style="getStyle">
-      <view-block class="top-box" v-if="!slotRefreshLoading">
-        <nut-icon
-          class="top-img"
-          name="https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png"
-        ></nut-icon>
-        <view-block class="top-text">松开刷新</view-block>
+      <view-block class="top-box">
+        <nut-icon class="top-img" :name="pullIcon"></nut-icon>
+        <view-block class="top-text">{{ pullTxt }}</view-block>
       </view-block>
-
-      <slot name="refreshLoading" v-else></slot>
     </view-block>
 
-    <view-block class="nut-infinite-container"><slot></slot></view-block>
+    <view-block class="nut-infinite-container">
+      <slot></slot>
+    </view-block>
 
     <view-block class="nut-infinite-bottom">
       <template v-if="isInfiniting">
-        <view-block v-if="!slotLoading" class="bottom-box">
-          <nut-icon
-            class="bottom-img"
-            name="https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png"
-          ></nut-icon>
-          <view-block class="bottom-text">加载中···</view-block>
+        <view-block class="bottom-box">
+          <nut-icon class="bottom-img" :name="loadIcon"></nut-icon>
+          <view-block class="bottom-text">{{ loadTxt }}</view-block>
         </view-block>
-
-        <slot name="loading" v-else></slot>
       </template>
       <template v-else-if="!hasMore">
-        <view-block class="tips" v-if="!slotUnloadMore">{{
-          unloadMoreTxt
-        }}</view-block>
-        <slot name="unloadMore" v-else></slot>
+        <view-block class="tips">{{ loadMoreTxt }}</view-block>
       </template>
     </view-block>
   </view-block>
 </template>
 <script lang="ts">
 import {
-  ref,
   toRefs,
   onMounted,
   onUnmounted,
@@ -63,7 +51,25 @@ export default create({
       type: Number,
       default: 200
     },
-    unloadMoreTxt: {
+    pullIcon: {
+      type: String,
+      default:
+        'https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png'
+    },
+    pullTxt: {
+      type: String,
+      default: '松开刷新'
+    },
+    loadIcon: {
+      type: String,
+      default:
+        'https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png'
+    },
+    loadTxt: {
+      type: String,
+      default: '加载中···'
+    },
+    loadMoreTxt: {
       type: String,
       default: '哎呀,这里是底部了啦'
     },
@@ -84,63 +90,43 @@ export default create({
       default: false
     }
   },
-  emits: ['scrollChange', 'loadMore', 'refresh'],
+  emits: ['scroll-change', 'load-more', 'refresh'],
 
   setup(props, { emit, slots }) {
-    console.log('componentName', componentName);
-
-    const {
-      hasMore,
-      threshold,
-      containerId,
-      useWindow,
-      useCapture,
-      isOpenRefresh
-    } = toRefs(props);
-
-    let scrollEl: Window | HTMLElement = window;
-    const scroller = ref<null | HTMLElement>(null);
-    const refreshTop = ref<null | HTMLElement>(null);
-    const beforeScrollTop = ref(0);
-    const isTouching = ref(false);
-    const isInfiniting = ref(false);
-    const refreshMaxH = ref(0);
-
-    const slot = reactive({
-      slotLoading: false,
-      slotUnloadMore: false,
-      slotRefreshLoading: false
-    });
-
-    const pageStart = reactive({
+    const state = reactive({
+      scrollEl: window as Window | HTMLElement | (Node & ParentNode),
+      scroller: null as null | HTMLElement,
+      refreshTop: null as null | HTMLElement,
+      beforeScrollTop: 0,
+      isTouching: false,
+      isInfiniting: false,
+      refreshMaxH: 0,
       y: 0,
       x: 0,
       distance: 0
     });
 
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true
+      };
+    });
+
     const getStyle = computed(() => {
       const style: CSSProperties = {};
-
-      if (pageStart.distance < 0) {
-        style.height = 0 + 'px';
-      } else {
-        style.height = pageStart.distance + 'px';
-      }
-
-      if (isTouching.value) {
-        style.transition = `height 0s cubic-bezier(0.25,0.1,0.25,1)`;
-      } else {
-        style.transition = `height 0.2s cubic-bezier(0.25,0.1,0.25,1)`;
-      }
-      return style;
+      return {
+        height: state.distance < 0 ? `0px` : `${state.distance}px`,
+        transition: state.isTouching
+          ? `height 0s cubic-bezier(0.25,0.1,0.25,1)`
+          : `height 0.2s cubic-bezier(0.25,0.1,0.25,1)`
+      };
     });
 
-    /** 获取监听自定义滚动节点 */
-    const getParentElement = el => {
-      if (containerId.value != '') {
-        return document.querySelector(`#${containerId.value}`);
-      }
-      return el && el.parentNode;
+    const getParentElement = (el: HTMLElement) => {
+      return !!props.containerId
+        ? document.querySelector(`#${props.containerId}`)
+        : el && el.parentNode;
     };
 
     const requestAniFrame = () => {
@@ -152,7 +138,7 @@ export default create({
         }
       );
     };
-    /** 获取滚动条高度 */
+
     const getWindowScrollTop = () => {
       return window.pageYOffset !== undefined
         ? window.pageYOffset
@@ -163,24 +149,22 @@ export default create({
           ).scrollTop;
     };
 
-    const calculateTopPosition = el => {
-      if (!el) {
-        return 0;
-      }
-      return el.offsetTop + calculateTopPosition(el.offsetParent);
+    const calculateTopPosition = (el: HTMLElement): number => {
+      return !el
+        ? 0
+        : el.offsetTop + calculateTopPosition(el.offsetParent as HTMLElement);
     };
 
-    /** 判断是否滚动到底部 */
     const isScrollAtBottom = () => {
       let offsetDistance = 0;
       let resScrollTop = 0;
-      let direction = 'down'; // 滚动的方向
+      let direction = 'down';
       const windowScrollTop = getWindowScrollTop();
-      if (useWindow.value) {
-        if (scroller.value) {
+      if (props.useWindow) {
+        if (state.scroller) {
           offsetDistance =
-            calculateTopPosition(scroller.value) +
-            scroller.value.offsetHeight -
+            calculateTopPosition(state.scroller) +
+            state.scroller.offsetHeight -
             windowScrollTop -
             window.innerHeight;
         }
@@ -190,119 +174,109 @@ export default create({
           scrollHeight,
           clientHeight,
           scrollTop
-        } = scrollEl as HTMLElement;
+        } = state.scrollEl as HTMLElement;
 
         offsetDistance = scrollHeight - clientHeight - scrollTop;
         resScrollTop = scrollTop;
       }
 
-      if (beforeScrollTop.value > resScrollTop) {
+      if (state.beforeScrollTop > resScrollTop) {
         direction = 'up';
       } else {
         direction = 'down';
       }
 
-      beforeScrollTop.value = resScrollTop;
+      state.beforeScrollTop = resScrollTop;
 
-      emit('scrollChange', resScrollTop);
+      emit('scroll-change', resScrollTop);
 
-      return offsetDistance <= threshold.value && direction == 'down';
+      return offsetDistance <= props.threshold && direction == 'down';
     };
 
     const infiniteDone = () => {
-      isInfiniting.value = false;
+      state.isInfiniting = false;
     };
-    /** 滚动函数 */
+
     const handleScroll = () => {
       requestAniFrame()(() => {
-        if (!isScrollAtBottom() || !hasMore.value || isInfiniting.value) {
+        if (!isScrollAtBottom() || !props.hasMore || state.isInfiniting) {
           return false;
         } else {
-          console.log('无限加载更多');
-          isInfiniting.value = true;
-          emit('loadMore', infiniteDone);
+          state.isInfiniting = true;
+          emit('load-more', infiniteDone);
         }
       });
     };
 
-    /** 滚动监听 */
     const scrollListener = () => {
-      scrollEl.addEventListener('scroll', handleScroll, useCapture.value);
+      state.scrollEl.addEventListener('scroll', handleScroll, props.useCapture);
     };
 
-    /** 下拉加载完成回到初始状态 */
     const refreshDone = () => {
-      pageStart.distance = 0;
-      isTouching.value = false;
+      state.distance = 0;
+      state.isTouching = false;
     };
 
-    const touchStart = event => {
+    const touchStart = (event: TouchEvent) => {
       if (
-        beforeScrollTop.value == 0 &&
-        !isTouching.value &&
-        isOpenRefresh.value
+        state.beforeScrollTop == 0 &&
+        !state.isTouching &&
+        props.isOpenRefresh
       ) {
-        pageStart.y = event.touches[0].pageY;
-        isTouching.value = true;
+        state.y = event.touches[0].pageY;
+        state.isTouching = true;
 
-        const childHeight = ((refreshTop.value as HTMLElement)
+        const childHeight = ((state.refreshTop as HTMLElement)
           .firstElementChild as HTMLElement).offsetHeight;
-        refreshMaxH.value = Math.floor(childHeight * 1 + 10);
+        state.refreshMaxH = Math.floor(childHeight * 1 + 10);
       }
     };
 
-    const touchMove = event => {
-      pageStart.distance = event.touches[0].pageY - pageStart.y;
+    const touchMove = (event: TouchEvent) => {
+      state.distance = event.touches[0].pageY - state.y;
 
-      if (pageStart.distance > 0 && isTouching.value) {
+      if (state.distance > 0 && state.isTouching) {
         event.preventDefault();
-        if (pageStart.distance >= refreshMaxH.value)
-          pageStart.distance = refreshMaxH.value;
+        if (state.distance >= state.refreshMaxH)
+          state.distance = state.refreshMaxH;
       } else {
-        pageStart.distance = 0;
-        isTouching.value = false;
+        state.distance = 0;
+        state.isTouching = false;
       }
     };
+
     const touchEnd = () => {
-      if (pageStart.distance < refreshMaxH.value) {
-        pageStart.distance = 0;
+      if (state.distance < state.refreshMaxH) {
+        state.distance = 0;
       } else {
         emit('refresh', refreshDone);
       }
     };
 
-    /** 生命周期 首次加载 */
     onMounted(() => {
-      const parentElement = getParentElement(scroller);
-
-      let scrollElCopy = window;
-      if (useWindow.value === false) {
-        scrollElCopy = parentElement;
-      }
-
-      scrollEl = scrollElCopy;
+      const parentElement = getParentElement(
+        state.scroller as HTMLElement
+      ) as Node & ParentNode;
+      state.scrollEl = props.useWindow ? window : parentElement;
 
       scrollListener();
-
-      slot.slotUnloadMore = slots.unloadMore ? true : false;
-      slot.slotLoading = slots.loading ? true : false;
-      slot.slotRefreshLoading = slots.refreshLoading ? true : false;
     });
 
-    /** 移除监听 */
     onUnmounted(() => {
-      scrollEl.removeEventListener('scroll', handleScroll, useCapture.value);
+      state.scrollEl.removeEventListener(
+        'scroll',
+        handleScroll,
+        props.useCapture
+      );
     });
 
     return {
-      scroller,
-      refreshTop,
+      classes,
+      ...toRefs(state),
       touchStart,
       touchMove,
       touchEnd,
-      getStyle,
-      isInfiniting,
-      ...toRefs(slot)
+      getStyle
     };
   }
 });

+ 4 - 0
src/styles/variables.scss

@@ -215,6 +215,10 @@ $notify-warning-background-color: linear-gradient(
 
 $tabbar-active-color: $primary-color;
 
+//infiniteloading
+
+$infinite-bottom-color: #c8c8c8;
+
 view-block {
   display: block;
 }