scroll.vue 12.2 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'
T
tank0317 已提交
55
  import deprecatedMixin from '../../common/mixins/deprecated'
U
ustbhuangyi 已提交
56
  import { getRect } from '../../common/helpers/dom'
T
tank0317 已提交
57
  import { camelize } from '../../common/lang/string'
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,
T
tank0317 已提交
86
    mixins: [scrollMixin, deprecatedMixin],
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
      listenScroll: {
        type: Boolean,
T
tank0317 已提交
108 109
        default: undefined,
        deprecated: {
F
fengweiyao 已提交
110
          replacedBy: 'scroll-events'
T
tank0317 已提交
111
        }
M
init  
miaodian 已提交
112 113 114
      },
      listenBeforeScroll: {
        type: Boolean,
T
tank0317 已提交
115 116
        default: undefined,
        deprecated: {
F
fengweiyao 已提交
117
          replacedBy: 'scroll-events'
T
tank0317 已提交
118
        }
M
init  
miaodian 已提交
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
      },
      direction: {
        type: String,
        default: DIRECTION_V
      },
      refreshDelay: {
        type: Number,
        default: 20
      }
    },
    data() {
      return {
        beforePullDown: true,
        isPullingDown: false,
        isPullUpLoad: false,
        pullUpDirty: true,
        bubbleY: 0,
136 137 138
        pullDownStyle: '',
        pullDownStop: 40,
        pullDownHeight: 60
M
init  
miaodian 已提交
139 140 141
      }
    },
    computed: {
142
      pullDownRefresh() {
143
        let pullDownRefresh = this.options.pullDownRefresh
D
dolymood 已提交
144
        if (!pullDownRefresh) {
145 146 147 148 149 150
          return pullDownRefresh
        }
        if (pullDownRefresh === true) {
          pullDownRefresh = {}
        }
        return Object.assign({stop: this.pullDownStop}, pullDownRefresh)
151
      },
152 153 154
      pullUpLoad() {
        return this.options.pullUpLoad
      },
M
init  
miaodian 已提交
155
      pullUpTxt() {
156
        const pullUpLoad = this.pullUpLoad
D
dolymood 已提交
157
        const txt = pullUpLoad && pullUpLoad.txt
D
doly mood 已提交
158 159
        const moreTxt = (txt && txt.more) || ''
        const noMoreTxt = (txt && txt.noMore) || ''
M
init  
miaodian 已提交
160 161 162 163

        return this.pullUpDirty ? moreTxt : noMoreTxt
      },
      refreshTxt() {
164
        const pullDownRefresh = this.pullDownRefresh
D
doly mood 已提交
165
        return (pullDownRefresh && pullDownRefresh.txt) || DEFAULT_REFRESH_TXT
A
Amy 已提交
166 167 168 169 170 171 172 173 174
      },
      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 已提交
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 221 222 223 224 225 226
    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 已提交
227 228
    },
    mounted() {
229
      this.$nextTick(() => {
F
funanamy 已提交
230
        this.initScroll()
231
      })
M
init  
miaodian 已提交
232
    },
D
doly mood 已提交
233 234 235
    beforeDestroy() {
      this.destroy()
    },
M
init  
miaodian 已提交
236
    methods: {
F
funanamy 已提交
237
      initScroll() {
M
init  
miaodian 已提交
238 239 240
        if (!this.$refs.wrapper) {
          return
        }
241
        this._calculateMinHeight()
M
init  
miaodian 已提交
242

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

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

A
Amy 已提交
251
        this._listenScrollEvents()
M
init  
miaodian 已提交
252 253

        if (this.pullDownRefresh) {
254
          this._getPullDownEleHeight()
255
          this._onPullDownRefresh()
M
init  
miaodian 已提交
256 257 258
        }

        if (this.pullUpLoad) {
259
          this._onPullUpLoad()
M
init  
miaodian 已提交
260 261 262 263 264 265 266 267 268
        }
      },
      disable() {
        this.scroll && this.scroll.disable()
      },
      enable() {
        this.scroll && this.scroll.enable()
      },
      refresh() {
269
        this._calculateMinHeight()
M
init  
miaodian 已提交
270 271
        this.scroll && this.scroll.refresh()
      },
F
funanamy 已提交
272
      destroy() {
D
dolymood 已提交
273 274 275 276
        if (this.scroll) {
          this._offScrollEvents()
          this.scroll.destroy()
        }
D
doly mood 已提交
277
        this.scroll = null
F
funanamy 已提交
278
      },
M
init  
miaodian 已提交
279 280 281 282 283 284 285 286 287
      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)
      },
288
      forceUpdate(dirty = false) {
M
init  
miaodian 已提交
289
        if (this.pullDownRefresh && this.isPullingDown) {
U
ustbhuangyi 已提交
290
          this.isPullingDown = false
291
          this._reboundPullDown(() => {
A
AmyFoxFN 已提交
292
            this._afterPullDown(dirty)
M
init  
miaodian 已提交
293 294 295 296 297
          })
        } else if (this.pullUpLoad && this.isPullUpLoad) {
          this.isPullUpLoad = false
          this.scroll.finishPullUp()
          this.pullUpDirty = dirty
A
AmyFoxFN 已提交
298
          dirty && this.refresh()
M
init  
miaodian 已提交
299
        } else {
A
AmyFoxFN 已提交
300
          dirty && this.refresh()
M
init  
miaodian 已提交
301 302
        }
      },
A
Amy 已提交
303 304 305
      resetPullUpTxt() {
        this.pullUpDirty = true
      },
A
Amy 已提交
306
      _listenScrollEvents() {
D
dolymood 已提交
307
        this._scrollEventsHandlers = {}
A
Amy 已提交
308
        this.finalScrollEvents.forEach((event) => {
D
dolymood 已提交
309 310
          const _event = camelize(event)
          this._scrollEventsHandlers[_event] = (...args) => {
A
Amy 已提交
311
            this.$emit(event, ...args)
D
dolymood 已提交
312 313 314 315 316 317 318 319 320
          }
          this.scroll.on(_event, this._scrollEventsHandlers[_event])
        })
      },
      _offScrollEvents() {
        this.finalScrollEvents.forEach((event) => {
          const _event = camelize(event)
          this.scroll.off(_event, this._scrollEventsHandlers[_event])
          this._scrollEventsHandlers[_event] = null
A
Amy 已提交
321
        })
D
dolymood 已提交
322
        this._scrollEventsHandlers = null
A
Amy 已提交
323
      },
324
      _calculateMinHeight() {
325 326
        if (this.$refs.listWrapper) {
          this.$refs.listWrapper.style.minHeight = this.pullDownRefresh || this.pullUpLoad ? `${getRect(this.$refs.wrapper).height + 1}px` : 0
327 328
        }
      },
329 330 331
      _onPullDownRefresh() {
        this.scroll.on('pullingDown', this._pullDownHandle)
        this.scroll.on('scroll', this._pullDownScrollHandle)
M
init  
miaodian 已提交
332
      },
333 334 335 336 337
      _offPullDownRefresh() {
        this.scroll.off('pullingDown', this._pullDownHandle)
        this.scroll.off('scroll', this._pullDownScrollHandle)
      },
      _pullDownHandle() {
338 339 340
        if (this.resetPullDownTimer) {
          clearTimeout(this.resetPullDownTimer)
        }
341 342 343 344 345 346
        this.beforePullDown = false
        this.isPullingDown = true
        this.$emit(EVENT_PULLING_DOWN)
      },
      _pullDownScrollHandle(pos) {
        if (this.beforePullDown) {
347 348
          this.bubbleY = Math.max(0, pos.y - this.pullDownHeight)
          this.pullDownStyle = `top:${Math.min(pos.y - this.pullDownHeight, 0)}px`
349 350
        } else {
          this.bubbleY = 0
351
          this.pullDownStyle = `top:${Math.min(pos.y - this.pullDownStop, 0)}px`
352 353 354 355 356 357 358 359 360 361 362
        }
      },
      _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 已提交
363
      },
364 365 366 367 368 369
      _reboundPullDown(next) {
        const {stopTime = DEFAULT_STOP_TIME} = this.pullDownRefresh
        setTimeout(() => {
          this.scroll.finishPullDown()
          next()
        }, stopTime)
M
init  
miaodian 已提交
370
      },
A
AmyFoxFN 已提交
371
      _afterPullDown(dirty) {
372
        this.resetPullDownTimer = setTimeout(() => {
373
          this.pullDownStyle = `top: -${this.pullDownHeight}px`
M
init  
miaodian 已提交
374
          this.beforePullDown = true
A
AmyFoxFN 已提交
375
          dirty && this.refresh()
M
init  
miaodian 已提交
376
        }, this.scroll.options.bounceTime)
A
Amy 已提交
377
      },
378 379 380 381 382 383 384 385 386 387 388 389
      _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 已提交
390 391 392 393 394 395 396 397 398 399
      }
    },
    components: {
      Loading,
      Bubble
    }
  }
</script>

<style lang="stylus" rel="stylesheet/stylus">
400 401 402
  @require "../../common/stylus/variable.styl"

  .cube-scroll-wrapper
403
    position: relative
404
    height: 100%
405
    overflow: hidden
406

407 408 409
  .cube-scroll-list-wrapper
    overflow: hidden

M
init  
miaodian 已提交
410 411 412 413 414 415 416 417
  .cube-pulldown-wrapper
    position: absolute
    width: 100%
    left: 0
    display: flex
    justify-content: center
    align-items: center
    transition: all
418 419 420 421
    .before-trigger
      height: 54px
      line-height: 0
      padding-top: 6px
M
init  
miaodian 已提交
422
    .after-trigger
423 424 425 426
      .loading
        padding: 8px 0
      .cube-pulldown-loaded
        padding: 12px 0
M
init  
miaodian 已提交
427 428 429 430 431 432

  .cube-pullup-wrapper
    width: 100%
    display: flex
    justify-content: center
    align-items: center
433 434
    .before-trigger
      padding: 22px 0
A
AmyFoxFN 已提交
435
      min-height: 1em
436
    .after-trigger
A
AmyFoxFN 已提交
437
      padding: 19px 0
U
ustbhuangyi 已提交
438 439 440 441

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

443 444 445 446 447
  .cube-scroll-item
    height: 60px
    line-height: 60px
    font-size: $fontsize-large-x
    padding-left: 20px
M
init  
miaodian 已提交
448
</style>