UNPKG

v-swiper

Version:

vue2.0,轻量,可配置,基于css3 transition的轮播组件

377 lines (345 loc) 12 kB
const DEFAULT_OPTIONS = { currentIndex: 0, width: 0, slideDerection: '', list: [], interval: 4000, auto: false, loop: false, direction: 'horizontal', showDots: false, dotsPosition: 'center', duration: 500, minMovingDistance: 30 } let uid = 0 class Swiper { constructor(props) { const opt = Object.assign({}, DEFAULT_OPTIONS, props) const { currentIndex, width, slideDerection, list, wrap, duration, interval, auto, loop, direction, minMovingDistance } = opt this.currentIndex = currentIndex this.width = width this.slideDerection = slideDerection this._touchStartInfo = {} this._touchMoveInfo = {} this._touchEndInfo = {} this.isTouchMoving = false this.list = list this.wrap = wrap this.duration = duration this._eventHandler = {} this.interval = interval this.auto = auto this.loop = loop this.minMovingDistance = minMovingDistance this.direction = direction this.onlyOne = this.list.length === 1 this.uid = uid += 1 this.destoryed = false this.transitionEvent = this.checkTransitionEvent() if (!this.list.length) { return } this._init() this._auto() this._bindEvent() } _init() { this._updateWidth() this.getSwiperItem() this.setOffset() this.setInitTransform() } _auto() { this._stop() if (this.auto && !this.destoryed && this.list.length > 1) { this.timer = setTimeout(() => { this.next() }, this.interval) } } _stop() { this.timer && clearTimeout(this.timer) } _bindEvent() { const swiperItem = this.getSwiperItem() const swiper = this.getSwiper() this.bindTransitionEndHandler = this.transitionEndHandler.bind(this) this.bindHandleTouchStart = this.handleTouchStart.bind(this) this.bindHandleTouchMove = this.handleTouchMove.bind(this) this.bindHandleTouchEnd = this.handleTouchEnd.bind(this) this.bindHandleTouchResize = this.handleTouchResize.bind(this) this.bindHandlerVisibilitychange = this.handleVisibilitychange.bind(this) swiperItem[0] && swiperItem[0].addEventListener( this.transitionEvent, this.bindTransitionEndHandler, false ) swiper.addEventListener('touchstart', this.bindHandleTouchStart, false) swiper.addEventListener('touchmove', this.bindHandleTouchMove, false) swiper.addEventListener('touchend', this.bindHandleTouchEnd, false) window.addEventListener('orientationchange', this.bindHandleTouchResize, false) document.addEventListener('visibilitychange', this.bindHandlerVisibilitychange, false) } _unbindEvent() { const swiper = this.getSwiper() this.forItems((item) => { item.removeEventListener(this.transitionEvent, this.bindTransitionEndHandler, false) }) swiper.removeEventListener('touchstart', this.bindHandleTouchStart, false) swiper.removeEventListener('touchmove', this.bindHandleTouchMove, false) swiper.removeEventListener('touchend', this.bindHandleTouchEnd, false) window.removeEventListener('orientationchange', this.bindHandleTouchResize.bind(this), false) document.removeEventListener('visibilitychange', this.bindHandlerVisibilitychange, false) } on(event, callback) { if (this._eventHandler[event]) { console.error('event不能重复注册') } if (typeof callback !== 'function') { console.error('callback需要是function') } this._eventHandler[event] = callback return this } _updateWidth() { const swiper = this.getSwiper() this.width = this.direction === 'horizontal' ? swiper.offsetWidth || document.documentElement.offsetWidth : swiper.offsetHeight } next() { this.slideDerection = 'nexted' this.currentIndex += 1 this.go(this.currentIndex) } prev() { this.slideDerection = 'preved' this.currentIndex -= 1 this.go(this.currentIndex) } go(idx) { this._stop() if (idx > this.list.length - 1) { this.currentIndex = 0 } if (idx < 0) { this.currentIndex = this.list.length - 1 } // 设置切换transform,同时设置this.duration,开启过度动画 this.forItems((item, key) => { this.setTransition(item, this.duration) let distance = this.slideDerection === 'nexted' ? this._offset[key] - this.width : this._offset[key] + this.width if (!this.loop && this.auto && this.currentIndex === 0) { distance = key * this.width } this.setTransform(item, distance) }) this._auto() } setDom(opration, node, parentNode, brotherNode) { // parentNode.insertBefore(newNode, referenceNode) // node.removeChild(child) // node.appendChild(child) switch (opration) { case 'remove': parentNode.removeChild(node) break case 'append': parentNode.appendChild(node) break case 'insertBefore': parentNode.insertBefore(node, brotherNode) break default: console.log('无匹配操作') } return node } transitionEndHandler(e) { // 如果开启无缝轮播,transition动画结束之后,调整元素位置 if (this.loop) { let swiperItem = this.getSwiperItem() const { parentNode } = swiperItem[0] const temp = this.slideDerection === 'nexted' ? this.setDom('remove', swiperItem[0], parentNode) : this.setDom('remove', swiperItem[swiperItem.length - 1], parentNode) swiperItem = this.getSwiperItem() this.slideDerection === 'nexted' ? this.setDom('append', temp, parentNode) : this.setDom('insertBefore', temp, parentNode, swiperItem[0]) } // 调整轮播元素transform,this.transtion = 0不设置动画 this.forItems((item, key) => { this.setTransition(item) let distance = this._offset[key] if (!this.loop) { distance = this.slideDerection === 'nexted' ? distance - this.width : distance + this.width if (this.auto && this.currentIndex === 0) { distance = key * this.width } } this.setTransform(item, distance) }) this.setOffset() // transition结束监听 this._eventHandler.swiperEnd && this._eventHandler.swiperEnd.call(this, this.currentIndex) e.preventDefault() } checkTransitionEvent() { const el = document.createElement('div') const transitions = { transition: 'transitionend', OTransition: 'oTransitionEnd', MozTransition: 'transitionend', WebkitTransition: 'webkitTransitionEnd' } for (let t in transitions) { if (el.style[t] !== undefined) { return transitions[t] } } } handleTouchStart(e) { const touches = e.targetTouches[0] this._touchStartInfo = { pageX: touches.pageX, pageY: touches.pageY } } handleTouchMove(e) { this.isTouchMoving = true const touches = e.targetTouches[0] this._touchMoveInfo = { pageX: touches.pageX, pageY: touches.pageY } const moveDistanceX = this._touchMoveInfo.pageX - this._touchStartInfo.pageX const moveDistanceY = this._touchMoveInfo.pageY - this._touchStartInfo.pageY const moveDistance = this.direction === 'horizontal' ? moveDistanceX : moveDistanceY // 设置移动距离 this.forItems((item, index) => { this.setTransition(item) const distance = parseInt(item.dataset.offset, 10) + moveDistance this.setTransform(item, distance) }) } handleTouchEnd(e) { this.isTouchMoving = false const touches = e.changedTouches[0] this._touchEndInfo = { pageX: touches.pageX, pageY: touches.pageY } const distanceX = this._touchEndInfo.pageX - this._touchStartInfo.pageX const distanceY = this._touchEndInfo.pageY - this._touchStartInfo.pageY const moveDistance = this.direction === 'horizontal' ? distanceX : distanceY // 是否需要切换到上一个or下一个 if ( moveDistance > this.minMovingDistance && (this.loop || (!this.loop && this.currentIndex !== 0)) ) { this.prev() } else if ( moveDistance < -this.minMovingDistance && (this.loop || (!this.loop && this.currentIndex !== this.list.length - 1)) ) { this.next() } else { this.forItems(item => { this.setTransition(item) const distance = parseInt(item.dataset.offset, 10) this.setTransform(item, distance) }) } } handleTouchResize() { setTimeout(() => { this._updateWidth() this.setOffset() this.setInitTransform() }, 100) } handleVisibilitychange() { if (document.visibilityState === "visible") { this.auto && this._auto() } else { this.auto && this._stop() } } setTransition(item, duration = 0) { if (this.onlyOne) return const transition = `all ${duration}ms ease 0s` item.style.webkitTransition = transition item.style.transition = transition } setTransform(item, distance) { if (this.onlyOne) return const transform = this.direction === 'horizontal' ? `translate3d(${distance}px, 0, 0)` : `translate3d(0, ${distance}px, 0)` item.style.transform = transform } setOffset() { // 0 750 1500 2250 // -750 0 750 1500 // -1500 -750 0 750 // -2250 -1500 -750 0 // 设置元素的data-offset属性 const temp = this.list.map((item, index) => { return this.loop ? -this.width * (1 - index) : this.width * (index - this.currentIndex) }) this._offset = temp || [] const swiperItem = this.getSwiperItem() this._offset.forEach((offset, index) => { swiperItem[index].setAttribute('data-offset', offset) }) } getSwiperItem() { const children = this.wrap.querySelectorAll(`.slide-item`) || [] return Array.from(children) } getSwiper() { return this.wrap || {} } forItems(fn) { const itemEles = this.getSwiperItem() itemEles.forEach(fn) } setInitTransform() { const itemEles = this.getSwiperItem() itemEles.forEach((item, key) => { if (this.onlyOne) return const distance = this._offset[key] this.setTransform(item, distance) }) } destory() { this.destoryed = true this.list.length = 0 this._stop() this._unbindEvent() this.currentIndex = 0 } } export default Swiper