UNPKG

saven

Version:
416 lines (378 loc) 11.8 kB
import Nerv from 'nervjs' import classNames from 'classnames' import { throttle } from '../../../utils/index' import './style/index.scss' class SwiperItem extends Nerv.Component { render () { return '' } } class Swiper extends Nerv.Component { constructor () { super(...arguments) this.state = { current: this.props.current } this.isSliding = false this.SwiperTimer = null } initSwiper (nextProps) { this.autoplay() this.bindEvent() this.setSwiperWpStyle(this.getSwiperStyle(false, parseInt(this.state.current) + 1, nextProps)) } getSwiperStyle (isAni, idx, props) { const {duration, easeType, children} = props || this.props const showCount = children.length + 2 const wrapperStyle = { width: `${showCount * 100}%`, WebkitTransition: isAni ? `webkitTransform ${duration}ms ${easeType}` : 'none', transition: isAni ? `transform ${duration}ms ${easeType}` : 'none', WebkitTransform: `translate3d(${-100 / showCount * idx}%, 0, 0)`, transiform: `translate3d(${-100 / showCount * idx}%, 0, 0)` } return wrapperStyle } setSwiperWpStyle (wrapperStyle) { for (let i in wrapperStyle) { this.SwiperWp.style[i] = wrapperStyle[i] } } // 判断左滑还是右滑 getSwiperDirection (e) { // 禁止快速滑动 if (this.isSliding) return let touchEndX = e.changedTouches[0].pageX let touchEndY = e.changedTouches[0].pageY let offsetMoveX = Math.abs(touchEndX - this.touchStartX) let offsetMoveY = Math.abs(touchEndY - this.touchStartY) if (offsetMoveY > offsetMoveX) { this.autoplay() return } // 根据方向进行滑动 if (this.SwiperDirection === 'pre') { this.slidePre() } else if (this.SwiperDirection === 'next') { this.slideNext('touch') } this.autoplay() } removeEvent () { this.SwiperWp.removeEventListener('touchstart', this.touchstartFn.bind(this)) this.SwiperWp.removeEventListener('touchmove', this.touchMoveFn, false) this.SwiperWp.removeEventListener('touchend', this.touchendFn) } isPreventDefault (e) { // 左右滑动时禁止滑动页面 let offsetMoveX = e.targetTouches[0].pageX - this.touchStartX let offsetMoveY = e.targetTouches[0].pageY - this.touchStartY if (Math.abs(offsetMoveX) > Math.abs(offsetMoveY)) { e.preventDefault() // 同时判断y属性,小于某个值才认为是滑动 if (offsetMoveX > this.SwiperGap) { this.SwiperDirection = 'pre' } else if (-offsetMoveX > this.SwiperGap) { this.SwiperDirection = 'next' } else { this.SwiperDirection = 'none' } } } bindEvent () { const { slideMult } = this.props let SwiperWidth = this.child && this.child.getBoundingClientRect().width this.SwiperGap = (SwiperWidth || 0) / slideMult this.touchstartFn = function (e) { this.SwiperDirection = 'none' this.pauseAutoPlay() this.touchStartX = e.changedTouches[0].pageX this.touchStartY = e.changedTouches[0].pageY }.bind(this) this.touchendFn = this.getSwiperDirection.bind(this) this.touchMoveFn = throttle(this.isPreventDefault.bind(this), 150) this.SwiperWp.addEventListener('touchstart', this.touchstartFn, false) this.SwiperWp.addEventListener('touchmove', this.touchMoveFn) this.SwiperWp.addEventListener('touchend', this.touchendFn, false) } // 滑动到某个idx的item中 SwiperTo (realIdx, type) { const {duration, children, circular, autoplay} = this.props const SwiperCount = children.length const SwiperIdx = realIdx + 1 this.isSliding = true if (realIdx >= SwiperCount && circular) { // 切换到下一张 this.setSwiperWpStyle(this.getSwiperStyle(true, SwiperIdx)) this.setState({current: 0}, () => { // 回复原位 setTimeout(() => { this.setSwiperWpStyle(this.getSwiperStyle(false, 1)) this.isSliding = false let e = {} Object.defineProperty(e, 'current', { enumerable: true, value: this.state.current + 1 }) Object.defineProperty(e, 'source', { enumerable: true, value: type }) this.props.onChange && this.props.onChange(e) }, duration) }) } else if (realIdx <= -1 && circular) { // 切换到下一张 this.setSwiperWpStyle(this.getSwiperStyle(true, SwiperIdx)) this.setState({current: SwiperCount - 1}, () => { setTimeout(() => { // 回复原位 this.setSwiperWpStyle(this.getSwiperStyle(false, SwiperCount)) this.isSliding = false let e = {} Object.defineProperty(e, 'current', { enumerable: true, value: this.state.current + 1 }) Object.defineProperty(e, 'source', { enumerable: true, value: type }) this.props.onChange && this.props.onChange(e) }, duration) }) } else if (realIdx > -1 && realIdx < SwiperCount) { // 切换到下一张 this.setSwiperWpStyle(this.getSwiperStyle(true, SwiperIdx)) this.setState({current: realIdx}, () => { setTimeout(() => { this.isSliding = false let e = {} Object.defineProperty(e, 'current', { enumerable: true, value: realIdx + 1 }) Object.defineProperty(e, 'source', { enumerable: true, value: type }) this.props.onChange && this.props.onChange(e) }, duration) }) } else if (realIdx >= SwiperCount || realIdx < 0) { if (autoplay) { const cur = realIdx >= SwiperCount ? 1 : SwiperCount this.setSwiperWpStyle(this.getSwiperStyle(true, cur)) this.setState({current: cur - 1}, () => { setTimeout(() => { this.isSliding = false let e = {} Object.defineProperty(e, 'current', { enumerable: true, value: cur }) Object.defineProperty(e, 'source', { enumerable: true, value: type }) this.props.onChange && this.props.onChange(e) }, duration) }) } } else { this.isSliding = false } } slideNext (type) { let idx = this.state.current const {children, circular, autoplay} = this.props const SwiperCount = children.length if (idx >= SwiperCount - 1 && !circular && !autoplay) { return } idx++ this.SwiperTo(idx, type) } slidePre () { let idx = this.state.current const {circular, autoplay} = this.props if (idx <= 0 && !circular && !autoplay) { return } idx-- this.SwiperTo(idx, 'touch') } autoplay () { const {interval, autoplay} = this.props this.SwiperTimer = null if (!autoplay) return this.SwiperTimer = setInterval(() => { this.slideNext('aotuplay') }, interval) } // 停止自动轮播 pauseAutoPlay () { clearInterval(this.SwiperTimer) } cloneChild (child, props) { const clone = Nerv.cloneElement(child, props) if (!clone) { return clone } if (Array.isArray(clone.children)) { const children = clone.children.map(child => { return this.cloneChild(child, child.props) }) clone.children = children } else if (clone.children) { clone.children = this.cloneChild(clone.children, clone.children.props) } if (clone.props && clone.props.children) { if (Array.isArray(clone.props.children)) { const children = clone.props.children.map(child => { return this.cloneChild(child, child.props) }) clone.props.children = children } else if (clone.props.children) { clone.props.children = this.cloneChild( clone.props.children, clone.props.children.props ) } } return clone } renderSwipers () { const {children} = this.props const SwiperCount = children.length const showCount = children.length + 2 let Swipers = [] let preSwiper = [] let lastSwiper = [] children.forEach((child, idx) => { child.children = child.props.children child.type = 'div' child.props.class = 'swiper_item' let dom1 = { 'data-index': idx, style: { width: `${100 / showCount}%` }, ref: (childDom) => { if (!this.child) this.child = childDom } } const res = Object.assign( dom1, child.props ) const cloneChild = this.cloneChild(child, res) Swipers.push(cloneChild) let dom2 = { 'data-index': SwiperCount, style: { width: `${100 / showCount}%` } } const res1 = Object.assign( dom2, child.props ) // 复制最后一个 if (idx === 0) { lastSwiper.push(this.cloneChild(child, res1)) } let dom3 = { 'data-index': -1, style: { width: `${100 / showCount}%` } } const res2 = Object.assign( dom3, child.props ) // 复制第一个 if (idx === SwiperCount - 1) { preSwiper.push(this.cloneChild(child, res2)) } }) return preSwiper.concat(Swipers, lastSwiper) } componentDidMount () { this.initSwiper() } componentWillReceiveProps (nextProps) { const {interval, autoplay} = nextProps this.pauseAutoPlay() if (!autoplay) return this.SwiperTimer = setInterval(() => { this.slideNext('aotuplay') }, interval) } componentWillUnmount () { this.pauseAutoPlay() this.removeEvent() } render () { const { wrapperClass, children, indicatorClass, indicatorItemClass, indicatorDots, indicatorColor, indicatorActiveColor, className } = this.props const cls = classNames('swiper', className) const {current} = this.state const SwiperCount = children.length const Swipers = this.renderSwipers() return <div className={wrapperClass}> <div className={cls} ref={(SwiperWp) => { this.SwiperWp = SwiperWp }}> <SwiperItem /> {Swipers} </div> {indicatorDots ? <IndicatorDots SwiperCount={SwiperCount} activeIdx={current} indicatorClass={indicatorClass} indicatorItemClass={indicatorItemClass} indicatorColor={indicatorColor} indicatorActiveColor={indicatorActiveColor} /> : null} </div> } } // 默认配置 Swiper.defaultProps = { wrapperClass: 'swiper_wrapper', indicatorClass: 'swiper_indicators', indicatorItemClass: 'swiper_indicators_item', easeType: 'ease-in-out', autoplay: false, interval: 5000, indicatorDots: false, duration: 500, indicatorColor: 'rgba(0, 0, 0, .3)', indicatorActiveColor: '#000', slideMult: 5, current: 0, circular: false } class IndicatorDots extends Nerv.Component { render () { const {activeIdx, SwiperCount, indicatorClass, indicatorItemClass, indicatorColor, indicatorActiveColor} = this.props const indicatorsArr = Array.apply(null, {length: SwiperCount}) const activeColor = `background: ${indicatorActiveColor}` const color = `background: ${indicatorColor}` return <div className={indicatorClass}> {indicatorsArr.map((item, idx) => { return <i className={classNames(indicatorItemClass, {'active': idx === activeIdx})} key={idx} style={idx === activeIdx ? activeColor : color} /> })} </div> } } export {Swiper, SwiperItem}