| @ -1,251 +0,0 @@ | |||||
| <template> | |||||
| <view class="swiper-item" ref="swiper_item" :style="itemStyle"> | |||||
| <view class="item-cntent" bubble="true" @click.stop="onClick"><slot></slot></view> | |||||
| </view> | |||||
| </template> | |||||
| <script> | |||||
| import { getStyleStr } from '../../utils/style.js'; | |||||
| // #ifdef APP-NVUE | |||||
| const animation = uni.requireNativePlugin('animation'); | |||||
| // #endif | |||||
| /** | |||||
| * esc-swiper-item | |||||
| * @description esc-swiper-item (不支持使用class) | |||||
| * @property {Number} index 索引(必填) | |||||
| * @property {Boolean} clickAny 可以点击任意项 | |||||
| * @event {Function} click 点击事件 | |||||
| */ | |||||
| export default { | |||||
| name: 'esc-swiper-item', | |||||
| inject: ['config'], | |||||
| props: { | |||||
| index: { | |||||
| type: Number, | |||||
| default: 0 | |||||
| }, | |||||
| clickAny: { | |||||
| type: Boolean, | |||||
| default: false | |||||
| } | |||||
| }, | |||||
| data() { | |||||
| return { | |||||
| isAnimated: false, | |||||
| timingFunction: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)', | |||||
| duration: 0, | |||||
| current: 0, | |||||
| position: 0, | |||||
| mScale: 0, | |||||
| canClick: true | |||||
| }; | |||||
| }, | |||||
| created() {}, | |||||
| computed: { | |||||
| size() { | |||||
| return this.config.size; | |||||
| }, | |||||
| width() { | |||||
| return this.config.width; | |||||
| }, | |||||
| height() { | |||||
| return this.config.height; | |||||
| }, | |||||
| itemWidth() { | |||||
| return this.config.itemWidth; | |||||
| }, | |||||
| itemHeight() { | |||||
| return this.config.itemHeight; | |||||
| }, | |||||
| space() { | |||||
| return this.config.space; | |||||
| }, | |||||
| itemStyle() { | |||||
| if (this.index == this.size - 1) { | |||||
| return this.rightSpaceStyle(); | |||||
| } else if (this.index == this.size - 2) { | |||||
| return this.centerSpaceStyle(); | |||||
| } else { | |||||
| return this.leftSpaceStyle(); | |||||
| } | |||||
| }, | |||||
| scale() { | |||||
| if (!this.config.is3D) { | |||||
| return 1; | |||||
| } | |||||
| if (this.myCurrent == this.current) { | |||||
| return this.mScale || this.config.scale; | |||||
| } else { | |||||
| return this.mScale || 1; | |||||
| } | |||||
| }, | |||||
| // 当前swiper-item所属current索引 | |||||
| myCurrent() { | |||||
| if (!this.config.isCircular) { | |||||
| return this.index; | |||||
| } | |||||
| const p = this.index; | |||||
| const plus = this.config.plus; | |||||
| const actSize = this.size - plus * 2; | |||||
| let current = 0; | |||||
| if (p < plus) { | |||||
| current = p + (actSize - plus); | |||||
| } else if (p >= this.size - plus) { | |||||
| current = p - (actSize + plus); | |||||
| } else { | |||||
| current = p - plus; | |||||
| } | |||||
| return current; | |||||
| } | |||||
| }, | |||||
| methods: { | |||||
| itemSize() { | |||||
| let style = { | |||||
| width: this.itemWidth + 'rpx', | |||||
| height: (this.itemHeight || this.height) + 'rpx' | |||||
| }; | |||||
| if (this.config.is3D) { | |||||
| // #ifndef APP-NVUE | |||||
| if (this.isAnimated) { | |||||
| style.transition = 'transform ' + this.duration + 'ms ' + this.timingFunction; | |||||
| } else { | |||||
| style.transition = ''; | |||||
| } | |||||
| style.transform = 'scale(' + this.scale + ')'; | |||||
| // #endif | |||||
| // #ifdef APP-NVUE | |||||
| const isIOS = uni.getSystemInfoSync().platform == 'ios'; | |||||
| if (isIOS) { | |||||
| style.transform = 'scale(' + this.scale + ')'; | |||||
| } else { | |||||
| if (!this.isAnimated) style.transform = 'scale(' + this.scale + ')'; | |||||
| } | |||||
| // #endif | |||||
| } | |||||
| return style; | |||||
| }, | |||||
| leftSpaceStyle() { | |||||
| return getStyleStr({ | |||||
| ...this.itemSize(), | |||||
| marginLeft: this.space + 'rpx' | |||||
| }); | |||||
| }, | |||||
| centerSpaceStyle() { | |||||
| return getStyleStr({ | |||||
| ...this.itemSize(), | |||||
| marginLeft: this.space + 'rpx', | |||||
| marginRight: this.space + 'rpx' | |||||
| }); | |||||
| }, | |||||
| rightSpaceStyle() { | |||||
| return getStyleStr({ | |||||
| ...this.itemSize(), | |||||
| marginRight: this.space + 'rpx' | |||||
| }); | |||||
| }, | |||||
| onClick(e) { | |||||
| if (!this.canClick) { | |||||
| return; | |||||
| } | |||||
| // #ifdef APP-NVUE | |||||
| e.stopPropagation(); | |||||
| // #endif | |||||
| // 点击任意项 | |||||
| if (this.clickAny) { | |||||
| this.$emit('click', e); | |||||
| return; | |||||
| } | |||||
| // 只能点击当前项 | |||||
| if (this.myCurrent == this.current) { | |||||
| this.$emit('click', e); | |||||
| } | |||||
| }, | |||||
| restoreScale(duration) { | |||||
| if (!this.config.is3D) { | |||||
| return; | |||||
| } | |||||
| // #ifndef APP-NVUE | |||||
| this.duration = duration; | |||||
| this.isAnimated = true; | |||||
| this.mScale = 0; | |||||
| setTimeout(() => { | |||||
| this.duration = 0; | |||||
| this.isAnimated = false; | |||||
| }, duration); | |||||
| // #endif | |||||
| // #ifdef APP-NVUE | |||||
| this.isAnimated = true; | |||||
| this.mScale = 0; | |||||
| animation.transition( | |||||
| this.$refs['swiper_item'].ref, | |||||
| { | |||||
| styles: { | |||||
| transform: 'scale(' + this.scale + ')' | |||||
| }, | |||||
| duration, //ms | |||||
| timingFunction: this.timingFunction, | |||||
| needLayout: false, | |||||
| delay: 0 //ms | |||||
| }, | |||||
| () => { | |||||
| this.isAnimated = false; | |||||
| } | |||||
| ); | |||||
| // #endif | |||||
| }, | |||||
| restoreToScale(scale, duration) { | |||||
| if (!this.config.is3D) { | |||||
| return; | |||||
| } | |||||
| // #ifndef APP-NVUE | |||||
| this.duration = duration; | |||||
| this.isAnimated = true; | |||||
| this.mScale = scale; | |||||
| setTimeout(() => { | |||||
| this.duration = 0; | |||||
| this.isAnimated = false; | |||||
| this.mScale = 0; | |||||
| }, duration); | |||||
| // #endif | |||||
| // #ifdef APP-NVUE | |||||
| this.isAnimated = true; | |||||
| animation.transition( | |||||
| this.$refs['swiper_item'].ref, | |||||
| { | |||||
| styles: { | |||||
| transform: 'scale(' + scale + ')' | |||||
| }, | |||||
| duration, //ms | |||||
| timingFunction: this.timingFunction, | |||||
| needLayout: false, | |||||
| delay: 0 //ms | |||||
| }, | |||||
| () => { | |||||
| this.isAnimated = false; | |||||
| this.mScale = 0; | |||||
| } | |||||
| ); | |||||
| // #endif | |||||
| } | |||||
| } | |||||
| }; | |||||
| </script> | |||||
| <style scoped> | |||||
| .swiper-item { | |||||
| position: relative; | |||||
| } | |||||
| .item-cntent { | |||||
| position: absolute; | |||||
| top: 0; | |||||
| right: 0; | |||||
| bottom: 0; | |||||
| left: 0; | |||||
| /* #ifndef APP-NVUE */ | |||||
| display: flex; | |||||
| /* #endif */ | |||||
| } | |||||
| </style> | |||||
| @ -1,21 +0,0 @@ | |||||
| /** | |||||
| * getSwiperList | |||||
| * @description 获取Swiper数据 | |||||
| * @param {Array} list 原数据 | |||||
| * @param {Object} options 配置 | |||||
| * @param {Boolean} options.circular 是否循环 | |||||
| * @param {Number} options.plus 左右追加个数(开启循环必填,至少为2) | |||||
| * @return {Array} | |||||
| */ | |||||
| export function getSwiperList(list, options = { | |||||
| circular: true, | |||||
| plus: 2 | |||||
| }) { | |||||
| if (!options.circular) { | |||||
| return list | |||||
| } | |||||
| const plus = options.plus || 2 | |||||
| const leftPlusList = [...list].reverse().slice(0, plus).reverse(); | |||||
| const rightPlusList = [...list].slice(0, plus); | |||||
| return [].concat(leftPlusList).concat(list).concat(rightPlusList); | |||||
| } | |||||
| @ -1,198 +0,0 @@ | |||||
| <template> | |||||
| <!-- #ifdef APP-VUE || H5 --> | |||||
| <view class="box" :style="boxStyle"> | |||||
| <view | |||||
| ref="container" | |||||
| class="container" | |||||
| :change:prop="swipe.changeData" | |||||
| :prop="wxsData" | |||||
| @touchstart.stop="swipe.touchstart" | |||||
| @touchmove.stop="swipe.touchmove" | |||||
| @touchend.stop="swipe.touchend" | |||||
| :style="containerStyle" | |||||
| > | |||||
| <slot></slot> | |||||
| </view> | |||||
| </view> | |||||
| <!-- #endif --> | |||||
| <!-- #ifdef MP-WEIXIN --> | |||||
| <view class="box" :style="boxStyle"> | |||||
| <view | |||||
| ref="container" | |||||
| class="container" | |||||
| :change:prop="swipe.changeData" | |||||
| :prop="wxsData" | |||||
| @touchstart="swipe.touchstart" | |||||
| @touchmove="swipe.touchmove" | |||||
| @touchend="swipe.touchend" | |||||
| :style="containerStyle" | |||||
| > | |||||
| <slot></slot> | |||||
| </view> | |||||
| </view> | |||||
| <!-- #endif --> | |||||
| <!-- #ifdef APP-NVUE --> | |||||
| <view class="box" :style="boxStyle"> | |||||
| <view ref="container" class="container" @horizontalpan="touchstart" :style="containerStyle"><slot></slot></view> | |||||
| </view> | |||||
| <!-- #endif --> | |||||
| <!-- 其他平台使用 js ,长列表性能可能会有影响--> | |||||
| <!-- #ifndef APP-PLUS || MP-WEIXIN || H5 --> | |||||
| <view class="box" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend" :style="boxStyle"> | |||||
| <view ref="container" class="container" :style="containerStyle"><slot></slot></view> | |||||
| </view> | |||||
| <!-- #endif --> | |||||
| </template> | |||||
| <script src="./mixins/index.wxs" module="swipe" lang="wxs"></script> | |||||
| <script> | |||||
| import SwiperMixin from './mixins/base.mixin.js'; | |||||
| // #ifdef APP-VUE || MP-WEIXIN || H5 | |||||
| import MpMixin from './mixins/mpwxs.js'; | |||||
| // #endif | |||||
| // #ifdef APP-NVUE | |||||
| import BindingxMixin from './mixins/bindingx.js'; | |||||
| // #endif | |||||
| // #ifndef APP-PLUS || MP-WEIXIN || H5 | |||||
| import OtherMixin from './mixins/mpother.js'; | |||||
| // #endif | |||||
| const mixins = [ | |||||
| // #ifdef APP-VUE || MP-WEIXIN || H5 | |||||
| MpMixin, | |||||
| // #endif | |||||
| // #ifdef APP-NVUE | |||||
| BindingxMixin, | |||||
| // #endif | |||||
| // #ifndef APP-PLUS || MP-WEIXIN || H5 | |||||
| OtherMixin, | |||||
| // #endif | |||||
| SwiperMixin | |||||
| ]; | |||||
| /** | |||||
| * esc-swiper | |||||
| * @description 自定义swiper (不支持使用class) | |||||
| * @property {String} mode = [normal|3d] 模式 | |||||
| * @property {Number} scale 3D模式选中项的scale | |||||
| * @property {Number} width 宽 | |||||
| * @property {Number} height 高 | |||||
| * @property {Number} itemWidth 项宽 | |||||
| * @property {Number} itemHeight 项高 | |||||
| * @property {Number} space 间距 | |||||
| * @property {Number} plus 左右追加个数(开启循环必填,至少为2) | |||||
| * @property {Number} current 选中项索引 | |||||
| * @property {Boolean} autoplay 自动轮播 | |||||
| * @property {Boolean} circular 是否循环,如果开启,至少需要3项 | |||||
| * @property {Boolean} bounce 阻尼效果 | |||||
| * @event {Function} change 索引变化 | |||||
| */ | |||||
| export default { | |||||
| name: 'esc-swiper', | |||||
| mixins, | |||||
| props: { | |||||
| mode: { | |||||
| type: String, | |||||
| default: 'normal' | |||||
| }, | |||||
| scale: Number, | |||||
| width: Number, | |||||
| height: Number, | |||||
| itemWidth: { | |||||
| type: Number, | |||||
| default: 0 | |||||
| }, | |||||
| itemHeight: { | |||||
| type: Number, | |||||
| default: 0 | |||||
| }, | |||||
| space: { | |||||
| type: Number, | |||||
| default: 0 | |||||
| }, | |||||
| plus: { | |||||
| type: Number, | |||||
| default: 0 | |||||
| }, | |||||
| autoplay: { | |||||
| type: Boolean, | |||||
| default: false | |||||
| }, | |||||
| current: { | |||||
| type: Number, | |||||
| default: 0 | |||||
| }, | |||||
| interval: { | |||||
| type: Number, | |||||
| default: 5000 | |||||
| }, | |||||
| duration: { | |||||
| type: Number, | |||||
| default: 500 | |||||
| }, | |||||
| circular: { | |||||
| type: Boolean, | |||||
| default: false | |||||
| }, | |||||
| bounce: { | |||||
| type: Boolean, | |||||
| default: true | |||||
| }, | |||||
| size: { | |||||
| type: Number, | |||||
| default: 0 | |||||
| } | |||||
| }, | |||||
| provide() { | |||||
| return { | |||||
| config: { | |||||
| is3D: this.is3D, | |||||
| isCircular: this.circular, | |||||
| scale: this.scale, | |||||
| size: this._size, | |||||
| width: this.width, | |||||
| height: this.height, | |||||
| itemWidth: this.itemWidth, | |||||
| itemHeight: this.itemHeight, | |||||
| space: this.space, | |||||
| plus: this.plus | |||||
| } | |||||
| }; | |||||
| }, | |||||
| mounted() { | |||||
| if (this.autoplay) { | |||||
| this.autoplayInterval = setInterval(() => { | |||||
| this.next(); | |||||
| }, this.interval); | |||||
| } | |||||
| }, | |||||
| watch: { | |||||
| autoplay(newV) { | |||||
| if (!newV) { | |||||
| clearInterval(this.autoplayInterval); | |||||
| } else { | |||||
| this.autoplayInterval = setInterval(() => { | |||||
| this.next(); | |||||
| }, this.interval); | |||||
| } | |||||
| } | |||||
| }, | |||||
| methods: {} | |||||
| }; | |||||
| </script> | |||||
| <style scoped> | |||||
| .box { | |||||
| position: relative; | |||||
| overflow: hidden; | |||||
| } | |||||
| .container { | |||||
| position: absolute; | |||||
| top: 0; | |||||
| /* #ifndef APP-NVUE */ | |||||
| display: flex; | |||||
| /* #endif */ | |||||
| flex-direction: row; | |||||
| align-items: center; | |||||
| } | |||||
| </style> | |||||
| @ -1,303 +0,0 @@ | |||||
| 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) | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -1,91 +0,0 @@ | |||||
| const BindingX = uni.requireNativePlugin('bindingx'); | |||||
| const animation = uni.requireNativePlugin('animation'); | |||||
| export default { | |||||
| mounted() { | |||||
| this.container = this.getEl(this.$refs['container']) | |||||
| }, | |||||
| methods: { | |||||
| _notifyTouchChangeForItems(isTouchEnd) { | |||||
| this.swiperViews && this.swiperViews.forEach(it => { | |||||
| it.canClick = isTouchEnd | |||||
| }) | |||||
| }, | |||||
| touchstart(e) { | |||||
| if (this.isAnimated) return; | |||||
| if (this.stop) return | |||||
| this.isScrolling = true | |||||
| this._notifyTouchChangeForItems(false) | |||||
| this.stop = true | |||||
| this.startTime = new Date().getTime() | |||||
| const props = [{ | |||||
| element: this.container, | |||||
| property: 'transform.translateX', | |||||
| expression: uni.upx2px(this.left) + '+x' | |||||
| }] | |||||
| if (this.is3D) { | |||||
| const deltaScale = `${this.scale}/${this.width}*x` | |||||
| const currentScale = `${this.scale}-abs(x)/${this.width}*1` | |||||
| const realScale = `min(${currentScale},${this.scale})` | |||||
| const currentCardExp = `${realScale} < 1 ? 1 : ${realScale}`; | |||||
| const leftCardExp = `${deltaScale} > 0 ? min(${this.scale},(1 + ${deltaScale})) : min(1,(1 + ${deltaScale}))`; | |||||
| const rightCardExp = `${deltaScale} > 0 ? min(1,(1 - ${deltaScale})) : min(${this.scale},(1 - ${deltaScale}))`; | |||||
| props.push({ | |||||
| element: this.getSwipteItemEl(this.position), | |||||
| property: 'transform.scale', | |||||
| expression: currentCardExp | |||||
| }) | |||||
| if (this.position - 1 > -1) { | |||||
| props.push({ | |||||
| element: this.getSwipteItemEl(this.position - 1), | |||||
| property: 'transform.scale', | |||||
| expression: leftCardExp | |||||
| }) | |||||
| } | |||||
| if (this.position + 1 > this._size) { | |||||
| props.push({ | |||||
| element: this.getSwipteItemEl(this.position + 1), | |||||
| property: 'transform.scale', | |||||
| expression: rightCardExp | |||||
| }) | |||||
| } | |||||
| } | |||||
| this.eventpan = BindingX.bind({ | |||||
| anchor: this.container, | |||||
| eventType: 'pan', | |||||
| props | |||||
| }, (e) => { | |||||
| if (e.state === 'end' || | |||||
| e.state === 'exit') { | |||||
| setTimeout(() => { | |||||
| this._notifyTouchChangeForItems(true) | |||||
| }, 10) | |||||
| const timing = new Date().getTime() - this.startTime | |||||
| const velocity = Math.abs(e.deltaX / timing) | |||||
| this.stop = false | |||||
| this.isScrolling = false | |||||
| if (this.eventpan && this.eventpan.token) { | |||||
| BindingX.unbind({ | |||||
| token: this.eventpan.token, | |||||
| eventType: 'pan' | |||||
| }) | |||||
| } | |||||
| this.moveEnd({ | |||||
| velocity, | |||||
| deltaX: e.deltaX, | |||||
| deltaY: e.deltaY | |||||
| }) | |||||
| } | |||||
| }) | |||||
| }, | |||||
| getSwipteItemEl(idx) { | |||||
| return this.swiperViews[idx].$el.ref | |||||
| }, | |||||
| /** | |||||
| * 获取ref | |||||
| * @param {Object} el | |||||
| */ | |||||
| getEl(el) { | |||||
| return el.ref | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -1,143 +0,0 @@ | |||||
| var MIN_DISTANCE = 10; | |||||
| function changeData(newValue, oldValue, ownerInstance, instance) { | |||||
| var state = instance.getState(); | |||||
| if (newValue.left != undefined) { | |||||
| state.left = newValue.left | |||||
| } | |||||
| if (newValue.bounce != undefined) { | |||||
| state.bounce = newValue.bounce | |||||
| } | |||||
| // console.log('changeData', JSON.stringify(newValue)) | |||||
| } | |||||
| /** | |||||
| * 开始触摸操作 | |||||
| * @param {Object} e | |||||
| * @param {Object} ins | |||||
| */ | |||||
| function touchstart(e, ins) { | |||||
| var instance = e.instance; | |||||
| // 计算滑动开始位置 | |||||
| stopTouchStart(e, ins) | |||||
| } | |||||
| /** | |||||
| * 开始滑动操作 | |||||
| * @param {Object} e | |||||
| * @param {Object} ownerInstance | |||||
| */ | |||||
| function touchmove(e, ownerInstance) { | |||||
| var instance = e.instance; | |||||
| // 是否可以滑动页面 | |||||
| stopTouchMove(e); | |||||
| if (e.preventDefault) { | |||||
| // 阻止页面滚动 | |||||
| e.preventDefault() | |||||
| } | |||||
| // var state = instance.getState(); | |||||
| // && state.bounce | |||||
| move(instance, ownerInstance) | |||||
| return false | |||||
| } | |||||
| /** | |||||
| * 结束触摸操作 | |||||
| * @param {Object} e | |||||
| * @param {Object} ownerInstance | |||||
| */ | |||||
| function touchend(e, ownerInstance) { | |||||
| var instance = e.instance; | |||||
| var state = instance.getState() | |||||
| ownerInstance.callMethod('moveEnd', { | |||||
| velocity: Math.abs(state.deltaX / state.timing), | |||||
| direction: state.direction, | |||||
| deltaX: state.deltaX, | |||||
| deltaY: state.deltaY | |||||
| }) | |||||
| } | |||||
| /** | |||||
| * 设置移动距离 | |||||
| * @param {Object} instance | |||||
| * @param {Object} ownerInstance | |||||
| */ | |||||
| function move(instance, ownerInstance) { | |||||
| var state = instance.getState() | |||||
| var value = state.deltaX || 0 | |||||
| var state = instance.getState() | |||||
| if (state.direction == 'horizontal') { | |||||
| // instance.requestAnimationFrame(function() { | |||||
| // instance.setStyle({ | |||||
| // transform: 'translateX(' + value + 'px)', | |||||
| // '-webkit-transform': 'translateX(' + value + 'px)' | |||||
| // }) | |||||
| // }) | |||||
| ownerInstance.callMethod('moveTo', { | |||||
| deltaX: value, | |||||
| deltaY: state.deltaY || 0, | |||||
| left: state.left + value | |||||
| }) | |||||
| } | |||||
| } | |||||
| /** | |||||
| * 滑动中,是否禁止打开 | |||||
| * @param {Object} event | |||||
| */ | |||||
| function stopTouchMove(event) { | |||||
| var instance = event.instance; | |||||
| var state = instance.getState(); | |||||
| var touch = event.touches[0]; | |||||
| state.timing = getDate().getTime() - state.startTime; | |||||
| state.deltaX = touch.clientX - state.startX; | |||||
| state.deltaY = touch.clientY - state.startY; | |||||
| state.offsetX = Math.abs(state.deltaX); | |||||
| state.offsetY = Math.abs(state.deltaY); | |||||
| state.direction = state.direction || getDirection(state.offsetX, state.offsetY); | |||||
| } | |||||
| /** | |||||
| * 设置滑动开始位置 | |||||
| * @param {Object} event | |||||
| */ | |||||
| function stopTouchStart(event) { | |||||
| var instance = event.instance; | |||||
| var state = instance.getState(); | |||||
| resetTouchStatus(instance); | |||||
| var touch = event.touches[0]; | |||||
| state.startTime = getDate().getTime(); | |||||
| state.startX = touch.clientX; | |||||
| state.startY = touch.clientY; | |||||
| } | |||||
| function getDirection(x, y) { | |||||
| if (x > y && x > MIN_DISTANCE) { | |||||
| return 'horizontal'; | |||||
| } | |||||
| if (y > x && y > MIN_DISTANCE) { | |||||
| return 'vertical'; | |||||
| } | |||||
| return ''; | |||||
| } | |||||
| /** | |||||
| * 重置滑动状态 | |||||
| * @param {Object} event | |||||
| */ | |||||
| function resetTouchStatus(instance) { | |||||
| var state = instance.getState(); | |||||
| state.direction = ''; | |||||
| state.deltaX = 0; | |||||
| state.deltaY = 0; | |||||
| state.offsetX = 0; | |||||
| state.offsetY = 0; | |||||
| } | |||||
| module.exports = { | |||||
| changeData: changeData, | |||||
| touchstart: touchstart, | |||||
| touchmove: touchmove, | |||||
| touchend: touchend | |||||
| } | |||||
| @ -1,62 +0,0 @@ | |||||
| const MIN_DISTANCE = 10; | |||||
| export default { | |||||
| methods: { | |||||
| touchstart(e) { | |||||
| this._left = this.left | |||||
| this.stopTouchStart(e) | |||||
| }, | |||||
| touchmove(e) { | |||||
| // 是否可以滑动页面 | |||||
| this.stopTouchMove(e); | |||||
| if (this.direction == 'horizontal') { | |||||
| this.moveTo({ | |||||
| deltaX: this.deltaX, | |||||
| deltaY: this.deltaY || 0, | |||||
| left: this._left + this.deltaX | |||||
| }) | |||||
| } | |||||
| // FIXME: 冒泡 | |||||
| return false | |||||
| }, | |||||
| touchend() { | |||||
| this.moveEnd({ | |||||
| velocity: Math.abs(this.deltaX / this.timing), | |||||
| direction: this.direction, | |||||
| deltaX: this.deltaX, | |||||
| deltaY: this.deltaY, | |||||
| }) | |||||
| }, | |||||
| stopTouchStart(event) { | |||||
| this.resetTouchStatus(); | |||||
| const touch = event.touches[0]; | |||||
| this.startTime = new Date().getTime(); | |||||
| this.startX = touch.clientX; | |||||
| this.startY = touch.clientY; | |||||
| }, | |||||
| stopTouchMove(event) { | |||||
| const touch = event.touches[0]; | |||||
| this.timing = new Date().getTime() - this.startTime; | |||||
| this.deltaX = touch.clientX - this.startX; | |||||
| this.deltaY = touch.clientY - this.startY; | |||||
| this.offsetX = Math.abs(this.deltaX); | |||||
| this.offsetY = Math.abs(this.deltaY); | |||||
| this.direction = this.direction || this.getDirection(this.offsetX, this.offsetY); | |||||
| }, | |||||
| getDirection(x, y) { | |||||
| if (x > y && x > MIN_DISTANCE) { | |||||
| return 'horizontal'; | |||||
| } | |||||
| if (y > x && y > MIN_DISTANCE) { | |||||
| return 'vertical'; | |||||
| } | |||||
| return ''; | |||||
| }, | |||||
| resetTouchStatus() { | |||||
| this.direction = ''; | |||||
| this.deltaX = 0; | |||||
| this.deltaY = 0; | |||||
| this.offsetX = 0; | |||||
| this.offsetY = 0; | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -1,7 +0,0 @@ | |||||
| export default { | |||||
| data() { | |||||
| return { | |||||
| wxsData: {} | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -1,15 +0,0 @@ | |||||
| const toLine = (name) => { | |||||
| return name.replace(/([A-Z])/g, '-$1').toLowerCase(); | |||||
| } | |||||
| /** | |||||
| * style对象转化为style字符串 | |||||
| * @return {string} | |||||
| */ | |||||
| export const getStyleStr = (styleObject) => { | |||||
| let transfrom = ''; | |||||
| for (let i in styleObject) { | |||||
| let line = toLine(i); | |||||
| transfrom += line + ':' + styleObject[i] + ';'; | |||||
| } | |||||
| return transfrom | |||||
| } | |||||
| @ -0,0 +1,227 @@ | |||||
| <template> | |||||
| <view class="redact-address"> | |||||
| <uv-form label-width="210rpx" :model="addressDetail" ref="form"> | |||||
| <uv-form-item label="联系人" prop="name"> | |||||
| <uv-input v-model="addressDetail.name" placeholder="请输入联系人姓名" border="none"> | |||||
| </uv-input> | |||||
| </uv-form-item> | |||||
| <uv-form-item label="手机号" prop="phone"> | |||||
| <uv-input v-model="addressDetail.phone" placeholder="请输入手机号" border="none"> | |||||
| </uv-input> | |||||
| </uv-form-item> | |||||
| <uv-form-item label="所在地区" prop="address"> | |||||
| <uv-input v-model="addressDetail.address" placeholder="请选择所在地区" border="none"> | |||||
| </uv-input> | |||||
| <template #right> | |||||
| <view style="padding-right: 40rpx;color: #FBAB32;" @click.stop="selectAddr"> | |||||
| <image src="../../static/address/selectIcon.png" mode="aspectFit"></image>定位 | |||||
| </view> | |||||
| </template> | |||||
| </uv-form-item> | |||||
| <uv-form-item label="详细地址" prop="addressDetail"> | |||||
| <uv-input v-model="addressDetail.addressDetails" placeholder="请输入详细地址" border="none"> | |||||
| </uv-input> | |||||
| </uv-form-item> | |||||
| </uv-form> | |||||
| </view> | |||||
| </template> | |||||
| <script> | |||||
| import Position from '@/utils/position.js' | |||||
| export default { | |||||
| data() { | |||||
| return { | |||||
| addressDetail: { | |||||
| name: '', | |||||
| phone: '', | |||||
| address: '', | |||||
| addressDetails: '', | |||||
| defaultFlag: '', | |||||
| latitude: '', | |||||
| longitude: '' | |||||
| } | |||||
| } | |||||
| }, | |||||
| props: { | |||||
| title: { | |||||
| type: String, | |||||
| default: '新增地址' | |||||
| } | |||||
| }, | |||||
| methods: { | |||||
| open(addressDetail) { | |||||
| this.addressDetail = addressDetail | |||||
| this.$refs.addressPopup.open('bottom') | |||||
| }, | |||||
| close(){ | |||||
| this.$refs.addressPopup.close() | |||||
| }, | |||||
| //新增和修改地址 | |||||
| onSubmit() { | |||||
| let isOk = this.parameterVerification(this.addressDetail) | |||||
| if (isOk && !isOk.auth) { | |||||
| return uni.showToast({ | |||||
| icon: 'none', | |||||
| title: isOk.title, | |||||
| 'zIndex': 10000 | |||||
| }) | |||||
| } | |||||
| this.$emit('saveOrUpdate', this.addressDetail) | |||||
| }, | |||||
| //验证用户参数合法性 | |||||
| parameterVerification(addressForm) { | |||||
| let { | |||||
| name, | |||||
| phone, | |||||
| address, | |||||
| addressDetails | |||||
| } = addressForm | |||||
| if (name.trim() == '') { | |||||
| return { | |||||
| title: '请填写联系人', | |||||
| auth: false | |||||
| } | |||||
| } else if (phone.trim() == '') { | |||||
| return { | |||||
| title: '请填写手机号', | |||||
| auth: false | |||||
| } | |||||
| } else if (address.trim() == '') { | |||||
| return { | |||||
| title: '请填写所在地区', | |||||
| auth: false | |||||
| } | |||||
| } else if (addressDetails.trim() == '') { | |||||
| return { | |||||
| title: '请填写详细地址', | |||||
| auth: false | |||||
| } | |||||
| } else if (phone.trim() != '') { | |||||
| if (!this.$utils.verificationPhone(phone)) { | |||||
| return { | |||||
| title: '手机号格式不合法', | |||||
| auth: false | |||||
| } | |||||
| } | |||||
| } | |||||
| return { | |||||
| title: '验证通过', | |||||
| auth: true | |||||
| } | |||||
| }, | |||||
| //地图上选择地址 | |||||
| selectAddr() { | |||||
| // Position.getLocation(res => { | |||||
| Position.selectAddress(0, 0, success => { | |||||
| this.setAddress(success) | |||||
| }) | |||||
| // }) | |||||
| }, | |||||
| //提取用户选择的地址信息复制给表单数据 | |||||
| setAddress(res) { | |||||
| //经纬度信息 | |||||
| this.addressDetail.latitude = res.latitude | |||||
| this.addressDetail.longitude = res.longitude | |||||
| if (!res.address && res.name) { //用户直接选择城市的逻辑 | |||||
| return this.addressDetail.address = res.name | |||||
| } | |||||
| if (res.address || res.name) { | |||||
| return this.addressDetail.address = res.address + res.name | |||||
| } | |||||
| this.addressDetail.address = '' //用户啥都没选就点击勾选 | |||||
| }, | |||||
| } | |||||
| } | |||||
| </script> | |||||
| <style lang="scss" scoped> | |||||
| .redact-address { | |||||
| box-sizing: border-box; | |||||
| .redact-address-title { | |||||
| height: 80rpx; | |||||
| line-height: 80rpx; | |||||
| font-size: 30rpx; | |||||
| color: #333333; | |||||
| font-weight: 600; | |||||
| } | |||||
| .save { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: center; | |||||
| width: 90%; | |||||
| height: 80rpx; | |||||
| border-radius: 40rpx; | |||||
| color: white; | |||||
| font-size: 28rpx; | |||||
| margin: 0rpx auto; | |||||
| background: $uni-color; | |||||
| margin-top: 150rpx; | |||||
| } | |||||
| image { | |||||
| width: 25rpx; | |||||
| height: 25rpx; | |||||
| } | |||||
| //修改组件默认样式 | |||||
| .uv-form { | |||||
| padding: 30rpx 0rpx; | |||||
| } | |||||
| .uv-input__content__field-wrapper__field{ | |||||
| padding: 30rpx !important; | |||||
| height: 180rpx !important; | |||||
| } | |||||
| &::v-deep .uv-cell { | |||||
| padding: 0rpx 0rpx; | |||||
| font-size: 26rpx; | |||||
| &::after { | |||||
| border: none !important; | |||||
| } | |||||
| .uv-field__label { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| height: 80rpx; | |||||
| } | |||||
| .uv-field__control, | |||||
| .uv-field__right-icon { | |||||
| height: 80rpx; | |||||
| font-size: 26rpx; | |||||
| border-bottom: 2rpx solid #cbc8c8; | |||||
| } | |||||
| .uv-field__right-icon { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| height: 78rpx; | |||||
| color: #5FCC9F; | |||||
| } | |||||
| .uv-cell__value { | |||||
| height: 120rpx; | |||||
| } | |||||
| } | |||||
| &::v-deep .uv-field__error-message { | |||||
| color: #5AC796; | |||||
| font-size: 20rpx; | |||||
| margin-top: 10rpx; | |||||
| } | |||||
| } | |||||
| </style> | |||||