slide.vue 9.0 KB
Newer Older
M
init  
miaodian 已提交
1 2 3
<template>
  <div class="cube-slide" ref="slide">
    <div class="cube-slide-group" ref="slideGroup">
4
      <slot>
A
Amy 已提交
5 6 7 8 9 10
        <cube-slide-item
          v-for="(item, index) in data"
          :key="index"
          @click.native="clickItem(item, index)"
          :item="item">
        </cube-slide-item>
11
      </slot>
M
init  
miaodian 已提交
12
    </div>
13
    <div class="cube-slide-dots" v-if="showDots">
D
dolymood 已提交
14
      <slot name="dots" :current="currentPageIndex" :dots="dots">
15
        <span :class="{active: currentPageIndex === index}" v-for="(item, index) in dots" :key="index"></span>
D
dolymood 已提交
16
      </slot>
M
init  
miaodian 已提交
17 18 19 20 21
    </div>
  </div>
</template>

<script type="text/ecmascript-6">
D
dolymood 已提交
22
  import CubeSlideItem from './slide-item.vue'
M
init  
miaodian 已提交
23
  import BScroll from 'better-scroll'
A
Amy 已提交
24
  import scrollMixin from '../../common/mixins/scroll'
T
tank0317 已提交
25
  import deprecatedMixin from '../../common/mixins/deprecated'
M
init  
miaodian 已提交
26 27 28

  const COMPONENT_NAME = 'cube-slide'
  const EVENT_CHANGE = 'change'
A
AmyFoxFN 已提交
29
  const EVENT_SELECT = 'click'
A
Amy 已提交
30
  const EVENT_SCROLL_END = 'scroll-end'
J
JiZhi 已提交
31
  const EVENT_SCROLL = 'scroll'
A
Amy 已提交
32

33 34
  const DIRECTION_H = 'horizontal'
  const DIRECTION_V = 'vertical'
M
init  
miaodian 已提交
35

A
Amy 已提交
36 37 38
  const DEFAULT_OPTIONS = {
    momentum: false,
    click: true,
A
AmyFoxFN 已提交
39 40
    observeDOM: false,
    bounce: false
A
Amy 已提交
41 42
  }

M
init  
miaodian 已提交
43 44
  export default {
    name: COMPONENT_NAME,
T
tank0317 已提交
45
    mixins: [scrollMixin, deprecatedMixin],
M
init  
miaodian 已提交
46
    props: {
47 48 49
      data: {
        type: Array,
        default() {
50
          /* istanbul ignore next */
51 52 53
          return []
        }
      },
D
dolymood 已提交
54 55 56 57
      initialIndex: {
        type: Number,
        default: 0
      },
M
init  
miaodian 已提交
58 59 60 61 62 63 64 65 66 67 68
      loop: {
        type: Boolean,
        default: true
      },
      threshold: {
        type: Number,
        default: 0.3
      },
      speed: {
        type: Number,
        default: 400
D
dolymood 已提交
69
      },
A
Amy 已提交
70
      autoPlay: {
D
dolymood 已提交
71
        type: Boolean,
A
Amy 已提交
72
        default: true
73
      },
A
Amy 已提交
74 75 76 77 78
      interval: {
        type: Number,
        default: 4000
      },
      showDots: {
79
        type: Boolean,
A
Amy 已提交
80
        default: true
81 82 83 84 85
      },
      direction: {
        type: String,
        default: DIRECTION_H
      },
A
Amy 已提交
86 87
      // The props allowVertical, stopPropagation could be removed in next minor version.
      allowVertical: {
88
        type: Boolean,
T
tank0317 已提交
89 90 91 92
        default: undefined,
        deprecated: {
          replacedBy: 'options'
        }
A
Amy 已提交
93 94 95
      },
      stopPropagation: {
        type: Boolean,
T
tank0317 已提交
96 97 98 99
        default: undefined,
        deprecated: {
          replacedBy: 'options'
        }
100 101 102 103
      },
      refreshResetCurrent: {
        type: Boolean,
        default: true
M
init  
miaodian 已提交
104 105 106 107 108
      }
    },
    data() {
      return {
        dots: 0,
D
dolymood 已提交
109 110 111
        currentPageIndex: this.initialIndex || 0
      }
    },
112
    created() {
D
dolymood 已提交
113
      this._dataWatchers = []
A
Amy 已提交
114
      const needRefreshProps = ['data', 'loop', 'autoPlay', 'options.eventPassthrough', 'threshold', 'speed', 'allowVertical']
115
      needRefreshProps.forEach((key) => {
D
dolymood 已提交
116
        this._dataWatchers.push(this.$watch(key, () => {
A
Amy 已提交
117 118 119 120 121
          // To fix the render bug when add items since loop.
          if (key === 'data') {
            this._destroy()
          }

122
          /* istanbul ignore next */
123 124 125
          this.$nextTick(() => {
            this.refresh()
          })
D
dolymood 已提交
126
        }))
127 128
      })
    },
D
dolymood 已提交
129 130 131 132 133
    watch: {
      initialIndex(newIndex) {
        if (newIndex !== this.currentPageIndex) {
          this.slide && this.slide.goToPage(newIndex)
        }
M
init  
miaodian 已提交
134 135 136
      }
    },
    methods: {
A
AmyFoxFN 已提交
137
      clickItem(item, index) {
138
        /* istanbul ignore next */
139 140
        this.$emit(EVENT_SELECT, item, index)
      },
M
init  
miaodian 已提交
141
      refresh() {
142 143 144 145
        /* istanbul ignore if */
        if (this.slide === null) {
          return
        }
A
Amy 已提交
146
        this._destroy()
D
dolymood 已提交
147
        clearTimeout(this._timer)
148

149
        if (this.slide && this.refreshResetCurrent) {
150 151 152 153
          this.currentPageIndex = 0
        }
        this._updateSlideDom()
        if (this.showDots) {
D
dolymood 已提交
154
          this._initDots()
155
        }
D
dolymood 已提交
156 157 158
        if (this.currentPageIndex >= this.dots.length) {
          this.currentPageIndex = this.dots.length - 1
        }
159
        this._initSlide()
D
dolymood 已提交
160

161 162 163
        if (this.autoPlay) {
          this._play()
        }
D
dolymood 已提交
164
      },
A
Amy 已提交
165 166 167
      _destroy() {
        this.slide && this.slide.destroy()
      },
D
dolymood 已提交
168
      _refresh() {
169
        this._updateSlideDom(true)
M
init  
miaodian 已提交
170 171
        this.slide.refresh()
      },
172
      _updateSlideDom(isResize) {
D
dolymood 已提交
173
        this._setSlideStyle(isResize)
M
init  
miaodian 已提交
174
      },
D
dolymood 已提交
175
      _setSlideStyle(isResize) {
176 177
        this.children = this.$refs.slideGroup.children

D
dolymood 已提交
178 179 180 181 182 183 184 185
        const target = this.direction === DIRECTION_H ? 'width' : 'height'
        let allSize = 0
        const slideSize = this.$refs.slide[`client${target[0].toUpperCase() + target.slice(1)}`]
        const len = this.children.length
        for (let i = 0; i < len; i++) {
          const child = this.children[i]
          child.style[target] = slideSize + 'px'
          allSize += slideSize
186
        }
D
dolymood 已提交
187 188
        if (this.loop && !isResize && len > 1) {
          allSize += 2 * slideSize
189
        }
D
dolymood 已提交
190
        this.$refs.slideGroup.style[target] = allSize + 'px'
191
      },
M
init  
miaodian 已提交
192
      _initSlide() {
193
        const eventPassthrough = this.direction === DIRECTION_H && this.allowVertical ? DIRECTION_V : ''
A
Amy 已提交
194 195

        const options = Object.assign({}, DEFAULT_OPTIONS, {
196 197 198
          scrollX: this.direction === DIRECTION_H,
          scrollY: this.direction === DIRECTION_V,
          eventPassthrough,
M
init  
miaodian 已提交
199 200 201 202 203
          snap: {
            loop: this.loop,
            threshold: this.threshold,
            speed: this.speed
          },
A
Amy 已提交
204 205 206 207
          stopPropagation: this.stopPropagation
        }, this.options)

        this.slide = new BScroll(this.$refs.slide, options)
M
init  
miaodian 已提交
208

209 210
        this.slide.on('scrollEnd', this._onScrollEnd)

D
dolymood 已提交
211 212
        this.slide.goToPage(this.currentPageIndex, 0, 0)

213 214
        /* dispatch scroll position constantly */
        if (this.options.listenScroll && this.options.probeType === 3) {
J
JiZhi 已提交
215 216
          this.slide.on('scroll', this._onScroll)
        }
217 218
        const slideEl = this.$refs.slide
        slideEl.removeEventListener('touchend', this._touchEndEvent, false)
D
dolymood 已提交
219
        this._touchEndEvent = () => {
M
init  
miaodian 已提交
220 221 222
          if (this.autoPlay) {
            this._play()
          }
D
dolymood 已提交
223
        }
224
        slideEl.addEventListener('touchend', this._touchEndEvent, false)
M
init  
miaodian 已提交
225 226 227 228 229 230 231 232

        this.slide.on('beforeScrollStart', () => {
          if (this.autoPlay) {
            clearTimeout(this._timer)
          }
        })
      },
      _onScrollEnd() {
A
Amy 已提交
233 234
        const { pageX, pageY } = this.slide.getCurrentPage()
        let pageIndex = this.direction === DIRECTION_H ? pageX : pageY
M
init  
miaodian 已提交
235 236
        if (this.currentPageIndex !== pageIndex) {
          this.currentPageIndex = pageIndex
A
Amy 已提交
237
          this.$emit(EVENT_CHANGE, pageIndex)
M
init  
miaodian 已提交
238 239
        }

A
Amy 已提交
240 241
        this.$emit(EVENT_SCROLL_END, pageIndex)

M
init  
miaodian 已提交
242 243 244 245
        if (this.autoPlay) {
          this._play()
        }
      },
J
JiZhi 已提交
246 247 248
      _onScroll(pos) {
        this.$emit(EVENT_SCROLL, pos)
      },
M
init  
miaodian 已提交
249 250 251 252 253 254
      _initDots() {
        this.dots = new Array(this.children.length)
      },
      _play() {
        clearTimeout(this._timer)
        this._timer = setTimeout(() => {
D
dolymood 已提交
255
          this.slide.next()
M
init  
miaodian 已提交
256 257 258 259 260 261
        }, this.interval)
      },
      _deactivated() {
        clearTimeout(this._timer)
        clearTimeout(this._resizeTimer)
        window.removeEventListener('resize', this._resizeHandler)
262 263 264 265
        const slideEl = this.$refs.slide
        if (slideEl) {
          slideEl.removeEventListener('touchend', this._touchEndEvent, false)
        }
M
init  
miaodian 已提交
266 267
      },
      _resizeHandler() {
268
        /* istanbul ignore if */
M
init  
miaodian 已提交
269 270 271 272 273
        if (!this.slide) {
          return
        }
        clearTimeout(this._resizeTimer)
        this._resizeTimer = setTimeout(() => {
274
          /* istanbul ignore if */
M
init  
miaodian 已提交
275 276 277 278 279 280 281
          if (this.slide.isInTransition) {
            this._onScrollEnd()
          } else {
            if (this.autoPlay) {
              this._play()
            }
          }
D
dolymood 已提交
282
          this._refresh()
M
init  
miaodian 已提交
283 284 285 286
        }, 60)
      }
    },
    mounted() {
287 288 289
      this.$nextTick(() => {
        this.refresh()
      })
M
init  
miaodian 已提交
290 291 292 293

      window.addEventListener('resize', this._resizeHandler)
    },
    activated() {
294
      /* istanbul ignore next */
M
init  
miaodian 已提交
295 296 297 298 299 300
      if (this.autoPlay) {
        this._play()
      }
      window.addEventListener('resize', this._resizeHandler)
    },
    deactivated() {
301
      /* istanbul ignore next */
M
init  
miaodian 已提交
302 303 304 305
      this._deactivated()
    },
    destroyed() {
      this._deactivated()
A
Amy 已提交
306 307
      this._destroy()
      this.slide = null
D
dolymood 已提交
308 309 310 311 312

      this._dataWatchers.forEach((cancalWatcher) => {
        cancalWatcher()
      })
      this._dataWatchers = null
D
dolymood 已提交
313 314 315
    },
    components: {
      CubeSlideItem
M
init  
miaodian 已提交
316 317 318 319 320
    }
  }
</script>

<style lang="stylus" rel="stylesheet/stylus">
D
dolymood 已提交
321
  @require "../../common/stylus/variable.styl"
M
init  
miaodian 已提交
322
  .cube-slide
A
AmyFoxFN 已提交
323
    position: relative
M
init  
miaodian 已提交
324
    min-height: 1px
A
AmyFoxFN 已提交
325
    height: 100%
326
    overflow: hidden
M
init  
miaodian 已提交
327 328 329

  .cube-slide-group
    position: relative
A
AmyFoxFN 已提交
330
    height: 100%
M
init  
miaodian 已提交
331 332 333 334 335 336 337 338 339
    overflow: hidden
    white-space: nowrap

  .cube-slide-dots
    position: absolute
    bottom: 2px
    right: 0
    left: 0
    padding: 0 6px
A
AmyFoxFN 已提交
340
    font-size: 0
M
init  
miaodian 已提交
341 342 343 344
    text-align: center
    transform: translateZ(1px)
    > span
      display: inline-block
D
dolymood 已提交
345
      vertical-align: bottom
M
init  
miaodian 已提交
346 347 348 349 350 351 352
      margin: 0 1px
      width: 10px
      height: 1px
      background: $slide-dot-bgc
      &.active
        background: $slide-dot-active-bgc
</style>