slide.vue 7.9 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>
5 6
        <cube-slide-item v-for="(item, index) in data" :key="index" @click.native="clickItem(item, index)"
                         :item="item"></cube-slide-item>
7
      </slot>
M
init  
miaodian 已提交
8
    </div>
9
    <div class="cube-slide-dots" v-if="showDots">
D
dolymood 已提交
10
      <slot name="dots" :current="currentPageIndex" :dots="dots">
11
        <span :class="{active: currentPageIndex === index}" v-for="(item, index) in dots" :key="index"></span>
D
dolymood 已提交
12
      </slot>
M
init  
miaodian 已提交
13 14 15 16 17
    </div>
  </div>
</template>

<script type="text/ecmascript-6">
D
dolymood 已提交
18
  import CubeSlideItem from './slide-item.vue'
M
init  
miaodian 已提交
19 20 21 22
  import BScroll from 'better-scroll'

  const COMPONENT_NAME = 'cube-slide'
  const EVENT_CHANGE = 'change'
A
AmyFoxFN 已提交
23
  const EVENT_SELECT = 'click'
24 25
  const DIRECTION_H = 'horizontal'
  const DIRECTION_V = 'vertical'
M
init  
miaodian 已提交
26 27 28 29

  export default {
    name: COMPONENT_NAME,
    props: {
30 31 32
      data: {
        type: Array,
        default() {
33
          /* istanbul ignore next */
34 35 36
          return []
        }
      },
D
dolymood 已提交
37 38 39 40
      initialIndex: {
        type: Number,
        default: 0
      },
M
init  
miaodian 已提交
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
      loop: {
        type: Boolean,
        default: true
      },
      autoPlay: {
        type: Boolean,
        default: true
      },
      interval: {
        type: Number,
        default: 4000
      },
      threshold: {
        type: Number,
        default: 0.3
      },
      speed: {
        type: Number,
        default: 400
D
dolymood 已提交
60 61 62 63
      },
      allowVertical: {
        type: Boolean,
        default: false
64 65 66 67 68 69 70 71 72 73 74 75
      },
      stopPropagation: {
        type: Boolean,
        default: false
      },
      direction: {
        type: String,
        default: DIRECTION_H
      },
      showDots: {
        type: Boolean,
        default: true
M
init  
miaodian 已提交
76 77 78 79 80
      }
    },
    data() {
      return {
        dots: 0,
D
dolymood 已提交
81 82 83
        currentPageIndex: this.initialIndex || 0
      }
    },
84 85 86 87
    created() {
      const needRefreshProps = ['data', 'loop', 'autoPlay', 'threshold', 'speed', 'allowVertical']
      needRefreshProps.forEach((key) => {
        this.$watch(key, () => {
88
          /* istanbul ignore next */
89 90 91
          this.$nextTick(() => {
            this.refresh()
          })
92 93 94
        })
      })
    },
D
dolymood 已提交
95 96 97 98 99
    watch: {
      initialIndex(newIndex) {
        if (newIndex !== this.currentPageIndex) {
          this.slide && this.slide.goToPage(newIndex)
        }
M
init  
miaodian 已提交
100 101 102
      }
    },
    methods: {
A
AmyFoxFN 已提交
103
      clickItem(item, index) {
104
        /* istanbul ignore next */
105 106
        this.$emit(EVENT_SELECT, item, index)
      },
M
init  
miaodian 已提交
107
      refresh() {
108 109 110 111
        /* istanbul ignore if */
        if (this.slide === null) {
          return
        }
D
dolymood 已提交
112
        this.slide && this.slide.destroy()
D
dolymood 已提交
113
        clearTimeout(this._timer)
114 115 116 117 118 119

        if (this.slide) {
          this.currentPageIndex = 0
        }
        this._updateSlideDom()
        if (this.showDots) {
D
dolymood 已提交
120
          this._initDots()
121 122
        }
        this._initSlide()
D
dolymood 已提交
123

124 125 126
        if (this.autoPlay) {
          this._play()
        }
D
dolymood 已提交
127 128
      },
      _refresh() {
129
        this._updateSlideDom(true)
M
init  
miaodian 已提交
130 131
        this.slide.refresh()
      },
132 133 134 135 136 137 138
      _updateSlideDom(isResize) {
        if (this.direction === DIRECTION_H) {
          this._setSlideWidth(isResize)
        } else {
          this._setSlideHeight(isResize)
        }
      },
M
init  
miaodian 已提交
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
      _setSlideWidth(isResize) {
        this.children = this.$refs.slideGroup.children

        let width = 0
        let slideWidth = this.$refs.slide.clientWidth
        for (let i = 0; i < this.children.length; i++) {
          let child = this.children[i]
          child.style.width = slideWidth + 'px'
          width += slideWidth
        }
        if (this.loop && !isResize) {
          width += 2 * slideWidth
        }
        this.$refs.slideGroup.style.width = width + 'px'
      },
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
      _setSlideHeight(isResize) {
        this.children = this.$refs.slideGroup.children

        let height = 0
        let slideHeight = this.$refs.slide.clientHeight
        for (let i = 0; i < this.children.length; i++) {
          let child = this.children[i]
          child.style.height = slideHeight + 'px'
          height += slideHeight
        }
        if (this.loop && !isResize) {
          height += 2 * slideHeight
        }
        this.$refs.slideGroup.style.height = height + 'px'
      },
M
init  
miaodian 已提交
169
      _initSlide() {
170
        const eventPassthrough = this.direction === DIRECTION_H && this.allowVertical ? DIRECTION_V : ''
M
init  
miaodian 已提交
171
        this.slide = new BScroll(this.$refs.slide, {
172 173
          scrollX: this.direction === DIRECTION_H,
          scrollY: this.direction === DIRECTION_V,
M
init  
miaodian 已提交
174
          momentum: false,
175
          bounce: false,
176
          eventPassthrough,
M
init  
miaodian 已提交
177 178 179 180 181
          snap: {
            loop: this.loop,
            threshold: this.threshold,
            speed: this.speed
          },
182
          stopPropagation: this.stopPropagation,
D
dolymood 已提交
183 184
          click: true,
          observeDOM: false
M
init  
miaodian 已提交
185 186
        })

D
dolymood 已提交
187 188
        this.slide.goToPage(this.currentPageIndex, 0, 0)

M
init  
miaodian 已提交
189 190
        this.slide.on('scrollEnd', this._onScrollEnd)

191 192
        const slideEl = this.$refs.slide
        slideEl.removeEventListener('touchend', this._touchEndEvent, false)
D
dolymood 已提交
193
        this._touchEndEvent = () => {
M
init  
miaodian 已提交
194 195 196
          if (this.autoPlay) {
            this._play()
          }
D
dolymood 已提交
197
        }
198
        slideEl.addEventListener('touchend', this._touchEndEvent, false)
M
init  
miaodian 已提交
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222

        this.slide.on('beforeScrollStart', () => {
          if (this.autoPlay) {
            clearTimeout(this._timer)
          }
        })
      },
      _onScrollEnd() {
        let pageIndex = this.slide.getCurrentPage().pageX
        if (this.currentPageIndex !== pageIndex) {
          this.currentPageIndex = pageIndex
          this.$emit(EVENT_CHANGE, this.currentPageIndex)
        }

        if (this.autoPlay) {
          this._play()
        }
      },
      _initDots() {
        this.dots = new Array(this.children.length)
      },
      _play() {
        clearTimeout(this._timer)
        this._timer = setTimeout(() => {
D
dolymood 已提交
223
          this.slide.next()
M
init  
miaodian 已提交
224 225 226 227 228 229
        }, this.interval)
      },
      _deactivated() {
        clearTimeout(this._timer)
        clearTimeout(this._resizeTimer)
        window.removeEventListener('resize', this._resizeHandler)
230 231 232 233
        const slideEl = this.$refs.slide
        if (slideEl) {
          slideEl.removeEventListener('touchend', this._touchEndEvent, false)
        }
M
init  
miaodian 已提交
234 235
      },
      _resizeHandler() {
236
        /* istanbul ignore if */
M
init  
miaodian 已提交
237 238 239 240 241
        if (!this.slide) {
          return
        }
        clearTimeout(this._resizeTimer)
        this._resizeTimer = setTimeout(() => {
242
          /* istanbul ignore if */
M
init  
miaodian 已提交
243 244 245 246 247 248 249
          if (this.slide.isInTransition) {
            this._onScrollEnd()
          } else {
            if (this.autoPlay) {
              this._play()
            }
          }
D
dolymood 已提交
250
          this._refresh()
M
init  
miaodian 已提交
251 252 253 254
        }, 60)
      }
    },
    mounted() {
255 256 257
      this.$nextTick(() => {
        this.refresh()
      })
M
init  
miaodian 已提交
258 259 260 261

      window.addEventListener('resize', this._resizeHandler)
    },
    activated() {
262
      /* istanbul ignore next */
M
init  
miaodian 已提交
263 264 265 266 267 268
      if (this.autoPlay) {
        this._play()
      }
      window.addEventListener('resize', this._resizeHandler)
    },
    deactivated() {
269
      /* istanbul ignore next */
M
init  
miaodian 已提交
270 271 272 273
      this._deactivated()
    },
    destroyed() {
      this._deactivated()
274 275 276 277
      if (this.slide) {
        this.slide.destroy()
        this.slide = null
      }
D
dolymood 已提交
278 279 280
    },
    components: {
      CubeSlideItem
M
init  
miaodian 已提交
281 282 283 284 285
    }
  }
</script>

<style lang="stylus" rel="stylesheet/stylus">
D
dolymood 已提交
286
  @require "../../common/stylus/variable.styl"
M
init  
miaodian 已提交
287
  .cube-slide
A
AmyFoxFN 已提交
288
    position: relative
M
init  
miaodian 已提交
289
    min-height: 1px
A
AmyFoxFN 已提交
290
    height: 100%
M
init  
miaodian 已提交
291 292 293

  .cube-slide-group
    position: relative
A
AmyFoxFN 已提交
294
    height: 100%
M
init  
miaodian 已提交
295 296 297 298 299 300 301 302 303
    overflow: hidden
    white-space: nowrap

  .cube-slide-dots
    position: absolute
    bottom: 2px
    right: 0
    left: 0
    padding: 0 6px
A
AmyFoxFN 已提交
304
    font-size: 0
M
init  
miaodian 已提交
305 306 307 308
    text-align: center
    transform: translateZ(1px)
    > span
      display: inline-block
D
dolymood 已提交
309
      vertical-align: bottom
M
init  
miaodian 已提交
310 311 312 313 314 315 316
      margin: 0 1px
      width: 10px
      height: 1px
      background: $slide-dot-bgc
      &.active
        background: $slide-dot-active-bgc
</style>