Browse Source

Merge branch 'dev' of https://github.com/jdf2e/nutui into dev

杨磊 7 years ago
parent
commit
42da6062fc

+ 7 - 0
config.json

@@ -342,6 +342,13 @@
       "showDemo": true
     },
     {
+      "name": "Scroller",
+      "chnName": "下拉刷新上拉加载组件",
+      "desc": "可下拉刷新上拉加载的容器组件",
+      "type": "component",
+      "showDemo": true
+    }, 
+    {
       "name": "FooterCom",
       "chnName": "公共底部菜单",
       "desc": "公共底部菜单",

+ 60 - 0
src/demo/scroller.vue

@@ -0,0 +1,60 @@
+<template>
+  <div>
+    <nut-docheader 
+    :name="$route.name"
+    :showQrCode="true"></nut-docheader>
+
+    <h5>示例</h5>
+    <div class="scroller-container">
+      <nut-scroller 
+        :onRefresh="onRefresh" :onInfinite="onInfinite">
+        <div v-for="(item, index) in list" :key="index" class="content-item">{{'滚动区域的内容' + (index + 1)}}</div>
+      </nut-scroller>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+     list: [...Array(15)]
+    };
+  },
+  methods: {
+    onRefresh(done) {
+      setTimeout(() => {
+        this.list = [...Array(15)]
+          done()
+      }, 2000)
+    },
+    onInfinite(done) {
+      setTimeout(() => {
+        if (this.list && this.list.length < 30) {
+          this.list = [...this.list, ...Array(10)]
+          done(true);
+          return
+        }
+        done(false)
+      }, 2000)
+    }
+  }
+};
+</script>
+
+<style lang="scss">
+  .scroller-container {
+    position: relative;
+    width: 100%;
+    height: 500px;
+		background: #f2f2f2;
+  }
+  .content-item {
+    padding: 5px;
+    line-height: 20px;
+    background: #cccccc;
+    margin-bottom: 10px;
+    font-size: 14px;
+  }
+</style>
+

+ 7 - 0
src/package/scroller/index.js

@@ -0,0 +1,7 @@
+import Scroller from './src/scroller.vue';
+
+Scroller.install = function(Vue) {
+  Vue.component(Scroller.name, Scroller);
+};
+
+export default Scroller

+ 62 - 0
src/package/scroller/src/module/animate.js

@@ -0,0 +1,62 @@
+
+window.requestAnimFrame = (function(){
+  return  window.requestAnimationFrame       || 
+          window.webkitRequestAnimationFrame || 
+          window.mozRequestAnimationFrame    || 
+          window.oRequestAnimationFrame      || 
+          window.msRequestAnimationFrame     || 
+          function( callback ){
+            window.setTimeout(callback, 1000 / 60);
+          };
+})();
+
+export default content => {
+
+  let global = window;
+
+  let docStyle = document.documentElement.style;
+
+  let engine;
+  if (global.opera && Object.prototype.toString.call(opera) === '[object Opera]') {
+    engine = 'presto';
+  } else if ('MozAppearance' in docStyle) {
+    engine = 'gecko';
+  } else if ('WebkitAppearance' in docStyle) {
+    engine = 'webkit';
+  } else if (typeof navigator.cpuClass === 'string') {
+    engine = 'trident';
+  }
+
+  let vendorPrefix = {
+    trident: 'ms',
+    gecko: 'Moz',
+    webkit: 'Webkit',
+    presto: 'O'
+  }[engine];
+
+  return (end, start) => {
+    if (typeof start === 'undefined') {
+      content.style[vendorPrefix + 'Transform'] = 'translate3d(0, ' + end + 'px, 0)'
+      return
+    }
+    let _end = end
+    let _start = start
+    let dis 
+    let tar 
+    let step
+    let _move = () => {
+      dis = _end - _start
+      step = dis / 10
+      tar = _start + step
+      _start = tar
+      if (Math.abs(dis - step) < 1) {
+        content.style.WebkitTransform = 'translate3d(0, ' + end + 'px, 0)'
+        return
+      }
+      content.style.WebkitTransform = 'translate3d(0, ' + tar + 'px, 0)'
+      window.requestAnimFrame(_move)
+    }
+    window.requestAnimFrame(_move)
+  }
+}
+

+ 116 - 0
src/package/scroller/src/module/scroller.js

@@ -0,0 +1,116 @@
+export default class Scroller {
+  constructor(callback, options) {
+    let _options = {
+      top : 0,
+      pullH : 0,
+      loadH: 0,
+      outerH : 0,
+      prevTop : 0,
+      startY : 0,
+      offset : 100,
+      fetching : false,
+      hasMore: true
+    }
+    this.__animate = callback
+    for (let key in _options) {
+			this[key] = _options[key];
+    }
+    for (let key in options) {
+			this[key] = options[key];
+    }
+    this.innerH = this.innerContent.clientHeight
+    this.moveY = 0
+  }
+
+  initPullToRefresh(startPullCallback, arrivalFreedCallback, freedCallback) {
+    this.pullStart = startPullCallback
+    this.arrivalFreed = arrivalFreedCallback
+    this.freed = freedCallback
+  }
+
+  initPullToInfinite(infiniteCallback) {
+    this.onInfinite = infiniteCallback
+  }
+
+  doTouchStart(touches) {
+    this.prevTop = this.top
+    this.startY = touches[0].pageY
+    this.innerH = this.innerContent.clientHeight
+    this.moveY = 0
+  }
+
+  doTouchMove(touches) {
+    if (this.fetching) return
+
+    let _moveY = touches[0].pageY - this.startY
+    this.moveY = _moveY
+   
+    let _top = this.moveY + this.prevTop
+    this.__animate(_top)
+    this.top = _top
+    this.moveY = _moveY
+    if (this.fetching) return
+
+    let isBottom = Math.abs(_top) + this.outerH > this.innerH ? true : false
+    if (_moveY > 0) {
+      if (!this.enableRefresh) return
+      _top >= this.offset ? this.arrivalFreed() : this.pullStart()
+    } else {
+      if (!isBottom || !this.hasMore || !this.enableInfinite) return
+      this.fetching = true
+      this.onInfinite()
+    }
+  }
+
+  doTouchEnd(e) {
+    this.innerH = this.innerContent.clientHeight
+    let _normalEndTop = this.outerH - this.innerH
+    let isBottom = Math.abs(this.top) + this.outerH >= this.innerH ? true : false
+    if (this.top >= this.offset) {
+      if (!this.enableRefresh) {
+        this.__animate(0, this.top) 
+        this.top = 0
+        return
+      }
+      this.__animate(this.pullH, this.top)
+      this.top = this.pullH
+      this.freed()
+      return
+    }
+    if (this.top < 0 && !isBottom) {
+      this.__animate(this.top, this.top - this.moveY)
+      return
+    }
+    if (this.top < 0 && isBottom) {
+      if (this.outerH >= this.innerH) {
+        this.__animate(0, this.top)
+        this.top = 0
+        return
+      }
+      let _h = _normalEndTop
+      if (this.fetching) {
+        _h = _normalEndTop - this.loadH
+      }
+      this.__animate(_h, this.top)
+      this.top = _h
+      return
+    }
+    this.__animate(0, this.top)
+    this.top = 0
+  }
+
+  startRefresh() {
+    this.fetching = true
+  }
+
+  finishedRefesh() {
+    this.fetching = false
+    this.__animate(0, this.top)
+    this.top = 0
+  }
+
+  finishedInfinite(hasMore) {
+    this.fetching = false
+    this.hasMore = hasMore
+  }
+}

File diff suppressed because it is too large
+ 238 - 0
src/package/scroller/src/scroller.vue


+ 214 - 0
src/view/scroller.vue

@@ -0,0 +1,214 @@
+<template>
+    <div>
+        <nut-docheader 
+        :name="$route.name"
+        :showQrCode="true"></nut-docheader>
+        <h5>示例</h5>
+        <h6>通用数据代码和回调示例</h6>
+        <nut-codebox :code="code"></nut-codebox>
+
+        <h6>示例</h6>
+        <div class="scroller-container">
+          <nut-scroller 
+            :onRefresh="onRefresh" :onInfinite="onInfinite">
+            <div v-for="(item, index) in list" :key="index" class="content-item">{{'滚动区域的内容' + (index + 1)}}</div>
+          </nut-scroller>
+        </div>
+      
+        <h5>Props</h5>
+        <div class="tbl-wrapper">
+        <table class="u-full-width">
+          <thead>
+            <tr>
+              <th>参数</th>
+              <th>说明</th>
+              <th>类型</th>
+              <th>默认值</th>
+              <th>是否必传</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr>
+              <td>offset</td>
+              <td>触发下拉刷新的阈值</td>
+              <td>Number</td>
+              <td>100</td>
+              <td>否</td>
+            </tr>
+            <tr>
+              <td>enableInfinite</td>
+              <td>是否开启上拉加载</td>
+              <td>Boolean</td>
+              <td>true</td>
+              <td>否</td>
+            </tr>
+            <tr>
+              <td>enableRefresh</td>
+              <td>是否开启下拉刷新</td>
+              <td>Boolean</td>
+              <td>true</td>
+              <td>否</td>
+            </tr>
+            <tr>
+              <td>onRefresh</td>
+              <td>下拉刷新执行的函数</td>
+              <td>Function</td>
+              <td>--</td>
+              <td>如果开启下拉刷新功能,必传</td>
+            </tr>
+            <tr>
+              <td>onInfinite</td>
+              <td>加载更多执行的函数</td>
+              <td>Function</td>
+              <td>--</td>
+              <td>如果开启上拉加载功能,必传</td>
+            </tr>
+            <tr>
+              <td>noDataText</td>
+              <td>没有更多数据时的提示语</td>
+              <td>String</td>
+              <td>没有更多数据了</td>
+              <td>否</td>
+            </tr>
+            <tr>
+              <td>refreshText</td>
+              <td>下拉刷新提示</td>
+              <td>String</td>
+              <td>下拉刷新</td>
+              <td>否</td>
+            </tr>
+            <tr>
+              <td>freedRefreshText</td>
+              <td>释放下拉提示语</td>
+              <td>String</td>
+              <td>松开刷新数据</td>
+              <td>否</td>
+            </tr>
+          </tbody>
+        </table>
+        </div>
+
+        <h5>插槽</h5>
+        <div class="tbl-wrapper">
+        <table class="u-full-width">
+          <thead>
+            <tr>
+              <th>插槽名</th>
+              <th>说明</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr>
+              <td>refresh</td>
+              <td>刷新时的效果或文字</td>
+            </tr>
+            <tr>
+              <td>load-more</td>
+              <td>加载时的效果或文字</td>
+            </tr>
+          </tbody>
+        </table>
+        </div>
+    </div>
+
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      list: [...Array(10)],
+      code: `
+        <template>
+          <div class="scroller-container">
+            <nut-scroller 
+              :on-refresh="onRefresh"
+              :on-infinite="onInfinite">
+              <div v-for="(item, index) in list" :key="index" class="content-item">{{'滚动区域的内容' + (index + 1)}}</div>
+            </nut-scroller>
+          </div>
+        </template>
+        <style lang="scss">
+          .scroller-container {
+            position: relative;
+            width: 100%;
+            height: 300px;
+            background: #f2f2f2;
+          }
+          .content-item {
+            padding: 5px;
+            line-height: 20px;
+            background: #cccccc;
+            margin-bottom: 10px;
+            font-size: 14px;
+          }
+        </style>
+        
+          export default {
+            data() {
+              retuen {
+                list: [...Array(10)]
+              }
+            },
+            methods: {
+              onRefresh(done) {
+                setTimeout(() => {
+                  this.list = [...Array(10)]
+                    done()
+                }, 2000)
+              },
+              onInfinite(done) {
+                setTimeout(() => {
+                  if (this.list && this.list.length < 30) {
+                    this.list = [...this.list, ...Array(10)]
+                    done(true);
+                    return
+                  }
+                  done(false) // 回传没有更多数据的标识
+                }, 2000)
+              }
+            }
+          }
+        
+      `
+    };
+  },
+  mounted() {
+    
+  },
+  methods: {
+    onRefresh(done) {
+      setTimeout(() => {
+        this.list = [...Array(10)]
+          done()
+      }, 2000)
+    },
+    onInfinite(done) {
+      setTimeout(() => {
+        if (this.list && this.list.length < 30) {
+          this.list = [...this.list, ...Array(10)]
+          done(true);
+          return
+        }
+        done(false)
+      }, 2000)
+    }
+  }
+};
+</script>
+
+<style lang="scss">
+  .scroller-container {
+    position: relative;
+    width: 100%;
+    height: 300px;
+		background: #f2f2f2;
+  }
+  .content-item {
+    padding: 5px;
+    line-height: 20px;
+    background: #cccccc;
+    margin-bottom: 10px;
+    font-size: 14px;
+  }
+</style>