mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
[breaking change] reconstruct Swipe component (#194)
* Swipe: reconstruct * [bugfix]: Swipe autoplay [bugfix] AddressEdit doc link
This commit is contained in:
parent
77f6a798e4
commit
560ccfdec2
@ -139,4 +139,4 @@ export default {
|
|||||||
| address | 详细地址 | `String` |
|
| address | 详细地址 | `String` |
|
||||||
|
|
||||||
#### 省市县列表数据格式
|
#### 省市县列表数据格式
|
||||||
请参考 [Area](/zanui/vue/component/area) 组件。
|
请参考 [Area](#/zh-CN/component/area) 组件。
|
||||||
|
@ -1,10 +1,34 @@
|
|||||||
<style>
|
<style>
|
||||||
.demo-swipe {
|
.demo-swipe {
|
||||||
|
padding-bottom: 30px;
|
||||||
|
|
||||||
.van-swipe {
|
.van-swipe {
|
||||||
height: 200px;
|
cursor: pointer;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
color: #fff;
|
||||||
|
min-height: 140px;
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 150px;
|
||||||
|
|
||||||
|
&:nth-child(even) {
|
||||||
|
background-color: #39a9ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(odd) {
|
||||||
|
background-color: #66c6f2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 240px;
|
||||||
|
display: block;
|
||||||
|
padding: 30px 60px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #fff;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -14,21 +38,13 @@
|
|||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
autoImages: [
|
|
||||||
'https://img.yzcdn.cn/upload_files/2017/03/09/FvkZahKoq1vkxLQFdVWeLf2UCqDz.png',
|
|
||||||
'https://img.yzcdn.cn/upload_files/2017/03/09/Fk0rpe_svu9d5Xk3MUCWd1QeMXOu.png'
|
|
||||||
],
|
|
||||||
images: [
|
images: [
|
||||||
'https://img.yzcdn.cn/upload_files/2017/03/14/FmTPs0SeyQaAOSK1rRe1sL8RcwSY.jpeg',
|
'https://img.yzcdn.cn/public_files/2017/09/05/3bd347e44233a868c99cf0fe560232be.jpg',
|
||||||
'https://img.yzcdn.cn/upload_files/2017/03/15/FvexrWlG_WxtCE9Omo5l27n_mAG_.jpeg'
|
'https://img.yzcdn.cn/public_files/2017/09/05/c0dab461920687911536621b345a0bc9.jpg',
|
||||||
|
'https://img.yzcdn.cn/public_files/2017/09/05/4e3ea0898b1c2c416eec8c11c5360833.jpg',
|
||||||
|
'https://img.yzcdn.cn/public_files/2017/09/05/fd08f07665ed67d50e11b32a21ce0682.jpg'
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
handlePageEnd(page, index) {
|
|
||||||
console.log(page, index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -46,14 +62,27 @@ Vue.component(SwipeItem.name, SwipeItem);
|
|||||||
### 代码演示
|
### 代码演示
|
||||||
|
|
||||||
#### 基础用法
|
#### 基础用法
|
||||||
|
通过`autoplay`属性设置自动轮播间隔
|
||||||
|
|
||||||
:::demo 基础用法
|
:::demo 基础用法
|
||||||
```html
|
```html
|
||||||
|
<van-swipe :autoplay="3000">
|
||||||
|
<van-swipe-item>1</van-swipe-item>
|
||||||
|
<van-swipe-item>2</van-swipe-item>
|
||||||
|
<van-swipe-item>3</van-swipe-item>
|
||||||
|
<van-swipe-item>4</van-swipe-item>
|
||||||
|
</van-swipe>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
#### 图片懒加载
|
||||||
|
配合 [Lazyload](#/zh-CN/component/lazyload) 组件实现图片懒加载
|
||||||
|
|
||||||
|
:::demo 图片懒加载
|
||||||
|
```html
|
||||||
<van-swipe>
|
<van-swipe>
|
||||||
<van-swipe-item v-for="(img, index) in images" :key="index">
|
<van-swipe-item v-for="(image, index) in images" :key="index">
|
||||||
<a href="https://youzan.com" target="_blank">
|
<img v-lazy="image" />
|
||||||
<img v-lazy="img" alt="">
|
|
||||||
</a>
|
|
||||||
</van-swipe-item>
|
</van-swipe-item>
|
||||||
</van-swipe>
|
</van-swipe>
|
||||||
```
|
```
|
||||||
@ -63,84 +92,25 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
images: [
|
images: [
|
||||||
'https://img.yzcdn.cn/upload_files/2017/03/14/FmTPs0SeyQaAOSK1rRe1sL8RcwSY.jpeg',
|
'https://img.yzcdn.cn/1.jpg',
|
||||||
'https://img.yzcdn.cn/upload_files/2017/03/15/FvexrWlG_WxtCE9Omo5l27n_mAG_.jpeg'
|
'https://img.yzcdn.cn/2.jpg'
|
||||||
]
|
]
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
#### 隐藏指示器
|
|
||||||
|
|
||||||
需要设置`show-indicators`属性为`false`,即会隐藏指示器。
|
|
||||||
|
|
||||||
:::demo 隐藏指示器
|
|
||||||
```html
|
|
||||||
<van-swipe :show-indicators="false">
|
|
||||||
<van-swipe-item v-for="(img, index) in autoImages" :key="index">
|
|
||||||
<img v-lazy="img" alt="">
|
|
||||||
</van-swipe-item>
|
|
||||||
</van-swipe>
|
|
||||||
```
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
autoImages: [
|
|
||||||
'https://img.yzcdn.cn/upload_files/2017/03/09/FvkZahKoq1vkxLQFdVWeLf2UCqDz.png',
|
|
||||||
'https://img.yzcdn.cn/upload_files/2017/03/09/Fk0rpe_svu9d5Xk3MUCWd1QeMXOu.png'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
#### 自动轮播
|
|
||||||
|
|
||||||
需要设置`auto-play`属性为`true`,即会自动轮播。
|
|
||||||
|
|
||||||
:::demo 自动轮播
|
|
||||||
```html
|
|
||||||
<van-swipe auto-play @pagechange:end="handlePageEnd">
|
|
||||||
<van-swipe-item v-for="(img, index) in autoImages" :key="index">
|
|
||||||
<img v-lazy="img" alt="">
|
|
||||||
</van-swipe-item>
|
|
||||||
</van-swipe>
|
|
||||||
```
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
autoImages: [
|
|
||||||
'https://img.yzcdn.cn/upload_files/2017/03/09/FvkZahKoq1vkxLQFdVWeLf2UCqDz.png',
|
|
||||||
'https://img.yzcdn.cn/upload_files/2017/03/09/Fk0rpe_svu9d5Xk3MUCWd1QeMXOu.png'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
handlePageEnd(page, index) {
|
|
||||||
console.log(page, index);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### API
|
### API
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|
||||||
|-----------|-----------|-----------|-------------|-------------|
|
|-----------|-----------|-----------|-------------|-------------|
|
||||||
| autoPlay | 是否自动轮播 | `Boolean` | `false` | `true`, `false` |
|
| autoplay | 自动轮播间隔,单位为 ms | `Number` | - | - |
|
||||||
| showIndicators | 是否显示指示器 | `Boolean` | `true` | `true`, `false` |
|
| duration | 动画时长,单位为 ms | `Number` | `500` | - |
|
||||||
|
| showIndicators | 是否显示指示器 | `Boolean` | `true` | - |
|
||||||
|
|
||||||
### 事件
|
### 事件
|
||||||
|
|
||||||
| 事件名 | 说明 | 参数 |
|
| 事件名 | 说明 | 参数 |
|
||||||
|-----------|-----------|-----------|
|
|-----------|-----------|-----------|
|
||||||
| `pagechange:end` | 每一页轮播结束后触发 | `(elem, currIndex)`:`elem`为触发页当前的DOM节点 |
|
| change | 每一页轮播结束后触发 | index, 当前页的索引 |
|
||||||
|
@ -4,6 +4,10 @@
|
|||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
body {
|
body {
|
||||||
|
color: #333;
|
||||||
|
line-height: 1;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
font-family: Arial, Helvetica, "STHeiti STXihei", "Microsoft YaHei", Tohoma, sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="van-swipe-item">
|
<div class="van-swipe-item" :style="style">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -10,13 +10,27 @@ export default {
|
|||||||
|
|
||||||
beforeCreate() {
|
beforeCreate() {
|
||||||
this.$parent.swipes.push(this);
|
this.$parent.swipes.push(this);
|
||||||
|
this.$parent.childrenOffset.push(0);
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
offset: 0,
|
||||||
|
index: this.$parent.swipes.indexOf(this)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
style() {
|
||||||
|
return {
|
||||||
|
width: this.$parent.width + 'px',
|
||||||
|
transform: `translate3d(${this.offset}px, 0, 0)`
|
||||||
|
};
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
destroyed() {
|
destroyed() {
|
||||||
const index = this.$parent.swipes.indexOf(this);
|
this.$parent.swipes.splice(this.index, 1);
|
||||||
if (index > -1) {
|
|
||||||
this.$parent.swipes.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,97 +1,161 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="van-swipe">
|
<div class="van-swipe">
|
||||||
<div class="van-swipe__items">
|
<div
|
||||||
|
:style="trackStyle"
|
||||||
|
class="van-swipe__track"
|
||||||
|
@touchstart="onTouchStart"
|
||||||
|
@touchmove="onTouchMove"
|
||||||
|
@touchend="onTouchEnd"
|
||||||
|
@touchcancel="onTouchEnd"
|
||||||
|
@transitionend="$emit('change', activeIndicator)"
|
||||||
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="van-swipe__indicators" v-if="showIndicators && count > 1">
|
||||||
class="van-swipe__indicators"
|
<i v-for="index in count" :class="{ 'van-swipe__indicator--active': index - 1 === activeIndicator }" />
|
||||||
v-if="showIndicators && swipes.length > 1"
|
|
||||||
>
|
|
||||||
<span class="van-swipe__indicator" v-for="(item, index) in swipes.length" :key="index" :class="{
|
|
||||||
'van-swipe__indicator--active': currIndex === index
|
|
||||||
}">
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Input from './input';
|
|
||||||
import Scroll from './scroll';
|
|
||||||
import SpringDummy from './spring_dummy';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'van-swipe',
|
name: 'van-swipe',
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
autoPlay: Boolean,
|
autoplay: Number,
|
||||||
showIndicators: {
|
showIndicators: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
|
},
|
||||||
|
duration: {
|
||||||
|
type: Number,
|
||||||
|
default: 500
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
currIndex: 0,
|
offset: 0,
|
||||||
swipes: []
|
startX: 0,
|
||||||
|
startY: 0,
|
||||||
|
active: 0,
|
||||||
|
deltaX: 0,
|
||||||
|
swipes: [],
|
||||||
|
childrenOffset: [],
|
||||||
|
direction: '',
|
||||||
|
currentDuration: 0,
|
||||||
|
width: window.innerWidth
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.input = new Input(this.$el, {
|
this.move(0);
|
||||||
listenMoving: true
|
this.autoPlay();
|
||||||
});
|
|
||||||
|
|
||||||
this.input.on('move', function(dist, isEnd, e, extra) {
|
|
||||||
if (extra.orgDirection) {
|
|
||||||
e.preventDefault();
|
|
||||||
scroll.movePage(dist.x, isEnd);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.scroll = new Scroll(this.$el, {
|
|
||||||
autoPlay: this.autoPlay
|
|
||||||
});
|
|
||||||
|
|
||||||
const scroll = this.scroll;
|
|
||||||
scroll.on('pageChangeEnd', this.onPageChangeEnd);
|
|
||||||
|
|
||||||
const dummy = new SpringDummy(scroll, this.input);
|
|
||||||
|
|
||||||
dummy.on('bounce', function(dist, isEnd) {
|
|
||||||
scroll.movePage(dist.x, isEnd);
|
|
||||||
}).on('autoPlay', function(dist, isEnd) {
|
|
||||||
scroll.movePage(dist.x, isEnd);
|
|
||||||
});
|
|
||||||
this.dummy = dummy;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
computed: {
|
||||||
swipes(value) {
|
count() {
|
||||||
if (this.autoPlay && value.length > 1) {
|
return this.swipes.length;
|
||||||
this.dummy.initMove();
|
|
||||||
} else {
|
|
||||||
this.dummy.clearMove();
|
|
||||||
}
|
|
||||||
this.scroll.update();
|
|
||||||
return value;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
autoPlay(value) {
|
trackStyle() {
|
||||||
if (value && this.swipes.length > 1) {
|
return this.count === 1 ? {} : {
|
||||||
this.dummy.initMove();
|
paddingLeft: this.width + 'px',
|
||||||
} else {
|
width: (this.count + 2) * this.width + 'px',
|
||||||
this.dummy.clearMove();
|
transitionDuration: `${this.currentDuration}ms`,
|
||||||
}
|
transform: `translate3d(${this.offset}px, 0, 0)`
|
||||||
return value;
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
activeIndicator() {
|
||||||
|
return (this.active + this.count) % this.count;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onPageChangeEnd(page, currIndex) {
|
onTouchStart(event) {
|
||||||
this.currIndex = +currIndex;
|
clearTimeout(this.timer);
|
||||||
this.$emit('pagechange:end', page, currIndex);
|
|
||||||
|
this.deltaX = 0;
|
||||||
|
this.direction = '';
|
||||||
|
this.currentDuration = 0;
|
||||||
|
this.startX = event.touches[0].clientX;
|
||||||
|
this.startY = event.touches[0].clientY;
|
||||||
|
|
||||||
|
if (this.active === -1) {
|
||||||
|
this.move(this.count);
|
||||||
|
}
|
||||||
|
if (this.active === this.count) {
|
||||||
|
this.move(-this.count);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onTouchMove(event) {
|
||||||
|
this.direction = this.direction || this.getDirection(event.touches[0]);
|
||||||
|
|
||||||
|
if (this.direction === 'horizontal') {
|
||||||
|
event.preventDefault();
|
||||||
|
this.deltaX = event.touches[0].clientX - this.startX;
|
||||||
|
this.move(0, this.range(this.deltaX, [-this.width, this.width]));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onTouchEnd() {
|
||||||
|
if (this.deltaX) {
|
||||||
|
this.move(Math.abs(this.deltaX) > 50 ? (this.deltaX > 0 ? -1 : 1) : 0);
|
||||||
|
this.currentDuration = this.duration;
|
||||||
|
}
|
||||||
|
this.autoPlay();
|
||||||
|
},
|
||||||
|
|
||||||
|
move(move = 0, offset = 0) {
|
||||||
|
const { active, count, swipes, deltaX, width } = this;
|
||||||
|
|
||||||
|
if (move) {
|
||||||
|
if (active === -1) {
|
||||||
|
swipes[count - 1].offset = 0;
|
||||||
|
}
|
||||||
|
swipes[0].offset = active === count - 1 ? count * width : 0;
|
||||||
|
|
||||||
|
this.active += move;
|
||||||
|
} else {
|
||||||
|
if (active === 0) {
|
||||||
|
swipes[count - 1].offset = deltaX > 0 ? -count * width : 0;
|
||||||
|
} else if (active === count - 1) {
|
||||||
|
swipes[0].offset = deltaX < 0 ? count * width : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.offset = offset - (this.active + 1) * this.width;
|
||||||
|
},
|
||||||
|
|
||||||
|
autoPlay() {
|
||||||
|
const { autoplay } = this;
|
||||||
|
if (autoplay && this.count > 1) {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
this.timer = setTimeout(() => {
|
||||||
|
this.currentDuration = 0;
|
||||||
|
|
||||||
|
if (this.active === this.count) {
|
||||||
|
this.move(-this.count);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.currentDuration = this.duration;
|
||||||
|
this.move(1);
|
||||||
|
this.autoPlay();
|
||||||
|
}, 30);
|
||||||
|
}, autoplay);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getDirection(touch) {
|
||||||
|
const distanceX = Math.abs(touch.clientX - this.startX);
|
||||||
|
const distanceY = Math.abs(touch.clientY - this.startY);
|
||||||
|
return distanceX > distanceY ? 'horizontal' : distanceX < distanceY ? 'vertical' : '';
|
||||||
|
},
|
||||||
|
|
||||||
|
range(num, arr) {
|
||||||
|
return Math.min(Math.max(num, arr[0]), arr[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,119 +0,0 @@
|
|||||||
import Vue from 'vue';
|
|
||||||
import { EventEmitter, extend, bindEvents, removeEvents } from './utils';
|
|
||||||
|
|
||||||
function Input(host, options) {
|
|
||||||
EventEmitter.apply(this, arguments);
|
|
||||||
|
|
||||||
this.isStarting = false;
|
|
||||||
this.startPt = null;
|
|
||||||
this.endPt = null;
|
|
||||||
this.isDeaf = false;
|
|
||||||
|
|
||||||
this.options = extend({
|
|
||||||
listenMoving: false
|
|
||||||
}, options);
|
|
||||||
|
|
||||||
this.host = host;
|
|
||||||
this.onTouchStart = this.onTouchStart.bind(this);
|
|
||||||
this.onTouchMove = this.onTouchMove.bind(this);
|
|
||||||
this.onTouchEnd = this.onTouchEnd.bind(this);
|
|
||||||
|
|
||||||
this.bind(this.host);
|
|
||||||
}
|
|
||||||
|
|
||||||
Input.prototype = Object.create(new EventEmitter());
|
|
||||||
|
|
||||||
extend(Input.prototype, {
|
|
||||||
bind: function(host) {
|
|
||||||
if (Vue.prototype.$isServer) return;
|
|
||||||
bindEvents(host, 'dragstart', e => e.preventDefault());
|
|
||||||
bindEvents(host, 'touchstart mousedown', this.onTouchStart);
|
|
||||||
if (this.options.listenMoving) {
|
|
||||||
bindEvents(window, 'touchmove mousemove', this.onTouchMove);
|
|
||||||
}
|
|
||||||
bindEvents(window, 'touchend mouseup touchcancel', this.onTouchEnd);
|
|
||||||
},
|
|
||||||
|
|
||||||
onTouchStart: function(e) {
|
|
||||||
if (this.isDeaf || this.isStarting) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.isStarting = true;
|
|
||||||
this.orgDirection = null;
|
|
||||||
this.startPt = this.pointerEventToXY(e);
|
|
||||||
},
|
|
||||||
|
|
||||||
onTouchMove: function(e) {
|
|
||||||
if (!this.isStarting) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.caculate(e);
|
|
||||||
},
|
|
||||||
|
|
||||||
onTouchEnd: function(e) {
|
|
||||||
if (!this.isStarting) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.isStarting = false;
|
|
||||||
this.caculate(e, true);
|
|
||||||
},
|
|
||||||
|
|
||||||
caculate: function(e, isEnd) {
|
|
||||||
var distY, distX;
|
|
||||||
this.endPt = this.pointerEventToXY(e);
|
|
||||||
|
|
||||||
distY = this.startPt.y - this.endPt.y;
|
|
||||||
distX = this.startPt.x - this.endPt.x;
|
|
||||||
|
|
||||||
if (distY) {
|
|
||||||
this.emit(distY > 0 ? 'up' : 'down', distY, isEnd, e);
|
|
||||||
}
|
|
||||||
if (distX) {
|
|
||||||
this.emit(distX > 0 ? 'left' : 'right', distX, isEnd, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.orgDirection == null) {
|
|
||||||
this.orgDirection = Math.abs(distX) > Math.abs(distY);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.emit('move', { x: distX, y: distY }, isEnd, e, { orgDirection: this.orgDirection });
|
|
||||||
},
|
|
||||||
|
|
||||||
pointerEventToXY: function(e) {
|
|
||||||
var out = { x: 0, y: 0 };
|
|
||||||
var type = e.type;
|
|
||||||
if (e.originalEvent) {
|
|
||||||
e = e.originalEvent;
|
|
||||||
}
|
|
||||||
if (['touchstart', 'touchmove', 'touchend', 'touchcancel'].indexOf(type) > -1) {
|
|
||||||
var touch = e.touches[0] || e.changedTouches[0];
|
|
||||||
out.x = touch.pageX;
|
|
||||||
out.y = touch.pageY;
|
|
||||||
} else if (
|
|
||||||
['mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'].indexOf(type) > -1
|
|
||||||
) {
|
|
||||||
out.x = e.pageX;
|
|
||||||
out.y = e.pageY;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
},
|
|
||||||
|
|
||||||
deaf: function() {
|
|
||||||
this.isDeaf = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
undeaf: function() {
|
|
||||||
this.isDeaf = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
destroy: function() {
|
|
||||||
if (Vue.prototype.$isServer) return;
|
|
||||||
removeEvents(this.host, 'touchstart mousedown', this.onTouchStart);
|
|
||||||
if (this.options.listenMoving) {
|
|
||||||
removeEvents(window, 'touchmove mousemove', this.onTouchMove);
|
|
||||||
}
|
|
||||||
removeEvents(window, 'touchend mouseup touchcancel', this.onTouchEnd);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Input;
|
|
@ -1,146 +0,0 @@
|
|||||||
import { EventEmitter, extend } from './utils';
|
|
||||||
|
|
||||||
const setElementsStyles = (elems, styles) => {
|
|
||||||
Array.prototype.forEach.call(elems, item => {
|
|
||||||
extend(item.style, styles);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function Scroll(wrapElem, options) {
|
|
||||||
EventEmitter.apply(this, arguments);
|
|
||||||
this.wrapElem = wrapElem;
|
|
||||||
this.wrapSize = {
|
|
||||||
width: () => wrapElem.clientWidth,
|
|
||||||
height: () => wrapElem.clientHeight
|
|
||||||
};
|
|
||||||
|
|
||||||
this.options = extend({
|
|
||||||
loop: true,
|
|
||||||
autoPlay: false,
|
|
||||||
startIndex: 0
|
|
||||||
}, options);
|
|
||||||
this.init.apply(this, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
Scroll.prototype = Object.create(new EventEmitter());
|
|
||||||
extend(Scroll.prototype, {
|
|
||||||
init: function() {
|
|
||||||
this.update();
|
|
||||||
},
|
|
||||||
|
|
||||||
getCurrentDist: function() {
|
|
||||||
return this.mCache.currentDist;
|
|
||||||
},
|
|
||||||
|
|
||||||
update: function() {
|
|
||||||
const oldPages = this.pages;
|
|
||||||
this.pages = this.wrapElem.querySelectorAll('.van-swipe-item');
|
|
||||||
if (oldPages && oldPages.length === this.pages.length) {
|
|
||||||
const isSame = Array.prototype.every.call(this.pages, (elem, index) => {
|
|
||||||
return this.pages[index] === oldPages[index];
|
|
||||||
});
|
|
||||||
if (isSame) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var defaultStyle = {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: '100%',
|
|
||||||
height: '100%'
|
|
||||||
};
|
|
||||||
setElementsStyles(this.pages, defaultStyle);
|
|
||||||
this.mCache = {
|
|
||||||
dist: 0,
|
|
||||||
offsetPage: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setCurrentPage(0);
|
|
||||||
this.movePage(this.options.startIndex * this.wrapSize.width(), true);
|
|
||||||
},
|
|
||||||
|
|
||||||
renderPage: function(dist = 0, currentOffsetPage = 0) {
|
|
||||||
var wrapWidth = this.wrapSize.width();
|
|
||||||
var offset = currentOffsetPage * wrapWidth - dist;
|
|
||||||
var page;
|
|
||||||
var leftPage;
|
|
||||||
var rightPage;
|
|
||||||
var leftOffset = offset - wrapWidth;
|
|
||||||
var rightOffset = offset + wrapWidth;
|
|
||||||
|
|
||||||
leftPage = this.pages[this.mapLoopPage(currentOffsetPage - 1)];
|
|
||||||
if (leftPage) {
|
|
||||||
if (Math.abs(leftOffset) <= wrapWidth) {
|
|
||||||
leftPage.style['-webkit-transform'] = 'translate3d(' + leftOffset + 'px, 0, 0)';
|
|
||||||
} else {
|
|
||||||
if (this.pages.length > 2) {
|
|
||||||
leftPage.style['-webkit-transform'] = 'translate3d(-9999px, 0, 0)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rightPage = this.pages[this.mapLoopPage(currentOffsetPage + 1)];
|
|
||||||
if (rightPage) {
|
|
||||||
if (Math.abs(rightOffset) <= wrapWidth) {
|
|
||||||
rightPage.style['-webkit-transform'] = 'translate3d(' + rightOffset + 'px, 0, 0)';
|
|
||||||
} else {
|
|
||||||
if (this.pages.length > 2) {
|
|
||||||
rightPage.style['-webkit-transform'] = 'translate3d(-9999px, 0, 0)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
page = this.getCurrentPage();
|
|
||||||
if (page) {
|
|
||||||
page.style['-webkit-transform'] = 'translate3d(' + offset + 'px, 0, 0)';
|
|
||||||
page.style['display'] = 'block';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
movePage: function(dist, isEnd) {
|
|
||||||
var currentOffsetPage;
|
|
||||||
|
|
||||||
this.mCache.currentDist = dist + this.mCache.dist;
|
|
||||||
if (isEnd) {
|
|
||||||
this.mCache.dist += dist;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentOffsetPage = Math.round(this.mCache.currentDist / this.wrapSize.width()) || 0;
|
|
||||||
|
|
||||||
if (currentOffsetPage !== this.mCache.offsetPage) {
|
|
||||||
this.setCurrentPage(currentOffsetPage);
|
|
||||||
|
|
||||||
// 翻页
|
|
||||||
this.emit('pageChangeEnd', this.getCurrentPage()
|
|
||||||
, this.currentIndex, this.mCache.offsetPage);
|
|
||||||
this.mCache.offsetPage = currentOffsetPage;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.renderPage(this.mCache.currentDist, currentOffsetPage);
|
|
||||||
},
|
|
||||||
|
|
||||||
getCurrentPage: function() {
|
|
||||||
return this.pages[this.currentIndex];
|
|
||||||
},
|
|
||||||
|
|
||||||
mapLoopPage: function(num) {
|
|
||||||
if (this.options.loop) {
|
|
||||||
var direction = num < 0 ? -1 : 1;
|
|
||||||
var l = this.pages.length;
|
|
||||||
return Math.abs(l + direction * Math.abs(num) % l) % l;
|
|
||||||
} else {
|
|
||||||
if (num >= this.pages.length || num < 0) {
|
|
||||||
return this.pages.length;
|
|
||||||
} else {
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setCurrentPage: function(num) {
|
|
||||||
this.currentIndex = this.mapLoopPage(num);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Scroll;
|
|
@ -1,145 +0,0 @@
|
|||||||
import { requestAnimationFrame, cancelAnimationFrame, EventEmitter, extend } from './utils';
|
|
||||||
|
|
||||||
function SpringDummy(scroll, input, options) {
|
|
||||||
var wrapElem = scroll.wrapElem;
|
|
||||||
var self = this;
|
|
||||||
EventEmitter.apply(this, arguments);
|
|
||||||
|
|
||||||
this.scroll = scroll;
|
|
||||||
this.input = input;
|
|
||||||
this.input.on('move', this.movementReact.bind(this));
|
|
||||||
this.wrapSize = {
|
|
||||||
width: () => wrapElem.clientWidth,
|
|
||||||
height: () => wrapElem.clientHieght
|
|
||||||
};
|
|
||||||
|
|
||||||
this.options = extend({
|
|
||||||
intervalTween: 3000,
|
|
||||||
threshold: 20
|
|
||||||
}, options);
|
|
||||||
|
|
||||||
if (this.scroll.options.autoPlay) {
|
|
||||||
this.initMove();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.on('bounceEnd', function() {
|
|
||||||
if (self.scroll.options.autoPlay) {
|
|
||||||
self.initMove();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.input.undeaf();
|
|
||||||
}).on('bounceStart', function() {
|
|
||||||
self.input.deaf();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SpringDummy.prototype = Object.create(new EventEmitter());
|
|
||||||
extend(SpringDummy.prototype, {
|
|
||||||
|
|
||||||
clearTransition: function() {
|
|
||||||
cancelAnimationFrame(this.transitionReq);
|
|
||||||
},
|
|
||||||
|
|
||||||
movementReact: function(pt, isEnd, e, extra) {
|
|
||||||
if (isEnd) {
|
|
||||||
this.launch(extra.orgDirection ? pt.x : 0);
|
|
||||||
}
|
|
||||||
this.clearMove();
|
|
||||||
},
|
|
||||||
|
|
||||||
launch: function(dist) {
|
|
||||||
var self = this;
|
|
||||||
var direction = dist / Math.abs(dist);
|
|
||||||
var addition = 0;
|
|
||||||
var w = self.wrapSize.width();
|
|
||||||
var tempOffsetPage = Math.round(dist / w);
|
|
||||||
var offsetPage = this.scroll.mCache.offsetPage;
|
|
||||||
|
|
||||||
// 翻到对应页
|
|
||||||
addition = w * tempOffsetPage;
|
|
||||||
|
|
||||||
// addition为0是原位置
|
|
||||||
if (addition === 0) {
|
|
||||||
if (Math.abs(dist) > self.options.threshold) {
|
|
||||||
// 翻到下一页
|
|
||||||
addition = w * direction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!self.scroll.options.loop) {
|
|
||||||
if (offsetPage <= 0) {
|
|
||||||
if (Math.abs(dist) > self.options.threshold && direction > 0) {
|
|
||||||
addition = w * direction;
|
|
||||||
} else {
|
|
||||||
addition = w * (tempOffsetPage - offsetPage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.scroll.pages.length === 1) {
|
|
||||||
addition = 0;
|
|
||||||
} else if (offsetPage >= this.scroll.pages.length - 1) {
|
|
||||||
if (Math.abs(dist) > self.options.threshold && direction < 0) {
|
|
||||||
addition = w * direction;
|
|
||||||
} else {
|
|
||||||
addition = w * (tempOffsetPage - offsetPage + this.scroll.pages.length - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.initTween(addition - dist, 150, 'bounce');
|
|
||||||
},
|
|
||||||
|
|
||||||
initTween: function(dist, duration, eventName) {
|
|
||||||
if (dist === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var elapse;
|
|
||||||
var self = this;
|
|
||||||
var startTime = new Date();
|
|
||||||
|
|
||||||
this.cancelTween();
|
|
||||||
this.emit(eventName + 'Start');
|
|
||||||
|
|
||||||
function round() {
|
|
||||||
elapse = new Date() - startTime;
|
|
||||||
if (elapse > duration) {
|
|
||||||
self.emit(eventName, { x: dist }, true);
|
|
||||||
self.emit(eventName + 'End');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.emit(eventName, { x: dist / duration * elapse }, false);
|
|
||||||
self.tweenRid = requestAnimationFrame(round);
|
|
||||||
}
|
|
||||||
round();
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelTween: function() {
|
|
||||||
cancelAnimationFrame(this.tweenRid);
|
|
||||||
},
|
|
||||||
|
|
||||||
initMove: function() {
|
|
||||||
var self = this;
|
|
||||||
var scroll = this.scroll;
|
|
||||||
var intervalTween = self.options.intervalTween;
|
|
||||||
|
|
||||||
this.clearMove();
|
|
||||||
|
|
||||||
function round() {
|
|
||||||
if ((scroll.currentIndex === scroll.pages.length - 1) && !scroll.options.loop) {
|
|
||||||
self.initTween(-self.wrapSize.width() * (scroll.pages.length - 1), 200, 'autoPlay');
|
|
||||||
} else {
|
|
||||||
self.initTween(self.wrapSize.width(), 200, 'autoPlay');
|
|
||||||
}
|
|
||||||
self.moveTid = setTimeout(round, intervalTween);
|
|
||||||
}
|
|
||||||
self.moveTid = setTimeout(round, intervalTween);
|
|
||||||
},
|
|
||||||
|
|
||||||
clearMove: function() {
|
|
||||||
clearTimeout(this.moveTid);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default SpringDummy;
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
|||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
var extend = Object.assign.bind(Object);
|
|
||||||
|
|
||||||
function EventEmitter() {
|
|
||||||
this.__events = {};
|
|
||||||
}
|
|
||||||
EventEmitter.prototype = {
|
|
||||||
on: function(name, cb) {
|
|
||||||
this.__events[name] || (this.__events[name] = []);
|
|
||||||
this.__events[name].push(cb);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
emit: function(name) {
|
|
||||||
var arr = this.__events[name];
|
|
||||||
var argus = Array.prototype.slice.call(arguments, 1);
|
|
||||||
var self = this;
|
|
||||||
if (arr) {
|
|
||||||
arr.forEach(function(cb) {
|
|
||||||
cb.apply(self, argus);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
removeListener: function(name, fn) {
|
|
||||||
if (!this.__events[name]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let index;
|
|
||||||
if (fn) {
|
|
||||||
index = this.__events[name].indexOf(fn);
|
|
||||||
if (index > 0) {
|
|
||||||
this.__events[name].splice(index, 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
delete this.__events[name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const isSupportRequestAnimationFrame = !Vue.prototype.$isServer &&
|
|
||||||
(window.requestAnimationFrame ||
|
|
||||||
window.mozRequestAnimationFrame ||
|
|
||||||
window.webkitRequestAnimationFrame ||
|
|
||||||
window.msRequestAnimationFrame);
|
|
||||||
const isSupportCancelAnimationFrame = !Vue.prototype.$isServer &&
|
|
||||||
(window.cancelAnimationFrame ||
|
|
||||||
window.mozCancelAnimationFrame ||
|
|
||||||
window.webkitCancelAnimationFrame ||
|
|
||||||
window.msCancelAnimationFrame);
|
|
||||||
|
|
||||||
const requestAnimationFrame = isSupportRequestAnimationFrame || function(callback, element) {
|
|
||||||
return window.setTimeout(callback, 1000 / 60);
|
|
||||||
};
|
|
||||||
|
|
||||||
const cancelAnimationFrame = isSupportCancelAnimationFrame || function(id) {
|
|
||||||
clearTimeout(id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const bindEvents = (elem, eventNames, fn) => {
|
|
||||||
eventNames = eventNames.split(/\s+/);
|
|
||||||
eventNames.forEach(eventName => elem.addEventListener(eventName, fn));
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeEvents = (elem, eventNames, fn) => {
|
|
||||||
eventNames = eventNames.split(/\s+/);
|
|
||||||
eventNames.forEach(eventName => elem.removeEventListener(eventName, fn));
|
|
||||||
};
|
|
||||||
|
|
||||||
export {
|
|
||||||
extend,
|
|
||||||
EventEmitter,
|
|
||||||
requestAnimationFrame,
|
|
||||||
cancelAnimationFrame,
|
|
||||||
bindEvents,
|
|
||||||
removeEvents
|
|
||||||
};
|
|
||||||
|
|
@ -3,9 +3,10 @@
|
|||||||
|
|
||||||
.van-card {
|
.van-card {
|
||||||
color: $text-color;
|
color: $text-color;
|
||||||
height: 90px;
|
height: 100px;
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
padding: 5px 15px 5px 115px;
|
padding: 5px 15px 5px 115px;
|
||||||
|
|
||||||
&:not(:first-child) {
|
&:not(:first-child) {
|
||||||
|
4
packages/vant-css/src/common/normalize.css
vendored
4
packages/vant-css/src/common/normalize.css
vendored
@ -29,3 +29,7 @@ ul {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
@ -1,53 +1,44 @@
|
|||||||
@import './common/var.css';
|
@import './common/var.css';
|
||||||
|
$van-swipe-indicator: 6px;
|
||||||
|
|
||||||
.van-swipe {
|
.van-swipe {
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 100%;
|
position: relative;
|
||||||
height: 100%;
|
user-select: none;
|
||||||
|
|
||||||
&__indicators {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 10px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__indicator {
|
|
||||||
width: 5px;
|
|
||||||
height: 5px;
|
|
||||||
display: inline-block;
|
|
||||||
border-radius: 100%;
|
|
||||||
background: $gray-dark;
|
|
||||||
opacity: .8;
|
|
||||||
margin: 0 3px;
|
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
&--active {
|
|
||||||
background: $orange;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__items {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-item {
|
&-item {
|
||||||
display: none;
|
float: left;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
}
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
img {
|
&__track {
|
||||||
width: 100%;
|
height: 100%;
|
||||||
height: auto;
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__indicators {
|
||||||
|
left: 50%;
|
||||||
|
bottom: 10px;
|
||||||
|
position: absolute;
|
||||||
|
height: $van-swipe-indicator;
|
||||||
|
transform: translate3d(-50%, 0, 0);
|
||||||
|
|
||||||
|
> i {
|
||||||
|
border-radius: 100%;
|
||||||
|
vertical-align: top;
|
||||||
|
display: inline-block;
|
||||||
|
background-color: $gray-dark;
|
||||||
|
width: $van-swipe-indicator;
|
||||||
|
height: $van-swipe-indicator;
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: $van-swipe-indicator;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:first-child {
|
.van-swipe__indicator {
|
||||||
display: block;
|
&--active {
|
||||||
|
background-color: $orange;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user