@ -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> |