浏览代码

新增无限加载和滚动组件

宋成林 6 年之前
父节点
当前提交
cc849dee88

+ 11 - 1
src/config.json

@@ -398,7 +398,7 @@
       "desc": "滚动组件",
       "type": "component",
       "sort": "1",
-      "showDemo": false,
+      "showDemo": true,
       "author": "iris"
     },
     {
@@ -420,6 +420,16 @@
       "sort": "0",
       "showDemo": true,
       "author": "famanoder"
+    },
+    {
+      "version": "1.0.0",
+      "name": "InfiniteLoading",
+      "chnName": "无限加载",
+      "desc": "无限加载",
+      "type": "component",
+      "sort": "1",
+      "showDemo": true,
+      "author": "iris"
     }
   ]
 }

+ 59 - 0
src/packages/infiniteloading/demo.vue

@@ -0,0 +1,59 @@
+<template>
+    <div>
+        <nut-infiniteloading @loadmore="onInfinite" :is-show-mod="true" :has-more="isHasMore" :is-loading="isLoading"
+           :threshold="200">
+            <ul class="list" >
+                <li class="list-item" v-for="(item, index) of data" :key="item">我是测试数据{{index + 1}}</li>
+            </ul>
+        </nut-infiniteloading>
+    </div>
+</template>
+
+<script>
+export default {
+    data() {
+        return {
+            data: new Array(30),
+            page: 2,
+            num: 30,
+            isHasMore: true,
+            isLoading: false,
+            isErr: false,
+            timer: null
+        };
+    },
+    methods: {
+        onInfinite () {
+            this.isLoading = true;
+            this.timer = setTimeout(() => {
+                if (this.page <= 5) {
+                    this.data = new Array(this.num * this.page);
+                    this.page = this.page + 1;
+                } else {
+                    this.isHasMore = false;
+                }
+                this.isLoading = false;
+            }, 100);
+        }
+    },
+    destroyed() {
+        clearTimeout(this.timer);
+    }
+};
+</script>
+
+<style lang="scss" scoped>
+.list{
+    padding-top: 10px; 
+    .list-item{
+        height: 50px;
+        border: 1px solid mix($primary-color, #FFF, 40%);
+        margin-bottom: 10px;
+        font-size: 12px;
+        color: mix($primary-color, #FFF, 80%);
+        line-height: 50px;
+        text-align: center;
+        background-color: #fff;
+    }
+}
+</style>

+ 58 - 0
src/packages/infiniteloading/doc.md

@@ -0,0 +1,58 @@
+# InfiniteLoading 无限加载
+
+## 基本用法
+
+```html
+<nut-infiniteloading @loadmore="onInfinite" :is-show-mod="true" :has-more="isHasMore" :is-loading="isLoading" :threshold="200">
+    <ul class="list" >
+        <li class="list-item" v-for="(item, index) of data" :key="item">我是测试数据{{index + 1}}</li>
+    </ul>
+</nut-infiniteloading>
+```
+```javascript
+export default {
+    data() {
+        return {
+            data: new Array(30),
+            page: 2,
+            num: 30,
+            isHasMore: true,
+            isLoading: false,
+            isErr: false,
+            timer: null
+        };
+    },
+    methods: {
+        onInfinite () {
+            this.isLoading = true;
+            this.timer = setTimeout(() => {
+                if (this.page <= 5) {
+                    this.data = new Array(this.num * this.page);
+                    this.page = this.page + 1;
+                } else {
+                    this.isHasMore = false;
+                }
+                this.isLoading = false;
+            }, 100);
+        }
+    },
+    destroyed() {
+        clearTimeout(this.timer);
+    }
+};
+```
+
+## Prop
+
+| 字段 | 说明 | 类型 | 默认值
+|----- | ----- | ----- | -----
+| hasMore | 是否还有更多数据 | Boolean | true
+| isLoading | 是否加载中 | Boolean | false
+| threshold | 距离底部多远加载 | Number | 200
+| isShowMod | 是否展示懒加载模块内容,一般适用于选项卡切换 | Boolean | false
+
+## Event
+
+| 字段 | 说明 | 回调参数
+|----- | ----- | -----
+| loadmore | 继续加载的回调函数 | -

+ 8 - 0
src/packages/infiniteloading/index.js

@@ -0,0 +1,8 @@
+import InfiniteLoading from './infiniteloading.vue';
+import './infiniteloading.scss';
+
+InfiniteLoading.install = function(Vue) {
+  Vue.component(InfiniteLoading.name, InfiniteLoading);
+};
+
+export default InfiniteLoading

文件差异内容过多而无法显示
+ 49 - 0
src/packages/infiniteloading/infiniteloading.scss


+ 144 - 0
src/packages/infiniteloading/infiniteloading.vue

@@ -0,0 +1,144 @@
+<template>
+    <div class="nut-infiniteloading" 
+        ref="scroller" 
+        @touchstart="touchStartHandle($event)" 
+        @touchmove="touchMoveHandle($event)"
+    >
+        <slot></slot>
+        <div class="load-more">
+            <div class="bottom-tips">
+                <template v-if="isLoading" >
+                   <i class="loading-hint"></i><span class="loading-txt">加载中...</span>
+                </template>
+                <span v-else-if="!hasMore" class="tips-txt">哎呀,这里是底部了啦</span>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+export default {
+    name:'nut-infiniteloading',
+    props: {
+        hasMore: {
+            type: Boolean,
+            default: true
+        },
+        isLoading: {
+            type: Boolean,
+            default: false
+        },
+        threshold: {
+            type: Number,
+            default: 200
+        },
+        useWindow: {
+            type: Boolean,
+            default: true
+        },
+        isShowMod: {
+            type: Boolean,
+            default: false
+        }
+    },
+    data() {
+        return {
+            startX: 0,
+            startY: 0,
+            diffX: 0,
+            diffY: 0,
+            beforeScrollTop: 0
+        }
+    },
+
+    mounted: function () {
+        this.scrollListener();
+    },
+
+    methods: {
+        touchStartHandle(e) {
+            try {
+                this.startX = Number(e.changedTouches[0].pageX);
+                this.startY = Number(e.changedTouches[0].pageY);
+            } catch (e) {
+                console.log(e.message);
+            }
+        },
+        touchMoveHandle(e) {
+            let endX = Number(e.changedTouches[0].pageX);
+            let endY = Number(e.changedTouches[0].pageY);
+            this.diffX = endX - this.startX;
+            this.diffY = endY - this.startY;
+        },
+
+        scrollListener() {
+            window.addEventListener('scroll', this.handleScroll, false);
+            window.addEventListener('resize', this.handleScroll, false);
+        },
+
+        requestAniFrame() {
+            return (
+                window.requestAnimationFrame ||
+                window.webkitRequestAnimationFrame ||
+                window.mozRequestAnimationFrame ||
+                function(callback) {
+                    window.setTimeout(callback, 1000 / 60);
+                }
+            );
+        },
+
+        handleScroll() {
+            this.requestAniFrame()(() => {
+                if (!this.hasMore || !this.isScrollAtBottom() || this.isLoading || !this.isShowMod) {
+                    return false;
+                } else {
+                    this.$emit('loadmore');
+                }
+            })
+        },
+
+        calculateTopPosition(el) {
+            if (!el) {
+                return 0;
+            }
+            return el.offsetTop + this.calculateTopPosition(el.offsetParent);
+        },
+
+        getWindowScrollTop() {
+            return window.pageYOffset !== undefined ?
+                window.pageYOffset :
+                (document.documentElement || document.body.parentNode || document.body)
+                .scrollTop;
+        },
+
+        isScrollAtBottom() {
+            let offsetDistance;
+            
+            const windowScrollTop = this.getWindowScrollTop();
+            if (this.useWindow) {
+                offsetDistance =
+                    this.calculateTopPosition(this.$refs.scroller) +
+                    this.$refs.scroller.offsetHeight -
+                    windowScrollTop - window.innerHeight;
+            } else {
+                const {
+                    scrollHeight,
+                    clientHeight,
+                    scrollTop
+                } = this.$refs.scroller;
+                offsetDistance = scrollHeight - clientHeight - scrollTop;
+            }
+            
+            // 保证是往下滑动的
+            let beforeScrollTop = this.beforeScrollTop;
+            this.beforeScrollTop = windowScrollTop;
+
+            return (offsetDistance <= this.threshold && windowScrollTop >= this.beforeScrollTop) ;
+        }
+    },
+
+    destroyed() {
+        window.removeEventListener('scroll', this.handleScroll, false);
+        window.removeEventListener('resize', this.handleScroll, false);
+    }
+}
+</script>

+ 42 - 92
src/packages/scroller/demo.vue

@@ -6,44 +6,29 @@
         >此 Demo 在 PC 端浏览器与移动端浏览器体验差异较大,建议在 Android 或 iOS 设备上体验。</nut-noticebar>
         <h4>横向用法</h4>
         <div class="hor-panel">
-            <nut-scroller :list-data="listData"  
-                :is-un-more="isUnMore" 
-                :is-loading="isLoading"
-                @loadMore="loadMoreHor"
-                @jump="jump()"
-            >
+            <nut-scroller @jump="jump()">
                 <div slot="list" class="nut-hor-list-item" v-for="(item, index) of listData" :key="index">
-                    <div class="nut-scroller-item-img">
-                        <img src="" alt="" data-src="//m.360buyimg.com/babel/s452x452_jfs/t17869/89/1529579560/27020/8b71651a/5aced5aeN993e3538.jpg">
-                    </div>
                     <dl class="nut-scroller-item-info">
                         <dt>防水升级版 蓝 迷你小音</dt>
                         <dd>2018-02-25</dd>
                     </dl>
                 </div>
-                <slot slot="more"><div class="nut-hor-jump-more">释放查看更多</div></slot>
+                <slot slot="more"><div class="nut-hor-jump-more">查看更多</div></slot>
             </nut-scroller>
         </div>
         <h4>横向小于一屏</h4>
         <div class="hor-panel">
-            <nut-scroller :list-data="listData3"  
-                :is-un-more="isUnMore3" 
-                :is-loading="isLoading3"
-                @loadMore="loadMoreHor3"
-            >
+            <nut-scroller @jump="jump()">
                 <div slot="list" class="nut-hor-list-item" v-for="(item, index) of listData3" :key="index">
-                    <div class="nut-scroller-item-img">
-                        <img src="" alt="" data-src="//m.360buyimg.com/babel/s452x452_jfs/t17869/89/1529579560/27020/8b71651a/5aced5aeN993e3538.jpg">
-                    </div>
                     <dl class="nut-scroller-item-info">
                         <dt>防水升级版 蓝牙音箱 小音</dt>
                         <dd>2018-02-25</dd>
                     </dl>
                 </div>
-                <slot slot="more"><div class="nut-hor-jump-more">释放查看更多</div></slot>
+                <slot slot="more" ><div class="nut-hor-jump-more">查看更多</div></slot>
             </nut-scroller>
         </div>
-        <h4>向用法</h4>
+        <h4>向用法</h4>
         <div class="vert-panel">
             <nut-scroller
                 :is-un-more="isUnMore1" 
@@ -54,18 +39,15 @@
             > 
                 <div slot="list" class="nut-vert-list-panel">
                     <div class="nut-vert-list-item" v-for="(item, index) of listData1" :key="index">
-                        <div class="nut-scroller-item-img">
-                            <img src="" alt="" data-src="//m.360buyimg.com/babel/s452x452_jfs/t17869/89/1529579560/27020/8b71651a/5aced5aeN993e3538.jpg">
-                        </div>
                         <dl class="nut-scroller-item-info">
-                            <dt>防水升级版 蓝牙音箱 低音炮 IPX7设计 户外便携音响 迷你小音</dt>
+                            <dt>{{index}}防水升级版 蓝牙音箱 低音炮 IPX7设计 户外便携音响 迷你小音</dt>
                             <dd>2018-02-25</dd>
                         </dl>
                     </div>
                 </div>
             </nut-scroller>
         </div>
-        <h4>向不满一屏用法</h4>
+        <h4>向不满一屏用法</h4>
         <div class="vert-panel">
             <nut-scroller
                 :is-un-more="isUnMore2" 
@@ -80,7 +62,7 @@
                             <img src="" alt="" data-src="" class="load-err">
                         </div>
                         <dl class="nut-scroller-item-info">
-                            <dt>防水升级版 蓝牙音箱 低音炮 IPX7设计 户外便携音响 迷你小音</dt>
+                            <dt>{{index}}防水升级版 蓝牙音箱 低音炮 IPX7设计 户外便携音响 迷你小音</dt>
                             <dd>2018-02-25</dd>
                         </dl>
                     </div>
@@ -94,15 +76,11 @@
 export default {
     data() {
         return {
-            listData: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
-            listData1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+            listData: new Array(20),
+            listData1: new Array(20),
             listData2: [1, 2],
             listData3: [1],
-            isUnMore: false,
-            isLoading: false,
-            maxPages: 3,
-            page: 2,
-            timers: null,
+            maxPages: 4,
             isUnMore1: false,
             isLoading1: false,
             page1: 2,
@@ -110,47 +88,12 @@ export default {
             isUnMore2: false,
             isLoading2: false,
             page2: 2,
-            isUnMore3: false,
-            isLoading3: false,
-            page3: 2
+            timers: null,
         };
     },
 
     methods: {
-        loadMoreHor() {
-            this.isLoading = true;
-            if (this.page > this.maxPages) {
-                this.isUnMore = true;
-                this.isLoading = false;
-            } else {
-                clearTimeout(this.timers);
-                this.timer = setTimeout(() => {
-                    this.page = ++this.page;
-                    this.listData = [...this.listData, ...[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]];
-                    console.log(this.listData);
-                    this.isLoading = false;
-                }, 300);
-            }
-        },
-
-        loadMoreHor3() {
-            this.isLoading3 = true;
-            if (this.page3 > this.maxPages2) {
-                this.isUnMor3 = true;
-                this.isLoading3 = false;
-            } else {
-                clearTimeout(this.timers);
-                this.timer = setTimeout(() => {
-                    this.page3 = ++this.page3;
-                    this.listData3 = [...this.listData3, ...[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]];
-                    console.log(this.listData3);
-                    this.isLoading3 = false;
-                }, 300);
-            }
-        },
-
         jump() {
-            console.log('跳转');
             location.href = 'http://www.jd.com';
         },
 
@@ -162,12 +105,11 @@ export default {
             } else {
                 clearTimeout(this.timers);
                 this.timer = setTimeout(() => {
-                    this.page1 = ++this.page1;
                     this.isLoading1 = false;
-                    this.listData1 = [...this.listData1, ...[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]];
-                    console.log(this.listData1);
-                    
-                }, 2000);
+                    this.isUnMore1 = false;
+                    this.listData1 = new Array(20 * this.page1);
+                    this.page1 = ++this.page1;
+                }, 300);
             }
         },
 
@@ -175,12 +117,11 @@ export default {
             this.isLoading1 = true;
             clearTimeout(this.timers);
             this.timer = setTimeout(() => {
-                this.page1 = 2;
                 this.isLoading1 = false;
                 this.isUnMore1 = false;
-                this.listData1 = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
-                
-            }, 2000);
+                this.listData1 = new Array(20);
+                this.page1 = 2;
+            }, 300);
         },
 
 
@@ -192,11 +133,11 @@ export default {
             } else {
                 clearTimeout(this.timers);
                 this.timer = setTimeout(() => {
-                    this.page2 = ++this.page1;
                     this.isLoading2 = false;
-                    this.listData2 = [...this.listData1, ...[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]];
-                    console.log(this.listData1);
-                }, 2000);
+                     this.isUnMore2 = false;
+                    this.listData2 = new Array(20 * this.page2);
+                    this.page2 = ++this.page1;
+                }, 300);
             }
         },
 
@@ -204,11 +145,11 @@ export default {
             this.isLoading2 = true;
             clearTimeout(this.timers);
             this.timer = setTimeout(() => {
-                this.page2 = 2;
                 this.isLoading2 = false;
                 this.isUnMore2 = false;
-                this.listData2 = [11, 12, 13, 14, 15, 16];
-            }, 2000);
+                this.listData2 = new Array(10);
+                this.page2 = 2;
+            }, 300);
         }
     },
     
@@ -221,7 +162,8 @@ export default {
 <style lang="scss" scoped>
 .hor-panel{
     height: 80px;
-    background-color: mix($primary-color, #FFF, 10%);
+    background-color: #e0e0e0;
+    display: flex;
 }
 .nut-hor-list{
     .nut-hor-list-item{
@@ -233,18 +175,22 @@ export default {
         height: 60px;
         padding: 10px;
         margin-left: 10px;
-        background-color: mix($primary-color, #FFF, 90%);
-        color: #FFF;
+        background-color: #FFF;
+        color: #000;
         line-height: 100px; 
     }
 }
 .nut-hor-jump-more{
+    display: flex;
+    align-items: center;
     height: 100%;
     width: 20px;
-    padding: 5px 10px;
+    padding: 0 5px;
+    margin-left: 10px;
     font-size: 12px;
     text-align: center;
     color: $primary-color;
+    background-color: #fff;
 }
 .nut-scroller-item-img{
     height: 58px;
@@ -271,7 +217,7 @@ dl, dt, dd{
 .vert-panel{
     height: 400px; 
     padding: 10px; 
-    background-color: mix($primary-color, #FFF, 10%);
+    background-color: #e1e1e1;
 }
 .nut-vert-list-panel{
     .nut-vert-list-item{
@@ -279,8 +225,12 @@ dl, dt, dd{
         height: 60px;
         padding: 10px;
         margin-bottom: 10px;
-        background-color: mix($primary-color, #FFF, 90%);
-        color: #FFF;
+        //background-color: mix($primary-color, #FFF, 90%);
+        background-color: #FFF;
+        color: #000;
     }
 }
 </style>
+
+
+ 

+ 182 - 0
src/packages/scroller/doc.md

@@ -0,0 +1,182 @@
+# Scroller 滚动
+
+## 纵向下拉刷新上拉加载,横向滚动末尾跳转
+## 基本用法
+
+横向用法
+
+```html
+<nut-scroller @jump="jump()">
+    <div slot="list" class="nut-hor-list-item" v-for="(item, index) of listData" :key="index">
+        <dl class="nut-scroller-item-info">
+            <dt>防水升级版 蓝 迷你小音</dt>
+            <dd>2018-02-25</dd>
+        </dl>
+    </div>
+    <slot slot="more"><div class="nut-hor-jump-more">查看更多</div></slot>
+</nut-scroller>
+```
+
+横向小于一屏
+```html
+<div class="hor-panel">
+    <nut-scroller @jump="jump()">
+        <div slot="list" class="nut-hor-list-item" v-for="(item, index) of listData3" :key="index">
+            <dl class="nut-scroller-item-info">
+                <dt>防水升级版 蓝牙音箱 小音</dt>
+                <dd>2018-02-25</dd>
+            </dl>
+        </div>
+        <slot slot="more" ><div class="nut-hor-jump-more">查看更多</div></slot>
+    </nut-scroller>
+</div>
+```
+纵向用法
+
+```html
+<nut-scroller
+    :is-un-more="isUnMore1" 
+    :is-loading="isLoading1"
+    :type="'vertical'"
+    @loadMore="loadMoreVert"
+    @pulldown="pulldown"
+> 
+    <div slot="list" class="nut-vert-list-panel">
+        <div class="nut-vert-list-item" v-for="(item, index) of listData1" :key="index">
+            <dl class="nut-scroller-item-info">
+                <dt>{{index}}防水升级版 蓝牙音箱 低音炮 IPX7设计 户外便携音响 迷你小音</dt>
+                <dd>2018-02-25</dd>
+            </dl>
+        </div>
+    </div>
+</nut-scroller>
+```
+
+纵向不满一屏用法
+
+```html
+<nut-scroller
+    :is-un-more="isUnMore2" 
+    :is-loading="isLoading2"
+    :type="'vertical'"
+    @loadMore="loadMoreVert2"
+    @pulldown="pulldown2"
+> 
+    <div slot="list" class="nut-vert-list-panel">
+        <div class="nut-vert-list-item" v-for="(item, index) of listData2" :key="index">
+            <dl class="nut-scroller-item-info">
+                <dt>{{index}}防水升级版 蓝牙音箱 低音炮 IPX7设计 户外便携音响 迷你小音</dt>
+                <dd>2018-02-25</dd>
+            </dl>
+        </div>
+    </div>
+</nut-scroller>
+```
+
+```javascript
+export default {
+    data() {
+        return {
+            listData: new Array(20),
+            listData1: new Array(20),
+            listData2: [1, 2],
+            listData3: [1],
+            maxPages: 4,
+            isUnMore1: false,
+            isLoading1: false,
+            page1: 2,
+            maxPages2: 1,
+            isUnMore2: false,
+            isLoading2: false,
+            page2: 2,
+            timers: null,
+        };
+    },
+
+    methods: {
+        jump() {
+            location.href = 'http://www.jd.com';
+        },
+
+        loadMoreVert() {
+            this.isLoading1 = true;
+            if (this.page1 > this.maxPages) {
+                this.isUnMore1 = true;
+                this.isLoading1 = false;
+            } else {
+                clearTimeout(this.timers);
+                this.timer = setTimeout(() => {
+                    this.isLoading1 = false;
+                    this.isUnMore1 = false;
+                    this.listData1 = new Array(20 * this.page1);
+                    this.page1 = ++this.page1;
+                }, 300);
+            }
+        },
+
+        pulldown() {
+            this.isLoading1 = true;
+            clearTimeout(this.timers);
+            this.timer = setTimeout(() => {
+                this.isLoading1 = false;
+                this.isUnMore1 = false;
+                this.listData1 = new Array(20);
+                this.page1 = 2;
+            }, 300);
+        },
+
+
+        loadMoreVert2() {
+            this.isLoading2 = true;
+            if (this.page2 > this.maxPages2) {
+                this.isUnMore2 = true;
+                this.isLoading2 = false;
+            } else {
+                clearTimeout(this.timers);
+                this.timer = setTimeout(() => {
+                    this.isLoading2 = false;
+                     this.isUnMore2 = false;
+                    this.listData2 = new Array(20 * this.page2);
+                    this.page2 = ++this.page1;
+                }, 300);
+            }
+        },
+
+        pulldown2() {
+            this.isLoading2 = true;
+            clearTimeout(this.timers);
+            this.timer = setTimeout(() => {
+                this.isLoading2 = false;
+                this.isUnMore2 = false;
+                this.listData2 = new Array(10);
+                this.page2 = 2;
+            }, 300);
+        }
+    },
+    
+    destroyed() {
+        clearTimeout(this.timer);
+    }
+};
+```
+
+## Prop
+
+| 字段 | 说明 | 类型 | 默认值
+|----- | ----- | ----- | ----- 
+| type | 类型,'horizontal'横向或'vertical'纵向 | String | 'horizontal'
+| stretch | 拉伸范围 | Number | 100
+| isUnMore | 是否没有更多 | Boolean | false
+| isLoading | 是否正在加载 | Boolean | false
+| threshold | 距离底部多少开始加载 | Number | 100
+| pulldownTxt | 下拉展示文案 | String | '下拉刷新'
+| loadMoreTxt | 上拉展示文案 | String | '上拉加载'
+| unloadMoreTxt | 没有更多数据展示文案 | String | '没有更多了'
+
+## Event
+
+| 字段 | 说明 | 回调参数 
+|----- | ----- | ----- 
+| pulldown | 下拉刷新回调 | -
+| loadMore | 上拉加载回调 | -
+| jump | 查看更多跳转回调 | -

+ 58 - 85
src/packages/scroller/horizontal-scroll.vue

@@ -2,9 +2,10 @@
     <div class="nut-hor-scroll" rel="wrapper">
         <div class="nut-hor-list" ref="list">
             <slot name="list"></slot>
-            <div class="nut-hor-control" v-if="isUnMore && $slots.more && isShowLoadMore">
+	         <div class="nut-hor-control" v-if="$slots.more && isShowLoadMore">
                 <slot name="more"></slot>
             </div>
+            <slot name="arrow" v-if="$slots.arrow"></slot>
         </div>
     </div>
 </template>
@@ -12,26 +13,9 @@
 export default {
     name:'nut-hor-scroll',
     props: {
-        listData: {
-            type: Array,
-            required: true,
-            default: () => []
-        },
-        lineSpacing: {
-            type: Number,
-            default: 210
-        },
         stretch: {
             type: Number,
-            default: 200
-        },
-        isUnMore: {
-            type: Boolean,
-            default: false
-        },
-        isLoading: {
-            type: Boolean,
-            default: false
+            default: 40
         }
     },
     data() {
@@ -39,68 +23,45 @@ export default {
             touchParams: {
                 startX: 0, 
                 endX: 0, 
+                startY: 0, 
+                endY: 0,
                 startTime: 0, 
                 endTime: 0
             },
             transformX: 0,
             scrollDistance: 0,
             timer: null,
-            isShowLoadMore: false
+            isShowLoadMore: false,
+            isFirstShow: false
         }
     },
     methods: {
-        requestAnimationFrame(){
-            return window.requestAnimationFrame ||
-                window.webkitRequestAnimationFrame || 
-                function(callback){
-                    window.setTimeout(callback, 1000 / 60);
-                }
-        },
-
-        loazyLoadImg() {
-            let imgLazyLoadEl;
-            this.requestAnimationFrame()(() => {
-                imgLazyLoadEl = this.$refs.list.querySelectorAll('img');
-                imgLazyLoadEl.forEach((item, index) => {
-                    if (!item.getAttribute('src')) {
-                        let src = item.getAttribute('data-src');
-                        let img = new Image();
-                        img.src = src;
-                        console.log(src);
-                        img.onload = ()=>{
-                            item.src = src;
-                        }
-                        img.onerror = ()=>{
-                            item.classList.add('load-err');
-                        }
-                    }
-                });
-            })
-        },
-
         isShow() {
             let wrapH = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
-            let listH = this.listData.length * this.lineSpacing;
+            let listH = this.$refs.list.offsetWidth;
             if (wrapH <= listH) {
                 this.isShowLoadMore =  true;
             } else {
                 this.isShowLoadMore = false;
             }
         },
-        setTransform(translateX = 0, type, time = 500) {
+        
+        setTransform(translateX = 0, type, time = 500, unit = 'px') {
+            this.scrollDistance = translateX; 
+            translateX = translateX + unit;
             if (type === 'end') {
                 this.$refs.list.style.webkitTransition = `transform ${time}ms cubic-bezier(0.19, 1, 0.22, 1)`;
             } else {
                 this.$refs.list.style.webkitTransition = '';
             }
-            this.$refs.list.style.webkitTransform = `translate3d(${translateX}px, 0, 0)`;
-            this.scrollDistance = translateX;
+            this.$refs.list.style.webkitTransform = `translate3d(${translateX}, 0, 0)`;
+            
         },
 
         setMove(move, type, time) {
             let updateMove = move + this.transformX;
             let w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
-            let offsetWidth = this.lineSpacing * this.listData.length;
+            let offsetWidth = this.$refs.list.offsetWidth;
             if (type === 'end') {
                 if (updateMove > 0) {
                     updateMove = 0;
@@ -115,7 +76,7 @@ export default {
             } else {
                 let maxMove =  -offsetWidth + w;
                 if (updateMove > 0 && updateMove > this.stretch) {
-                    updateMove = this.stretc;
+                    updateMove = this.stretch;
                 } else if (updateMove < maxMove - this.stretch) {
                     if (maxMove <= 0) {
                         updateMove  = maxMove - this.stretch;
@@ -132,59 +93,71 @@ export default {
 
             let changedTouches = event.changedTouches[0];
             this.touchParams.startX = changedTouches.pageX;
+            this.touchParams.startY = changedTouches.pageY;
             this.touchParams.startTime = event.timestamp || Date.now();
             this.transformX = this.scrollDistance;
         },
 
-        touchMove(event) {
-
-            let changedTouches = event.changedTouches[0];
+        touchEvent(changedTouches, callback) {
             this.touchParams.lastX = changedTouches.pageX;
-            this.touchParams.lastTime = event.timestamp || Date.now();
+            this.touchParams.lastY = changedTouches.pageY;
+            let moveY = this.touchParams.lastY - this.touchParams.startY;
             let move = this.touchParams.lastX - this.touchParams.startX;
 
-            this.setMove(move);
+            if (!(Math.abs(move) > 20 && Math.abs(move) > Math.abs(moveY))) {
+                return false;
+            } else {
+                let w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
+                let maxMove = -this.$refs.list.offsetWidth + w;
+                callback && callback(move, maxMove, moveY);
+            }
         },
 
-        touchEnd(event) {
+        touchMove(event) {
             event.preventDefault();
-
             let changedTouches = event.changedTouches[0];
-            this.touchParams.lastX = changedTouches.pageX;
             this.touchParams.lastTime = event.timestamp || Date.now();
-            let move = this.touchParams.lastX - this.touchParams.startX;
+            let moveTime = this.touchParams.lastTime - this.touchParams.startTime;
+            this.touchEvent(changedTouches, (move, maxMove, moveY) => {
+                if (move > 0 && this.isFirstShow) {
+                    this.isFirstShow = false;
+                }
+                this.setMove(move);
+            });
+        },
 
+        touchEnd(event) {
+            let changedTouches = event.changedTouches[0];
+            this.touchParams.lastTime = event.timestamp || Date.now();
             let moveTime = this.touchParams.lastTime - this.touchParams.startTime;
-            let w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
-            let maxMove = -this.lineSpacing * this.listData.length + w;
+            this.touchEvent(changedTouches, (move, maxMove) => {
+                //if (moveTime <= 300) {
+                    move = move * 2;
+                //}
 
-            // 释放跳转之类
-            if (this.isUnMore && move < 0 && (move + this.transformX) < maxMove - 50) {
-                this.$emit('jump');
-            }
-            
-            // 加载更多
-            if (!this.isLoading && !this.isUnMore && move < 0 && (move + this.transformX) < maxMove + 2 * w) {
-                this.$emit('loadMore');
-            }
+                // 释放跳转之类
+                if (move < 0 && (move + this.transformX) < maxMove - 20 && this.isFirstShow) {
+                    this.$emit('jump');
+                }
 
-            if (moveTime <= 300) {
-                move = move * 2;
-                if (move < 0 && move + this.transformX < maxMove) {
-                    move = maxMove - this.transformX;
+                if (!this.isFirstShow && move < 0 && move + this.transformX < maxMove && this.$slots.more) {
+                    this.isFirstShow = true;
+                    //move = maxMove - this.transformX;
                 }
-                moveTime = moveTime + 500;
-                this.setMove(move, 'end', moveTime);
-            } else {
-                this.setMove(move, 'end');
-            }
+
+                if (moveTime <= 300) {
+                    moveTime = moveTime + 500;
+                    this.setMove(move, 'end', moveTime);
+                } else {
+                    this.setMove(move, 'end');
+                }
+            });
         }
     },
 
     mounted() {
         this.$nextTick(() => {
             this.isShow();
-            // this.loazyLoadImg();
             // 监听
             this.$el.addEventListener('touchstart', this.touchStart);
             this.$el.addEventListener('touchmove', this.touchMove);

+ 2 - 17
src/packages/scroller/scroller.scss

@@ -1,28 +1,13 @@
 @import "../../styles/animation/rotate";
-
 .nut-scroller{
     display: flex;
     height: 100%;
-    img{
-        display: block;
-        height: 100%;
-        width: 100%;
-        background-image: url(../../assets/img/img-loading.png);
-    }
-    img, .load-err{
-        background-repeat: no-repeat;
-        background-position: 50% 50%;
-        background-size: 50%;
-    }
-    .load-err{
-        background-image: url(../../assets/img/img-error.png);
-    }
 }
 // 横向滚动 
 .nut-hor-scroll{
     height: 100%;
     width: 100%;
-    overflow: hidden;
+    // overflow: hidden;
     .nut-hor-list{
         height: 100%;
         display: flex;
@@ -53,7 +38,7 @@
         text-align: center;
         line-height: 50px;
         font-size: 12px;
-        color: $text-color;
+        color: #999;
         .nut-vert-pulldown-status, .nut-vert-loadmore-status{
             height: 50px;
         }

+ 25 - 18
src/packages/scroller/scroller.vue

@@ -5,23 +5,22 @@
                 :stretch="stretch"
                 :is-un-more="isUnMore"
                 :is-loading="isLoading"
+                :threshold="threshold"
+                :pulldown-txt="pulldownTxt"
+                :load-more-txt="loadMoreTxt"
+                :unload-more-txt="unloadMoreTxt"
                 @loadMore="loadMore"
                 @pulldown="pulldown"
             >
                 <slot name="list"  slot="list"></slot>
+
             </nut-vert-scroll>
         </template>
         <template v-else-if="type === 'horizontal'">
-            <nut-hor-scroll :list-data="listData"
-                :line-spacing="lineSpacing"
-                :stretch="stretch"
-                :is-un-more="isUnMore"
-                :is-loading="isLoading"
-                @loadMore="loadMore"
-                @jump="jump"
-            >
+            <nut-hor-scroll :stretch="stretch" @jump="jump">
                 <slot name="list"  slot="list"></slot>
                 <slot name="more"  slot="more"></slot>
+                <slot name="arrow" slot="arrow"></slot>
             </nut-hor-scroll>
         </template>
     </div>
@@ -34,19 +33,11 @@ export default {
     props: {
         type: {
             type: String,
-            default: 'horizontal' // horizontal vertical
-        },
-        listData: {
-            type: Array,
-            default: () => []
-        },
-        lineSpacing: {
-            type: Number,
-            default: 210
+            default: 'horizontal' 
         },
         stretch: {
             type: Number,
-            default: 200
+            default: 100
         },
         isUnMore: {
             type: Boolean,
@@ -55,6 +46,22 @@ export default {
         isLoading: {
             type: Boolean,
             default: false
+        },
+        threshold: {
+            type: Number,
+            default: 100
+        },
+        pulldownTxt: {
+            type: String,
+            default: '下拉刷新'
+        },
+        loadMoreTxt: {
+            type: String,
+            default: '上拉加载'
+        },
+        unloadMoreTxt: {
+            type: String,
+            default: '没有更多了'
         }
     },
     data() {

+ 50 - 38
src/packages/scroller/vertical-scroll.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="nut-vert-scroll" ref="wrapper">
-        <div class="nut-vert-list" ref="list">
-            <div class="nut-vert-pulldown">
+        <div class="nut-vert-list" ref="list" :style="{'min-height': listMinHeightStyle}">
+            <div class="nut-vert-pulldown" v-if="isFirstPull">
                 <div class="nut-vert-pulldown-txt" v-if="!isLoading">{{pulldownTxt}}</div>
                 <div class="nut-vert-pulldown-status" v-else>
                     <span class="nut-vert-loading"></span>
@@ -9,15 +9,17 @@
                 </div>
             </div>
             <slot name="list"></slot>
-            <div class="nut-vert-loadmore" v-if="!isUnMore && isShowLoadMore">
-                <div class="nut-vert-load-txt" v-if="!isLoading">{{loadMoreTxt}}</div>
-                <div class="nut-vert-load-status" v-else>
-                    <span class="nut-vert-loading"></span>
-                    <span class="nut-vert-loading-txt">加载中...</span>
-                </div>
-            </div>
-            <div v-else-if="isUnMore" class="nut-vert-unloadmore" >
-                <div class="nut-vert-unloadmore-txt">{{unloadMoreTxt}}</div>
+            <div class="nut-vert-loadmore" >
+                <template v-if="!isUnMore && isShowLoadMore">
+                    <!-- <div class="nut-vert-load-txt" >{{loadMoreTxt}}</div> -->
+                    <div class="nut-vert-load-status" v-if="isLoading">
+                        <span class="nut-vert-loading"></span>
+                        <span class="nut-vert-loading-txt">加载中...</span>
+                    </div>
+                </template>
+                <template v-else-if="isUnMore">
+                    <div class="nut-vert-unloadmore-txt">{{unloadMoreTxt}}</div>
+                </template>
             </div>
         </div>
     </div>
@@ -28,7 +30,7 @@ export default {
     props: {
         stretch: {
             type: Number,
-            default: 50
+            default: 100
         },
         isUnMore: {
             type: Boolean,
@@ -50,10 +52,10 @@ export default {
             type: String,
             default: '没有更多了'
         },
-        isLazyLoadImg: {
-            type: Boolean,
-            default: false
-        }
+        threshold: {
+            type: Number,
+            default: 100
+        },
     },
     watch: {
         'isLoading': function(status) {
@@ -79,7 +81,9 @@ export default {
             timer: null,
             timerEmit: null,
             realMove: 0,
-            isShowLoadMore: false
+            isShowLoadMore: false,
+            listMinHeightStyle: 'auto',
+            isFirstPull: true
         }
     },
 
@@ -87,10 +91,13 @@ export default {
         isShow() {
             let wrapH = this.$refs.wrapper.offsetHeight;
             let listH = this.$refs.list.offsetHeight;
-            if (wrapH <= listH) {
+            if (wrapH < listH) {
                 this.isShowLoadMore = true;
+                this.listMinHeightStyle = 'auto'
             } else {
                 this.isShowLoadMore = false;
+                this.isFirstPull = true;
+                this.listMinHeightStyle = `${wrapH}px`;
             }
         },
 
@@ -110,26 +117,29 @@ export default {
             let maxMove = -this.$refs.list.offsetHeight + h;
             if (type === 'end') {
                 if (updateMove > 0) {
-                    updateMove = 50;
-                    this.realMove = 0;
-                    if (!this.isLoading) {
-                        clearTimeout(this.timerEmit);
-                        this.timerEmit = setTimeout(() => {
-                            this.$emit('pulldown');
-                        }, time / 2);
-                    }
-                } else if (updateMove < maxMove) {
-                    if (maxMove <= 0) {
-                        updateMove = maxMove;
+                    if (!this.isShowLoadMore || this.isFirstPull) {
+                        updateMove = 50;
+                        this.realMove = 0;
+                        if (!this.isLoading) {
+                            clearTimeout(this.timerEmit);
+                            this.timerEmit = setTimeout(() => {
+                                this.$emit('pulldown');
+                            }, time / 2);
+                        }
                     } else {
+                        this.isFirstPull = true;
                         updateMove = 0;
                     }
+                } else if (updateMove < maxMove + this.threshold) {
+                    if (updateMove < maxMove) {
+                        updateMove = maxMove;
+                    }
                     this.realMove = maxMove;
                     if (!this.isLoading && !this.isUnMore) {
-                        clearTimeout(this.timerEmit);
-                        this.timerEmit = setTimeout(() => {
+                        //clearTimeout(this.timerEmit);
+                        //this.timerEmit = setTimeout(() => {
                             this.$emit('loadMore');
-                        }, time / 2);
+                        // }, time / 2);
                     }
                 }
                 if (updateMove == 50 && !this.isLoading) {
@@ -140,11 +150,11 @@ export default {
                 }
                 this.setTransform(updateMove, type, time)
             } else {
-                // if (updateMove > 0 && updateMove > this.stretch) {
-                //     updateMove = this.stretc;
-                // } else if (updateMove < maxMove - this.stretch) {
-                //     updateMove = maxMove - this.stretch;
-                // }
+                if (updateMove > 0 && updateMove > this.stretch) {
+                    updateMove = this.stretch;
+                } else if (updateMove < maxMove - this.stretch) {
+                    updateMove = maxMove - this.stretch;
+                }
                 this.setTransform(updateMove, null, null);
             }
         },
@@ -164,7 +174,9 @@ export default {
             this.touchParams.lastY = changedTouches.pageY;
             this.touchParams.lastTime = event.timestamp || Date.now();
             let move = this.touchParams.lastY - this.touchParams.startY;
-
+            if (move < 0 && this.isShowLoadMore && this.isFirstPull) {
+                this.isFirstPull = false;
+            }
             this.setMove(move);
         },