scroll.vue 12.0 KB
Newer Older
M
init  
miaodian 已提交
1 2
<template>
  <div ref="wrapper" class="cube-scroll-wrapper">
U
ustbhuangyi 已提交
3
    <div class="cube-scroll-content">
4
      <div ref="listWrapper" class="cube-scroll-list-wrapper">
A
AmyFoxFN 已提交
5 6
        <slot>
          <ul class="cube-scroll-list">
A
Amy 已提交
7 8 9 10 11
            <li
              class="cube-scroll-item border-bottom-1px"
              v-for="(item, index) in data"
              :key="index"
              @click="clickItem(item)">{{item}}</li>
A
AmyFoxFN 已提交
12 13 14
          </ul>
        </slot>
      </div>
M
init  
miaodian 已提交
15 16
      <slot name="pullup" :pullUpLoad="pullUpLoad" :isPullUpLoad="isPullUpLoad">
        <div class="cube-pullup-wrapper" v-if="pullUpLoad">
A
AmyFoxFN 已提交
17
          <div class="before-trigger" v-if="!isPullUpLoad">
M
init  
miaodian 已提交
18 19
            <span>{{ pullUpTxt }}</span>
          </div>
A
AmyFoxFN 已提交
20
          <div class="after-trigger" v-else>
M
init  
miaodian 已提交
21 22 23 24 25
            <loading></loading>
          </div>
        </div>
      </slot>
    </div>
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
    <div v-if="pullDownRefresh" class="cube-pulldown" ref="pulldown">
      <slot
        name="pulldown"
        :pullDownRefresh="pullDownRefresh"
        :pullDownStyle="pullDownStyle"
        :beforePullDown="beforePullDown"
        :isPullingDown="isPullingDown"
        :bubbleY="bubbleY">
        <div class="cube-pulldown-wrapper" :style="pullDownStyle">
          <div class="before-trigger" v-show="beforePullDown">
            <bubble :y="bubbleY" class="bubble"></bubble>
          </div>
          <div class="after-trigger" v-show="!beforePullDown">
            <div v-show="isPullingDown" class="loading">
              <loading></loading>
            </div>
            <div v-show="!isPullingDown" class="cube-pulldown-loaded"><span>{{ refreshTxt }}</span></div>
M
init  
miaodian 已提交
43 44
          </div>
        </div>
45 46
      </slot>
    </div>
M
init  
miaodian 已提交
47 48 49 50 51 52 53
  </div>
</template>

<script type="text/ecmascript-6">
  import BScroll from 'better-scroll'
  import Loading from '../loading/loading.vue'
  import Bubble from '../bubble/bubble.vue'
A
Amy 已提交
54
  import scrollMixin from '../../common/mixins/scroll'
U
ustbhuangyi 已提交
55
  import { getRect } from '../../common/helpers/dom'
A
Amy 已提交
56 57
  import { camelize, kebab } from '../../common/lang/string'
  import { tip } from '../../common/helpers/debug'
M
init  
miaodian 已提交
58 59 60 61 62

  const COMPONENT_NAME = 'cube-scroll'
  const DIRECTION_H = 'horizontal'
  const DIRECTION_V = 'vertical'
  const DEFAULT_REFRESH_TXT = 'Refresh success'
63
  const DEFAULT_STOP_TIME = 600
M
init  
miaodian 已提交
64 65 66 67 68

  const EVENT_CLICK = 'click'
  const EVENT_PULLING_DOWN = 'pulling-down'
  const EVENT_PULLING_UP = 'pulling-up'

A
Amy 已提交
69 70 71 72 73 74
  const EVENT_SCROLL = 'scroll'
  const EVENT_BEFORE_SCROLL_START = 'before-scroll-start'
  const EVENT_SCROLL_END = 'scroll-end'

  const SCROLL_EVENTS = [EVENT_SCROLL, EVENT_BEFORE_SCROLL_START, EVENT_SCROLL_END]

75
  const DEFAULT_OPTIONS = {
D
dolymood 已提交
76
    observeDOM: true,
77 78 79 80 81 82 83
    click: true,
    probeType: 1,
    scrollbar: false,
    pullDownRefresh: false,
    pullUpLoad: false
  }

M
init  
miaodian 已提交
84 85
  export default {
    name: COMPONENT_NAME,
A
Amy 已提交
86
    mixins: [scrollMixin],
M
init  
miaodian 已提交
87 88 89 90 91 92 93
    props: {
      data: {
        type: Array,
        default() {
          return []
        }
      },
A
Amy 已提交
94 95
      scrollEvents: {
        type: Array,
96
        default() {
A
Amy 已提交
97 98 99 100 101 102
          return []
        },
        validator(arr) {
          return arr.every((item) => {
            return SCROLL_EVENTS.indexOf(item) !== -1
          })
103
        }
M
init  
miaodian 已提交
104
      },
A
Amy 已提交
105
      // TODO: plan to remove at 1.10.0
M
init  
miaodian 已提交
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
      listenScroll: {
        type: Boolean,
        default: false
      },
      listenBeforeScroll: {
        type: Boolean,
        default: false
      },
      direction: {
        type: String,
        default: DIRECTION_V
      },
      refreshDelay: {
        type: Number,
        default: 20
      }
    },
    data() {
      return {
        beforePullDown: true,
        isPullingDown: false,
        isPullUpLoad: false,
        pullUpDirty: true,
        bubbleY: 0,
130 131 132
        pullDownStyle: '',
        pullDownStop: 40,
        pullDownHeight: 60
M
init  
miaodian 已提交
133 134 135
      }
    },
    computed: {
136
      pullDownRefresh() {
137 138 139 140 141 142 143 144
        let pullDownRefresh = this.options.pullDownRefresh
        if (pullDownRefresh === false) {
          return pullDownRefresh
        }
        if (pullDownRefresh === true) {
          pullDownRefresh = {}
        }
        return Object.assign({stop: this.pullDownStop}, pullDownRefresh)
145
      },
146 147 148
      pullUpLoad() {
        return this.options.pullUpLoad
      },
M
init  
miaodian 已提交
149
      pullUpTxt() {
150
        const pullUpLoad = this.pullUpLoad
D
dolymood 已提交
151
        const txt = pullUpLoad && pullUpLoad.txt
D
doly mood 已提交
152 153
        const moreTxt = (txt && txt.more) || ''
        const noMoreTxt = (txt && txt.noMore) || ''
M
init  
miaodian 已提交
154 155 156 157

        return this.pullUpDirty ? moreTxt : noMoreTxt
      },
      refreshTxt() {
158
        const pullDownRefresh = this.pullDownRefresh
D
doly mood 已提交
159
        return (pullDownRefresh && pullDownRefresh.txt) || DEFAULT_REFRESH_TXT
A
Amy 已提交
160 161 162 163 164 165 166 167 168
      },
      finalScrollEvents() {
        const finalScrollEvents = this.scrollEvents.slice()

        if (!finalScrollEvents.length) {
          this.listenScroll && finalScrollEvents.push(EVENT_SCROLL)
          this.listenBeforeScroll && finalScrollEvents.push(EVENT_BEFORE_SCROLL_START)
        }
        return finalScrollEvents
M
init  
miaodian 已提交
169 170
      }
    },
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
    watch: {
      data() {
        setTimeout(() => {
          this.forceUpdate(true)
        }, this.refreshDelay)
      },
      pullDownRefresh: {
        handler(newVal, oldVal) {
          if (newVal) {
            this.scroll.openPullDown(newVal)
            if (!oldVal) {
              this._onPullDownRefresh()
              this._calculateMinHeight()
            }
          }

          if (!newVal && oldVal) {
            this.scroll.closePullDown()
            this._offPullDownRefresh()
            this._calculateMinHeight()
          }
        },
        deep: true
      },
      pullUpLoad: {
        handler(newVal, oldVal) {
          if (newVal) {
            this.scroll.openPullUp(newVal)
            if (!oldVal) {
              this._onPullUpLoad()
              this._calculateMinHeight()
            }
          }

          if (!newVal && oldVal) {
            this.scroll.closePullUp()
            this._offPullUpLoad()
            this._calculateMinHeight()
          }
        },
        deep: true
      }
    },
    activated() {
      /* istanbul ignore next */
      this.enable()
    },
    deactivated() {
      /* istanbul ignore next */
      this.disable()
M
init  
miaodian 已提交
221 222
    },
    mounted() {
223
      this.$nextTick(() => {
F
funanamy 已提交
224
        this.initScroll()
225
      })
A
Amy 已提交
226
      this._checkDeprecated()
M
init  
miaodian 已提交
227
    },
D
doly mood 已提交
228 229 230
    beforeDestroy() {
      this.destroy()
    },
M
init  
miaodian 已提交
231
    methods: {
F
funanamy 已提交
232
      initScroll() {
M
init  
miaodian 已提交
233 234 235
        if (!this.$refs.wrapper) {
          return
        }
236
        this._calculateMinHeight()
M
init  
miaodian 已提交
237

238
        let options = Object.assign({}, DEFAULT_OPTIONS, {
M
init  
miaodian 已提交
239
          scrollY: this.direction === DIRECTION_V,
A
Amy 已提交
240
          scrollX: this.direction === DIRECTION_H,
A
AmyFoxFN 已提交
241
          probeType: this.finalScrollEvents.indexOf(EVENT_SCROLL) !== -1 ? 3 : 1
242
        }, this.options)
M
init  
miaodian 已提交
243 244 245

        this.scroll = new BScroll(this.$refs.wrapper, options)

A
Amy 已提交
246
        this._listenScrollEvents()
M
init  
miaodian 已提交
247 248

        if (this.pullDownRefresh) {
249
          this._getPullDownEleHeight()
250
          this._onPullDownRefresh()
M
init  
miaodian 已提交
251 252 253
        }

        if (this.pullUpLoad) {
254
          this._onPullUpLoad()
M
init  
miaodian 已提交
255 256 257 258 259 260 261 262 263
        }
      },
      disable() {
        this.scroll && this.scroll.disable()
      },
      enable() {
        this.scroll && this.scroll.enable()
      },
      refresh() {
264
        this._calculateMinHeight()
M
init  
miaodian 已提交
265 266
        this.scroll && this.scroll.refresh()
      },
F
funanamy 已提交
267
      destroy() {
268
        this.scroll && this.scroll.destroy()
D
doly mood 已提交
269
        this.scroll = null
F
funanamy 已提交
270
      },
M
init  
miaodian 已提交
271 272 273 274 275 276 277 278 279
      scrollTo() {
        this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments)
      },
      scrollToElement() {
        this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments)
      },
      clickItem(item) {
        this.$emit(EVENT_CLICK, item)
      },
280
      forceUpdate(dirty = false) {
M
init  
miaodian 已提交
281
        if (this.pullDownRefresh && this.isPullingDown) {
U
ustbhuangyi 已提交
282
          this.isPullingDown = false
283
          this._reboundPullDown(() => {
A
AmyFoxFN 已提交
284
            this._afterPullDown(dirty)
M
init  
miaodian 已提交
285 286 287 288 289
          })
        } else if (this.pullUpLoad && this.isPullUpLoad) {
          this.isPullUpLoad = false
          this.scroll.finishPullUp()
          this.pullUpDirty = dirty
A
AmyFoxFN 已提交
290
          dirty && this.refresh()
M
init  
miaodian 已提交
291
        } else {
A
AmyFoxFN 已提交
292
          dirty && this.refresh()
M
init  
miaodian 已提交
293 294
        }
      },
A
Amy 已提交
295 296 297
      resetPullUpTxt() {
        this.pullUpDirty = true
      },
A
Amy 已提交
298 299 300 301 302 303 304
      _listenScrollEvents() {
        this.finalScrollEvents.forEach((event) => {
          this.scroll.on(camelize(event), (...args) => {
            this.$emit(event, ...args)
          })
        })
      },
305
      _calculateMinHeight() {
306 307
        if (this.$refs.listWrapper) {
          this.$refs.listWrapper.style.minHeight = this.pullDownRefresh || this.pullUpLoad ? `${getRect(this.$refs.wrapper).height + 1}px` : 0
308 309
        }
      },
310 311 312
      _onPullDownRefresh() {
        this.scroll.on('pullingDown', this._pullDownHandle)
        this.scroll.on('scroll', this._pullDownScrollHandle)
M
init  
miaodian 已提交
313
      },
314 315 316 317 318
      _offPullDownRefresh() {
        this.scroll.off('pullingDown', this._pullDownHandle)
        this.scroll.off('scroll', this._pullDownScrollHandle)
      },
      _pullDownHandle() {
319 320 321
        if (this.resetPullDownTimer) {
          clearTimeout(this.resetPullDownTimer)
        }
322 323 324 325 326 327
        this.beforePullDown = false
        this.isPullingDown = true
        this.$emit(EVENT_PULLING_DOWN)
      },
      _pullDownScrollHandle(pos) {
        if (this.beforePullDown) {
328 329
          this.bubbleY = Math.max(0, pos.y - this.pullDownHeight)
          this.pullDownStyle = `top:${Math.min(pos.y - this.pullDownHeight, 0)}px`
330 331
        } else {
          this.bubbleY = 0
332
          this.pullDownStyle = `top:${Math.min(pos.y - this.pullDownStop, 0)}px`
333 334 335 336 337 338 339 340 341 342 343
        }
      },
      _onPullUpLoad() {
        this.scroll.on('pullingUp', this._pullUpHandle)
      },
      _offPullUpLoad() {
        this.scroll.off('pullingUp', this._pullUpHandle)
      },
      _pullUpHandle() {
        this.isPullUpLoad = true
        this.$emit(EVENT_PULLING_UP)
M
init  
miaodian 已提交
344
      },
345 346 347 348 349 350
      _reboundPullDown(next) {
        const {stopTime = DEFAULT_STOP_TIME} = this.pullDownRefresh
        setTimeout(() => {
          this.scroll.finishPullDown()
          next()
        }, stopTime)
M
init  
miaodian 已提交
351
      },
A
AmyFoxFN 已提交
352
      _afterPullDown(dirty) {
353
        this.resetPullDownTimer = setTimeout(() => {
354
          this.pullDownStyle = `top: -${this.pullDownHeight}px`
M
init  
miaodian 已提交
355
          this.beforePullDown = true
A
AmyFoxFN 已提交
356
          dirty && this.refresh()
M
init  
miaodian 已提交
357
        }, this.scroll.options.bounceTime)
A
Amy 已提交
358 359 360 361 362 363
      },
      _checkDeprecated() {
        const deprecatedKeys = ['listenScroll', 'listenBeforeScroll']
        deprecatedKeys.forEach((key) => {
          this[key] && tip(`The property "${kebab(key)}" is deprecated, please use the recommended property "scroll-events" to replace it. Details could be found in https://didi.github.io/cube-ui/#/en-US/docs/scroll#cube-Propsconfiguration-anchor`, COMPONENT_NAME)
        })
364 365 366 367 368 369 370 371 372 373 374 375 376
      },
      _getPullDownEleHeight() {
        const pulldown = this.$refs.pulldown.firstChild
        this.pullDownHeight = getRect(pulldown).height

        this.beforePullDown = false
        this.isPullingDown = true
        this.$nextTick(() => {
          this.pullDownStop = getRect(pulldown).height

          this.beforePullDown = true
          this.isPullingDown = false
        })
M
init  
miaodian 已提交
377 378 379 380 381 382 383 384 385 386
      }
    },
    components: {
      Loading,
      Bubble
    }
  }
</script>

<style lang="stylus" rel="stylesheet/stylus">
387 388 389
  @require "../../common/stylus/variable.styl"

  .cube-scroll-wrapper
390
    position: relative
391
    height: 100%
392
    overflow: hidden
393

M
init  
miaodian 已提交
394 395 396 397 398 399 400 401
  .cube-pulldown-wrapper
    position: absolute
    width: 100%
    left: 0
    display: flex
    justify-content: center
    align-items: center
    transition: all
402 403 404 405
    .before-trigger
      height: 54px
      line-height: 0
      padding-top: 6px
M
init  
miaodian 已提交
406
    .after-trigger
407 408 409 410
      .loading
        padding: 8px 0
      .cube-pulldown-loaded
        padding: 12px 0
M
init  
miaodian 已提交
411 412 413 414 415 416

  .cube-pullup-wrapper
    width: 100%
    display: flex
    justify-content: center
    align-items: center
417 418
    .before-trigger
      padding: 22px 0
A
AmyFoxFN 已提交
419
      min-height: 1em
420
    .after-trigger
A
AmyFoxFN 已提交
421
      padding: 19px 0
U
ustbhuangyi 已提交
422 423 424 425

  .cube-scroll-content
    position: relative
    z-index: 1
426

427 428 429 430 431
  .cube-scroll-item
    height: 60px
    line-height: 60px
    font-size: $fontsize-large-x
    padding-left: 20px
M
init  
miaodian 已提交
432
</style>