import { getStyleStr } from '../../../utils/style.js'; // #ifdef APP-NVUE const animation = uni.requireNativePlugin('animation') // #endif export default { data() { return { timingFunction: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)', isAnimated: false, isScrolling: false, customDuration: 0, left: 0, mCurrent: this.current }; }, created() {}, mounted() { // #ifdef MP this.swiperViews = this.$children // #endif // #ifdef APP-PLUS || H5 this.swiperViews = this.$slots.default.map(it => it.child) // #endif this._setLeft(); this.mCurrent = this.current this._notifyCurrentForItems(this.current, this.position) }, watch: { mCurrent() { let current = this.mCurrent if (this.circular) { if (this.position == 1) { current = this.actualSize - (this.plus - 1) // console.log('最前了', current) } else if (this.position == this._size - 2) { current = this.plus - 2; // console.log('最后了', current) } if (current < 0) { current = this.position + 1 } current %= this.actualSize } // console.log('position', this.position, current) this.$emit('update:current', current) this.$emit('change', current) this._notifyCurrentForItems(current, this.position) } }, computed: { is3D() { return this.mode == '3d' }, position() { return this.circular ? (this.mCurrent + this.plus) : this.mCurrent }, manualDuration() { if (this.customDuration > 0) return this.customDuration return this.isAnimated ? this.duration : 0 }, boxStyle() { return getStyleStr({ width: this.width + 'rpx', height: this.height + 'rpx' }); }, containerStyle() { const style = { height: this.height + 'rpx' }; // #ifdef APP-NVUE // FIXME: 理论isAnimated=false应该不设置transform,但是ios有个奇怪的问题,top不为0导致布局错位 const isIOS = uni.getSystemInfoSync().platform == 'ios' if (isIOS) { style.transform = 'translate(' + uni.upx2px(this.left) + 'px' + ',0px)' } else { if (this.isAnimated == false) { style.transform = 'translate(' + uni.upx2px(this.left) + 'px' + ',0px)' } } // #endif // #ifndef APP-NVUE style.left = this.left + 'rpx' style.transition = 'left ' + this.manualDuration + 'ms ' + this.timingFunction // #endif return getStyleStr(style); }, _size() { return (this.$slots && this.$slots.default && this.$slots.default.length) || this.size; }, // plus * 2 plusSize() { return this.circular ? this.plus * 2 : 0; }, // 真实长度 actualSize() { return this._size - this.plusSize; } }, methods: { prev() { if (this.isAnimated) return; if (this.isScrolling) return; if (this.mCurrent == 0 && this.circular == false) return this.mCurrent--; this._run() }, next() { if (this.isAnimated) return; if (this.isScrolling) return; if (this.circular == true) { this.mCurrent++; if (this.mCurrent == this._size) { this.mCurrent = 0; } } else { if (this.mCurrent == this._size - 1) return this.mCurrent++; } this._run() }, moveTo(e) { if (this.isAnimated) return const { deltaX, left } = e this.isScrolling = true if (!this.circular) { if ( // 第一项,不能向右滑(上一项) (deltaX > 0 && this.mCurrent == 0) || // 最后一项,不能向左滑(下一项) (deltaX < 0 && this.mCurrent == this._size - 1) ) { if (!this.bounce) return // 添加阻尼滑动 const _left = this._left || this.wxsData.left this.left = _left + (deltaX * (1 - Math.abs(deltaX) * 3 / (this.width * 5))) this._set3DScale(deltaX) return } } this.left = left this._set3DScale(deltaX) }, moveEnd(e) { const { velocity, deltaX, deltaY } = e this.isScrolling = false if (!this.circular) { // 第一项,不能向右滑(上一项) if (deltaX > 0 && this.mCurrent == 0) { this._restoreStartTouch() return } // 最后一项,不能向左滑(下一项) if (deltaX < 0 && this.mCurrent == this._size - 1) { this._restoreStartTouch() return } } const isTurnPage = Math.abs(deltaX) > this.itemWidth / 2 if (isTurnPage || velocity > 0.2) { if (deltaX < 0) { this.customDuration = 350 this.next() } else if (deltaX > 0) { this.customDuration = 350 this.prev() } } else { this._restoreStartTouch() } }, _set3DScale(deltaX) { if (this.is3D) { const min = Math.min const maxScale = Math.abs(this.scale - 1) const mScale = deltaX * maxScale / this.width const mRealScale = min(this.scale, this.scale - Math.abs(mScale)) this.swiperViews[this.position].mScale = mRealScale < 1 ? 1 : mRealScale if (this.position - 1 > -1) { this.swiperViews[this.position - 1].mScale = mScale > 0 ? min(this.scale, 1 + mScale) : min(1, 1 + mScale) } if (this.position + 1 < this._size) { this.swiperViews[this.position + 1].mScale = mScale > 0 ? min(1, 1 - mScale) : min(this.scale, 1 - mScale) } } }, _restoreStartTouch() { const self = this this.customDuration = 300 // #ifdef APP-VUE || MP-WEIXIN || H5 this.left = this.wxsData.left // #endif // #ifndef APP-PLUS || MP-WEIXIN || H5 this.left = this._left // #endif this._run(false) if (this.is3D) { this.swiperViews[this.position].restoreScale(this.manualDuration) if (this.position - 1 > -1) { this.swiperViews[this.position - 1].restoreScale(this.manualDuration) } if (this.position + 1 < this._size) { this.swiperViews[this.position + 1].restoreScale(this.manualDuration) } } }, _notifyCurrentForItems(current, position) { this.swiperViews && this.swiperViews.forEach(it => { it.current = current it.position = position }) }, _run(isSet = true) { this.isAnimated = true; if (isSet) this._setLeft(); const self = this; if (this.is3D) { this.swiperViews[this.position].restoreToScale(this.scale, this.manualDuration) if (this.position - 1 > -1) { this.swiperViews[this.position - 1].restoreToScale(1, this.manualDuration) } if (this.position + 1 < this._size) { this.swiperViews[this.position + 1].restoreToScale(1, this.manualDuration) } } // #ifdef APP-NVUE animation.transition(this.$refs.container, { styles: { transform: 'translate(' + uni.upx2px(this.left) + 'px' + ',0px)', }, duration: this.manualDuration, //ms timingFunction: this.timingFunction, needLayout: false, delay: 0 //ms }, function() { self._reset(); }) // #endif // #ifndef APP-NVUE setTimeout(() => { this._reset(); }, this.manualDuration); // #endif }, _setLeft() { if (this.circular == true) { const s1 = (this.width - this.itemWidth - this.space * 2) / 2; let left = (this.plus + this.mCurrent) * (this.space + this.itemWidth) - s1; this.left = -left; } else { this.left = -(this.itemWidth + this.space) * this.mCurrent } // #ifdef APP-VUE || MP-WEIXIN || H5 this.wxsData = { left: this.left, bounce: this.bounce } // #endif }, _reset() { this.isScrolling = false this.isAnimated = false this.customDuration = 0 if (this.circular == true) { if (this.position == 1) { this.mCurrent = this.actualSize - (this.plus - 1); this._setLeft(); this._restoreScale() } // -2:原数组索引-1 + plus数组索引-1 if (this.position == this._size - 2) { this.mCurrent = this.plus - 2; this._setLeft(); this._restoreScale() } } }, _restoreScale() { if (this.is3D) { this.swiperViews[this.position].restoreToScale(this.scale, 0) if (this.position - 1 > -1) { this.swiperViews[this.position - 1].restoreToScale(1, 0) } if (this.position + 1 < this._size) { this.swiperViews[this.position + 1].restoreToScale(1, 0) } } } } }