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` |
|
||||
|
||||
#### 省市县列表数据格式
|
||||
请参考 [Area](/zanui/vue/component/area) 组件。
|
||||
请参考 [Area](#/zh-CN/component/area) 组件。
|
||||
|
@ -1,10 +1,34 @@
|
||||
<style>
|
||||
.demo-swipe {
|
||||
padding-bottom: 30px;
|
||||
|
||||
.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 {
|
||||
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 {
|
||||
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'
|
||||
],
|
||||
images: [
|
||||
'https://img.yzcdn.cn/upload_files/2017/03/14/FmTPs0SeyQaAOSK1rRe1sL8RcwSY.jpeg',
|
||||
'https://img.yzcdn.cn/upload_files/2017/03/15/FvexrWlG_WxtCE9Omo5l27n_mAG_.jpeg'
|
||||
'https://img.yzcdn.cn/public_files/2017/09/05/3bd347e44233a868c99cf0fe560232be.jpg',
|
||||
'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>
|
||||
@ -46,14 +62,27 @@ Vue.component(SwipeItem.name, SwipeItem);
|
||||
### 代码演示
|
||||
|
||||
#### 基础用法
|
||||
通过`autoplay`属性设置自动轮播间隔
|
||||
|
||||
:::demo 基础用法
|
||||
```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-item v-for="(img, index) in images" :key="index">
|
||||
<a href="https://youzan.com" target="_blank">
|
||||
<img v-lazy="img" alt="">
|
||||
</a>
|
||||
<van-swipe-item v-for="(image, index) in images" :key="index">
|
||||
<img v-lazy="image" />
|
||||
</van-swipe-item>
|
||||
</van-swipe>
|
||||
```
|
||||
@ -63,84 +92,25 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
images: [
|
||||
'https://img.yzcdn.cn/upload_files/2017/03/14/FmTPs0SeyQaAOSK1rRe1sL8RcwSY.jpeg',
|
||||
'https://img.yzcdn.cn/upload_files/2017/03/15/FvexrWlG_WxtCE9Omo5l27n_mAG_.jpeg'
|
||||
'https://img.yzcdn.cn/1.jpg',
|
||||
'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
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|
||||
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|
||||
|-----------|-----------|-----------|-------------|-------------|
|
||||
| autoPlay | 是否自动轮播 | `Boolean` | `false` | `true`, `false` |
|
||||
| showIndicators | 是否显示指示器 | `Boolean` | `true` | `true`, `false` |
|
||||
| autoplay | 自动轮播间隔,单位为 ms | `Number` | - | - |
|
||||
| duration | 动画时长,单位为 ms | `Number` | `500` | - |
|
||||
| showIndicators | 是否显示指示器 | `Boolean` | `true` | - |
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件名 | 说明 | 参数 |
|
||||
| 事件名 | 说明 | 参数 |
|
||||
|-----------|-----------|-----------|
|
||||
| `pagechange:end` | 每一页轮播结束后触发 | `(elem, currIndex)`:`elem`为触发页当前的DOM节点 |
|
||||
| change | 每一页轮播结束后触发 | index, 当前页的索引 |
|
||||
|
@ -4,6 +4,10 @@
|
||||
|
||||
<style lang="postcss">
|
||||
body {
|
||||
color: #333;
|
||||
line-height: 1;
|
||||
background-color: #f8f8f8;
|
||||
font-family: Arial, Helvetica, "STHeiti STXihei", "Microsoft YaHei", Tohoma, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="van-swipe-item">
|
||||
<div class="van-swipe-item" :style="style">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
@ -10,13 +10,27 @@ export default {
|
||||
|
||||
beforeCreate() {
|
||||
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() {
|
||||
const index = this.$parent.swipes.indexOf(this);
|
||||
if (index > -1) {
|
||||
this.$parent.swipes.splice(index, 1);
|
||||
}
|
||||
this.$parent.swipes.splice(this.index, 1);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -1,97 +1,161 @@
|
||||
<template>
|
||||
<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>
|
||||
</div>
|
||||
<div
|
||||
class="van-swipe__indicators"
|
||||
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 class="van-swipe__indicators" v-if="showIndicators && count > 1">
|
||||
<i v-for="index in count" :class="{ 'van-swipe__indicator--active': index - 1 === activeIndicator }" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Input from './input';
|
||||
import Scroll from './scroll';
|
||||
import SpringDummy from './spring_dummy';
|
||||
|
||||
export default {
|
||||
name: 'van-swipe',
|
||||
|
||||
props: {
|
||||
autoPlay: Boolean,
|
||||
autoplay: Number,
|
||||
showIndicators: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 500
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
currIndex: 0,
|
||||
swipes: []
|
||||
offset: 0,
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
active: 0,
|
||||
deltaX: 0,
|
||||
swipes: [],
|
||||
childrenOffset: [],
|
||||
direction: '',
|
||||
currentDuration: 0,
|
||||
width: window.innerWidth
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.input = new Input(this.$el, {
|
||||
listenMoving: true
|
||||
});
|
||||
|
||||
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;
|
||||
this.move(0);
|
||||
this.autoPlay();
|
||||
},
|
||||
|
||||
watch: {
|
||||
swipes(value) {
|
||||
if (this.autoPlay && value.length > 1) {
|
||||
this.dummy.initMove();
|
||||
} else {
|
||||
this.dummy.clearMove();
|
||||
}
|
||||
this.scroll.update();
|
||||
return value;
|
||||
computed: {
|
||||
count() {
|
||||
return this.swipes.length;
|
||||
},
|
||||
|
||||
autoPlay(value) {
|
||||
if (value && this.swipes.length > 1) {
|
||||
this.dummy.initMove();
|
||||
} else {
|
||||
this.dummy.clearMove();
|
||||
}
|
||||
return value;
|
||||
trackStyle() {
|
||||
return this.count === 1 ? {} : {
|
||||
paddingLeft: this.width + 'px',
|
||||
width: (this.count + 2) * this.width + 'px',
|
||||
transitionDuration: `${this.currentDuration}ms`,
|
||||
transform: `translate3d(${this.offset}px, 0, 0)`
|
||||
};
|
||||
},
|
||||
|
||||
activeIndicator() {
|
||||
return (this.active + this.count) % this.count;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onPageChangeEnd(page, currIndex) {
|
||||
this.currIndex = +currIndex;
|
||||
this.$emit('pagechange:end', page, currIndex);
|
||||
onTouchStart(event) {
|
||||
clearTimeout(this.timer);
|
||||
|
||||
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 {
|
||||
color: $text-color;
|
||||
height: 90px;
|
||||
height: 100px;
|
||||
background: #fafafa;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
padding: 5px 15px 5px 115px;
|
||||
|
||||
&: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;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
@ -1,53 +1,44 @@
|
||||
@import './common/var.css';
|
||||
$van-swipe-indicator: 6px;
|
||||
|
||||
.van-swipe {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
&__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%;
|
||||
}
|
||||
position: relative;
|
||||
user-select: none;
|
||||
|
||||
&-item {
|
||||
display: none;
|
||||
float: left;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
&__track {
|
||||
height: 100%;
|
||||
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 {
|
||||
display: block;
|
||||
.van-swipe__indicator {
|
||||
&--active {
|
||||
background-color: $orange;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user