[improvement] rename packages dir to src (#3659)
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<demo-section>
|
||||
<demo-block :title="$t('basicUsage')">
|
||||
<van-swipe
|
||||
:autoplay="3000"
|
||||
indicator-color="white"
|
||||
>
|
||||
<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>
|
||||
</demo-block>
|
||||
|
||||
<demo-block :title="$t('title2')">
|
||||
<van-swipe :autoplay="3000">
|
||||
<van-swipe-item
|
||||
v-for="(image, index) in images"
|
||||
:key="index"
|
||||
>
|
||||
<img v-lazy="image">
|
||||
</van-swipe-item>
|
||||
</van-swipe>
|
||||
</demo-block>
|
||||
|
||||
<demo-block :title="$t('title3')">
|
||||
<van-swipe
|
||||
indicator-color="white"
|
||||
@change="onChange"
|
||||
>
|
||||
<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>
|
||||
</demo-block>
|
||||
|
||||
<demo-block :title="$t('title4')">
|
||||
<van-swipe
|
||||
vertical
|
||||
:autoplay="3000"
|
||||
indicator-color="white"
|
||||
class="demo-swipe--vertical"
|
||||
>
|
||||
<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>
|
||||
</demo-block>
|
||||
|
||||
<demo-block :title="$t('title5')">
|
||||
<van-swipe
|
||||
:autoplay="3000"
|
||||
:width="300"
|
||||
:loop="false"
|
||||
indicator-color="white"
|
||||
>
|
||||
<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>
|
||||
</demo-block>
|
||||
|
||||
<demo-block :title="$t('title6')">
|
||||
<van-swipe @change="onChange">
|
||||
<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>
|
||||
|
||||
<template #indicator>
|
||||
<div class="custom-indicator">{{ current + 1 }}/4</div>
|
||||
</template>
|
||||
</van-swipe>
|
||||
</demo-block>
|
||||
</demo-section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
i18n: {
|
||||
'zh-CN': {
|
||||
title2: '图片懒加载',
|
||||
title3: '监听 change 事件',
|
||||
title4: '纵向滚动',
|
||||
title5: '设置滑块大小',
|
||||
title6: '自定义指示器',
|
||||
message: '当前 Swipe 索引:'
|
||||
},
|
||||
'en-US': {
|
||||
title2: 'Image Lazyload',
|
||||
title3: 'Change Event',
|
||||
title4: 'Vertical Scrolling',
|
||||
title5: 'Set Swiper Item Size',
|
||||
title6: 'Custom indicator',
|
||||
message: 'Current Swipe index:'
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
current: 0,
|
||||
images: [
|
||||
'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: {
|
||||
onChange(index) {
|
||||
this.current = index;
|
||||
this.$toast(this.$t('message') + index);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import '../../style/var';
|
||||
|
||||
.demo-swipe {
|
||||
padding-bottom: 30px;
|
||||
|
||||
.van-swipe {
|
||||
cursor: pointer;
|
||||
|
||||
&-item {
|
||||
color: @white;
|
||||
font-size: 20px;
|
||||
line-height: 150px;
|
||||
text-align: center;
|
||||
|
||||
&:nth-child(even) {
|
||||
background-color: #39a9ed;
|
||||
}
|
||||
|
||||
&:nth-child(odd) {
|
||||
background-color: #66c6f2;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 240px;
|
||||
padding: 30px 60px;
|
||||
background-color: @white;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&--vertical {
|
||||
height: 200px;
|
||||
|
||||
.van-swipe-item {
|
||||
line-height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-indicator {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
padding: 2px 5px;
|
||||
color: @white;
|
||||
font-size: 12px;
|
||||
background: rgba(0, 0, 0, .1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,160 @@
|
||||
# Swipe
|
||||
|
||||
### Install
|
||||
|
||||
``` javascript
|
||||
import { Swipe, SwipeItem } from 'vant';
|
||||
|
||||
Vue.use(Swipe).use(SwipeItem);
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
Use `autoplay` prop to set autoplay interval
|
||||
|
||||
```html
|
||||
<van-swipe :autoplay="3000" indicator-color="white">
|
||||
<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>
|
||||
```
|
||||
|
||||
### Image Lazyload
|
||||
|
||||
Use [Lazyload](#/en-US/lazyload) component to lazyload image
|
||||
|
||||
```html
|
||||
<van-swipe>
|
||||
<van-swipe-item v-for="(image, index) in images" :key="index">
|
||||
<img v-lazy="image" />
|
||||
</van-swipe-item>
|
||||
</van-swipe>
|
||||
```
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
images: [
|
||||
'https://img.yzcdn.cn/1.jpg',
|
||||
'https://img.yzcdn.cn/2.jpg'
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### change event
|
||||
|
||||
```html
|
||||
<van-swipe @change="onChange">
|
||||
<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>
|
||||
```
|
||||
|
||||
```js
|
||||
export default {
|
||||
methods: {
|
||||
onChange(index) {
|
||||
Toast('Current Swipe index:' + index);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Vertical Scrolling
|
||||
|
||||
```html
|
||||
<van-swipe :autoplay="3000" vertical>
|
||||
<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>
|
||||
```
|
||||
|
||||
### Set Swiper Item Size
|
||||
|
||||
```html
|
||||
<van-swipe :autoplay="3000" :width="300">
|
||||
<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>
|
||||
```
|
||||
|
||||
### Custom Indicator
|
||||
|
||||
```html
|
||||
<van-swipe @change="onChange">
|
||||
<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>
|
||||
|
||||
<div class="custom-indicator" slot="indicator">
|
||||
{{ current + 1 }}/4
|
||||
</div>
|
||||
</van-swipe>
|
||||
```
|
||||
|
||||
```js
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
current: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(index) {
|
||||
this.current = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Swipe Props
|
||||
|
||||
| Attribute | Description | Type | Default |
|
||||
|------|------|------|------|
|
||||
| autoplay | Autoplay interval (ms) | `Number` | - |
|
||||
| duration | Animation duration (ms) | `Number` | `500` |
|
||||
| loop | Whether to enable loop | `Boolean` | `true` |
|
||||
| vertical | Vertical Scrolling | `Boolean` | `false` |
|
||||
| touchable | Whether touchable | `Boolean` | `true` |
|
||||
| show-indicators | Whether to show indicators | `Boolean` | `true` |
|
||||
| indicator-color | Indicator color | `String` | `#1989fa` |
|
||||
| initial-swipe | Index of initial swipe, start from 0 | `Number` | `0` |
|
||||
| width | Set Swiper Item Width | `Number` | `0` |
|
||||
| height | Set Swiper Item Height | `Number` | `0` |
|
||||
|
||||
### Swipe Events
|
||||
|
||||
| Event | Description | Arguments |
|
||||
|------|------|------|
|
||||
| change | Triggered when current swipe change | index: index of current swipe |
|
||||
|
||||
### Swipe Methods
|
||||
|
||||
Use ref to get swipe instance and call instance methods
|
||||
|
||||
| Name | Attribute | Return value | Description |
|
||||
|------|------|------|------|
|
||||
| swipeTo | index: target index | void | Swipe to target index |
|
||||
|
||||
### Swipe Slots
|
||||
|
||||
| Name | Description |
|
||||
|------|------|
|
||||
| default | Content |
|
||||
| indicator | Custom indicator |
|
||||
@@ -0,0 +1,299 @@
|
||||
import { createNamespace } from '../utils';
|
||||
import { preventDefault } from '../utils/dom/event';
|
||||
import { TouchMixin } from '../mixins/touch';
|
||||
import { BindEventMixin } from '../mixins/bind-event';
|
||||
|
||||
const [createComponent, bem] = createNamespace('swipe');
|
||||
|
||||
export default createComponent({
|
||||
mixins: [
|
||||
TouchMixin,
|
||||
BindEventMixin(function (bind, isBind) {
|
||||
bind(window, 'resize', this.onResize, true);
|
||||
|
||||
if (isBind) {
|
||||
this.initialize();
|
||||
} else {
|
||||
this.clear();
|
||||
}
|
||||
})
|
||||
],
|
||||
|
||||
props: {
|
||||
width: Number,
|
||||
height: Number,
|
||||
autoplay: Number,
|
||||
vertical: Boolean,
|
||||
initialSwipe: Number,
|
||||
indicatorColor: String,
|
||||
loop: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
touchable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showIndicators: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 500
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
computedWidth: 0,
|
||||
computedHeight: 0,
|
||||
offset: 0,
|
||||
active: 0,
|
||||
deltaX: 0,
|
||||
deltaY: 0,
|
||||
swipes: [],
|
||||
swiping: false
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
swipes() {
|
||||
this.initialize();
|
||||
},
|
||||
|
||||
initialSwipe() {
|
||||
this.initialize();
|
||||
},
|
||||
|
||||
autoplay(autoplay) {
|
||||
if (!autoplay) {
|
||||
this.clear();
|
||||
} else {
|
||||
this.autoPlay();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
count() {
|
||||
return this.swipes.length;
|
||||
},
|
||||
|
||||
delta() {
|
||||
return this.vertical ? this.deltaY : this.deltaX;
|
||||
},
|
||||
|
||||
size() {
|
||||
return this[this.vertical ? 'computedHeight' : 'computedWidth'];
|
||||
},
|
||||
|
||||
trackSize() {
|
||||
return this.count * this.size;
|
||||
},
|
||||
|
||||
activeIndicator() {
|
||||
return (this.active + this.count) % this.count;
|
||||
},
|
||||
|
||||
isCorrectDirection() {
|
||||
const expect = this.vertical ? 'vertical' : 'horizontal';
|
||||
return this.direction === expect;
|
||||
},
|
||||
|
||||
trackStyle() {
|
||||
const mainAxis = this.vertical ? 'height' : 'width';
|
||||
const crossAxis = this.vertical ? 'width' : 'height';
|
||||
return {
|
||||
[mainAxis]: `${this.trackSize}px`,
|
||||
[crossAxis]: this[crossAxis] ? `${this[crossAxis]}px` : '',
|
||||
transitionDuration: `${this.swiping ? 0 : this.duration}ms`,
|
||||
transform: `translate${this.vertical ? 'Y' : 'X'}(${this.offset}px)`
|
||||
};
|
||||
},
|
||||
|
||||
indicatorStyle() {
|
||||
return {
|
||||
backgroundColor: this.indicatorColor
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// initialize swipe position
|
||||
initialize(active = this.initialSwipe) {
|
||||
clearTimeout(this.timer);
|
||||
if (this.$el) {
|
||||
const rect = this.$el.getBoundingClientRect();
|
||||
this.computedWidth = this.width || rect.width;
|
||||
this.computedHeight = this.height || rect.height;
|
||||
}
|
||||
this.swiping = true;
|
||||
this.active = active;
|
||||
this.offset = this.count > 1 ? -this.size * this.active : 0;
|
||||
this.swipes.forEach(swipe => {
|
||||
swipe.offset = 0;
|
||||
});
|
||||
this.autoPlay();
|
||||
},
|
||||
|
||||
onResize() {
|
||||
this.initialize(this.activeIndicator);
|
||||
},
|
||||
|
||||
onTouchStart(event) {
|
||||
if (!this.touchable) return;
|
||||
|
||||
this.clear();
|
||||
this.swiping = true;
|
||||
this.touchStart(event);
|
||||
this.correctPosition();
|
||||
},
|
||||
|
||||
onTouchMove(event) {
|
||||
if (!this.touchable || !this.swiping) return;
|
||||
|
||||
this.touchMove(event);
|
||||
|
||||
if (this.isCorrectDirection) {
|
||||
preventDefault(event, true);
|
||||
this.move({ offset: Math.min(Math.max(this.delta, -this.size), this.size) });
|
||||
}
|
||||
},
|
||||
|
||||
onTouchEnd() {
|
||||
if (!this.touchable || !this.swiping) return;
|
||||
|
||||
if (this.delta && this.isCorrectDirection) {
|
||||
const offset = this.vertical ? this.offsetY : this.offsetX;
|
||||
this.move({
|
||||
pace: offset > 0 ? (this.delta > 0 ? -1 : 1) : 0,
|
||||
emitChange: true
|
||||
});
|
||||
}
|
||||
|
||||
this.swiping = false;
|
||||
this.autoPlay();
|
||||
},
|
||||
|
||||
move({ pace = 0, offset = 0, emitChange }) {
|
||||
const { delta, active, count, swipes, trackSize } = this;
|
||||
const atFirst = active === 0;
|
||||
const atLast = active === count - 1;
|
||||
const outOfBounds =
|
||||
!this.loop &&
|
||||
((atFirst && (offset > 0 || pace < 0)) || (atLast && (offset < 0 || pace > 0)));
|
||||
|
||||
if (outOfBounds || count <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (swipes[0]) {
|
||||
swipes[0].offset = atLast && (delta < 0 || pace > 0) ? trackSize : 0;
|
||||
}
|
||||
|
||||
if (swipes[count - 1]) {
|
||||
swipes[count - 1].offset = atFirst && (delta > 0 || pace < 0) ? -trackSize : 0;
|
||||
}
|
||||
|
||||
if (pace && active + pace >= -1 && active + pace <= count) {
|
||||
this.active += pace;
|
||||
|
||||
if (emitChange) {
|
||||
this.$emit('change', this.activeIndicator);
|
||||
}
|
||||
}
|
||||
|
||||
this.offset = Math.round(offset - this.active * this.size);
|
||||
},
|
||||
|
||||
swipeTo(index) {
|
||||
this.swiping = true;
|
||||
this.resetTouchStatus();
|
||||
this.correctPosition();
|
||||
setTimeout(() => {
|
||||
this.swiping = false;
|
||||
this.move({
|
||||
pace: (index % this.count) - this.active,
|
||||
emitChange: true
|
||||
});
|
||||
}, 30);
|
||||
},
|
||||
|
||||
correctPosition() {
|
||||
if (this.active <= -1) {
|
||||
this.move({ pace: this.count });
|
||||
}
|
||||
if (this.active >= this.count) {
|
||||
this.move({ pace: -this.count });
|
||||
}
|
||||
},
|
||||
|
||||
clear() {
|
||||
clearTimeout(this.timer);
|
||||
},
|
||||
|
||||
autoPlay() {
|
||||
const { autoplay } = this;
|
||||
|
||||
if (autoplay && this.count > 1) {
|
||||
this.clear();
|
||||
this.timer = setTimeout(() => {
|
||||
this.swiping = true;
|
||||
this.resetTouchStatus();
|
||||
this.correctPosition();
|
||||
|
||||
setTimeout(() => {
|
||||
this.swiping = false;
|
||||
this.move({
|
||||
pace: 1,
|
||||
emitChange: true
|
||||
});
|
||||
this.autoPlay();
|
||||
}, 30);
|
||||
}, autoplay);
|
||||
}
|
||||
},
|
||||
|
||||
renderIndicator() {
|
||||
const { count, activeIndicator } = this;
|
||||
const slot = this.slots('indicator');
|
||||
|
||||
if (slot) {
|
||||
return slot;
|
||||
}
|
||||
|
||||
if (this.showIndicators && count > 1) {
|
||||
return (
|
||||
<div class={bem('indicators', { vertical: this.vertical })}>
|
||||
{Array(...Array(count)).map((empty, index) => (
|
||||
<i
|
||||
class={bem('indicator', { active: index === activeIndicator })}
|
||||
style={index === activeIndicator ? this.indicatorStyle : null}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render(h) {
|
||||
return (
|
||||
<div class={bem()}>
|
||||
<div
|
||||
ref="track"
|
||||
style={this.trackStyle}
|
||||
class={bem('track')}
|
||||
onTouchstart={this.onTouchStart}
|
||||
onTouchmove={this.onTouchMove}
|
||||
onTouchend={this.onTouchEnd}
|
||||
onTouchcancel={this.onTouchEnd}
|
||||
>
|
||||
{this.slots()}
|
||||
</div>
|
||||
{this.renderIndicator()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
@import '../style/var';
|
||||
|
||||
.van-swipe {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
|
||||
&__track {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__indicators {
|
||||
position: absolute;
|
||||
bottom: @swipe-indicator-margin;
|
||||
left: 50%;
|
||||
display: flex;
|
||||
transform: translateX(-50%);
|
||||
|
||||
&--vertical {
|
||||
top: 50%;
|
||||
bottom: auto;
|
||||
left: @swipe-indicator-margin;
|
||||
flex-direction: column;
|
||||
transform: translateY(-50%);
|
||||
|
||||
.van-swipe__indicator:not(:last-child) {
|
||||
margin-bottom: @swipe-indicator-size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__indicator {
|
||||
width: @swipe-indicator-size;
|
||||
height: @swipe-indicator-size;
|
||||
background-color: @swipe-indicator-inactive-background-color;
|
||||
border-radius: 100%;
|
||||
opacity: @swipe-indicator-inactive-opacity;
|
||||
transition: opacity .2s;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: @swipe-indicator-size;
|
||||
}
|
||||
|
||||
&--active {
|
||||
background-color: @swipe-indicator-active-background-color;
|
||||
opacity: @swipe-indicator-active-opacity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders demo correctly 1`] = `
|
||||
<div>
|
||||
<div>
|
||||
<div class="van-swipe">
|
||||
<div class="van-swipe__track" style="width: 400px; transition-duration: 0ms; transform: translateX(0px);">
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);">1</div>
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);">2</div>
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);">3</div>
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);">4</div>
|
||||
</div>
|
||||
<div class="van-swipe__indicators"><i class="van-swipe__indicator van-swipe__indicator--active" style="background-color: white;"></i><i class="van-swipe__indicator"></i><i class="van-swipe__indicator"></i><i class="van-swipe__indicator"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="van-swipe">
|
||||
<div class="van-swipe__track" style="width: 400px; transition-duration: 0ms; transform: translateX(0px);">
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);"><img data-src="https://img.yzcdn.cn/public_files/2017/09/05/3bd347e44233a868c99cf0fe560232be.jpg" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" lazy="loading"></div>
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);"><img data-src="https://img.yzcdn.cn/public_files/2017/09/05/c0dab461920687911536621b345a0bc9.jpg" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" lazy="loading"></div>
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);"><img data-src="https://img.yzcdn.cn/public_files/2017/09/05/4e3ea0898b1c2c416eec8c11c5360833.jpg" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" lazy="loading"></div>
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);"><img data-src="https://img.yzcdn.cn/public_files/2017/09/05/fd08f07665ed67d50e11b32a21ce0682.jpg" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" lazy="loading"></div>
|
||||
</div>
|
||||
<div class="van-swipe__indicators"><i class="van-swipe__indicator van-swipe__indicator--active"></i><i class="van-swipe__indicator"></i><i class="van-swipe__indicator"></i><i class="van-swipe__indicator"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="van-swipe">
|
||||
<div class="van-swipe__track" style="width: 400px; transition-duration: 0ms; transform: translateX(0px);">
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);">1</div>
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);">2</div>
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);">3</div>
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);">4</div>
|
||||
</div>
|
||||
<div class="van-swipe__indicators"><i class="van-swipe__indicator van-swipe__indicator--active" style="background-color: white;"></i><i class="van-swipe__indicator"></i><i class="van-swipe__indicator"></i><i class="van-swipe__indicator"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="demo-swipe--vertical van-swipe">
|
||||
<div class="van-swipe__track" style="height: 400px; transition-duration: 0ms; transform: translateY(0px);">
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100px; transform: translateY(0px);">1</div>
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100px; transform: translateY(0px);">2</div>
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100px; transform: translateY(0px);">3</div>
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100px; transform: translateY(0px);">4</div>
|
||||
</div>
|
||||
<div class="van-swipe__indicators van-swipe__indicators--vertical"><i class="van-swipe__indicator van-swipe__indicator--active" style="background-color: white;"></i><i class="van-swipe__indicator"></i><i class="van-swipe__indicator"></i><i class="van-swipe__indicator"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="van-swipe">
|
||||
<div class="van-swipe__track" style="width: 1200px; transition-duration: 0ms; transform: translateX(0px);">
|
||||
<div class="van-swipe-item" style="width: 300px; height: 100%; transform: translateX(0px);">1</div>
|
||||
<div class="van-swipe-item" style="width: 300px; height: 100%; transform: translateX(0px);">2</div>
|
||||
<div class="van-swipe-item" style="width: 300px; height: 100%; transform: translateX(0px);">3</div>
|
||||
<div class="van-swipe-item" style="width: 300px; height: 100%; transform: translateX(0px);">4</div>
|
||||
</div>
|
||||
<div class="van-swipe__indicators"><i class="van-swipe__indicator van-swipe__indicator--active" style="background-color: white;"></i><i class="van-swipe__indicator"></i><i class="van-swipe__indicator"></i><i class="van-swipe__indicator"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="van-swipe">
|
||||
<div class="van-swipe__track" style="width: 400px; transition-duration: 0ms; transform: translateX(0px);">
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);">1</div>
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);">2</div>
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);">3</div>
|
||||
<div class="van-swipe-item" style="width: 100px; height: 100%; transform: translateX(0px);">4</div>
|
||||
</div>
|
||||
<div class="custom-indicator">1/4</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,17 @@
|
||||
import Demo from '../demo';
|
||||
import demoTest from '../../../test/demo-test';
|
||||
import { mockGetBoundingClientRect } from '../../../test/utils';
|
||||
|
||||
let restore;
|
||||
|
||||
demoTest(Demo, {
|
||||
hookBeforeTest: () => {
|
||||
restore = mockGetBoundingClientRect({
|
||||
width: 100,
|
||||
height: 100
|
||||
});
|
||||
},
|
||||
hookAfterTest: () => {
|
||||
restore();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,126 @@
|
||||
import Swipe from '..';
|
||||
import SwipeItem from '../../swipe-item';
|
||||
import { mount, triggerDrag, later } from '../../../test/utils';
|
||||
|
||||
const Component = {
|
||||
template: `
|
||||
<swipe ref="swipe" v-bind="$props">
|
||||
<swipe-item :style="style">1</swipe-item>
|
||||
<swipe-item :style="style">2</swipe-item>
|
||||
<swipe-item :style="style">3</swipe-item>
|
||||
</swipe>
|
||||
`,
|
||||
components: {
|
||||
Swipe,
|
||||
SwipeItem
|
||||
},
|
||||
props: {
|
||||
vertical: Boolean,
|
||||
loop: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
touchable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
autoplay: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
initialSwipe: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
style: {
|
||||
width: '100px',
|
||||
height: '100px'
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
test('swipeTo', async () => {
|
||||
const wrapper = mount(Component);
|
||||
const { swipe } = wrapper.vm.$refs;
|
||||
swipe.swipeTo(2);
|
||||
|
||||
await later(30);
|
||||
expect(swipe.active).toEqual(2);
|
||||
});
|
||||
|
||||
test('initial swipe', () => {
|
||||
const wrapper = mount(Component);
|
||||
const { swipe } = wrapper.vm.$refs;
|
||||
|
||||
expect(swipe.active).toEqual(0);
|
||||
wrapper.setProps({ initialSwipe: 2 });
|
||||
expect(swipe.active).toEqual(2);
|
||||
});
|
||||
|
||||
test('vertical swipe', () => {
|
||||
const wrapper = mount(Component, {
|
||||
propsData: {
|
||||
vertical: true
|
||||
}
|
||||
});
|
||||
const { swipe } = wrapper.vm.$refs;
|
||||
const track = wrapper.find('.van-swipe__track');
|
||||
|
||||
triggerDrag(track, 0, -100);
|
||||
expect(swipe.active).toEqual(1);
|
||||
});
|
||||
|
||||
test('untouchable', () => {
|
||||
const wrapper = mount(Component, {
|
||||
propsData: {
|
||||
touchable: false
|
||||
}
|
||||
});
|
||||
const { swipe } = wrapper.vm.$refs;
|
||||
const track = wrapper.find('.van-swipe__track');
|
||||
|
||||
triggerDrag(track, 100, 0);
|
||||
expect(swipe.active).toEqual(0);
|
||||
});
|
||||
|
||||
test('loop', () => {
|
||||
const wrapper = mount(Component);
|
||||
const { swipe } = wrapper.vm.$refs;
|
||||
const track = wrapper.find('.van-swipe__track');
|
||||
|
||||
triggerDrag(track, -100, 0);
|
||||
expect(swipe.active).toEqual(1);
|
||||
triggerDrag(track, -100, 0);
|
||||
expect(swipe.active).toEqual(2);
|
||||
triggerDrag(track, -100, 0);
|
||||
expect(swipe.active).toEqual(3);
|
||||
triggerDrag(track, -100, 0);
|
||||
expect(swipe.active).toEqual(1);
|
||||
triggerDrag(track, 100, 0);
|
||||
expect(swipe.active).toEqual(0);
|
||||
triggerDrag(track, 100, 0);
|
||||
expect(swipe.active).toEqual(-1);
|
||||
triggerDrag(track, 100, 0);
|
||||
expect(swipe.active).toEqual(1);
|
||||
});
|
||||
|
||||
test('not loop', () => {
|
||||
const wrapper = mount(Component, {
|
||||
propsData: {
|
||||
loop: false
|
||||
}
|
||||
});
|
||||
const { swipe } = wrapper.vm.$refs;
|
||||
const track = wrapper.find('.van-swipe__track');
|
||||
|
||||
triggerDrag(track, -100, 0);
|
||||
expect(swipe.active).toEqual(1);
|
||||
triggerDrag(track, -100, 0);
|
||||
expect(swipe.active).toEqual(2);
|
||||
triggerDrag(track, -100, 0);
|
||||
expect(swipe.active).toEqual(2);
|
||||
});
|
||||
@@ -0,0 +1,166 @@
|
||||
# Swipe 轮播
|
||||
|
||||
### 引入
|
||||
|
||||
``` javascript
|
||||
import { Swipe, SwipeItem } from 'vant';
|
||||
|
||||
Vue.use(Swipe).use(SwipeItem);
|
||||
```
|
||||
|
||||
## 代码演示
|
||||
|
||||
### 基础用法
|
||||
|
||||
通过`autoplay`属性设置自动轮播间隔
|
||||
|
||||
```html
|
||||
<van-swipe :autoplay="3000" indicator-color="white">
|
||||
<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/lazyload) 组件实现图片懒加载
|
||||
|
||||
```html
|
||||
<van-swipe :autoplay="3000">
|
||||
<van-swipe-item v-for="(image, index) in images" :key="index">
|
||||
<img v-lazy="image" />
|
||||
</van-swipe-item>
|
||||
</van-swipe>
|
||||
```
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
images: [
|
||||
'https://img.yzcdn.cn/1.jpg',
|
||||
'https://img.yzcdn.cn/2.jpg'
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 监听 change 事件
|
||||
|
||||
```html
|
||||
<van-swipe @change="onChange">
|
||||
<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>
|
||||
```
|
||||
|
||||
```js
|
||||
export default {
|
||||
methods: {
|
||||
onChange(index) {
|
||||
Toast('当前 Swipe 索引:' + index);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 纵向滚动
|
||||
|
||||
```html
|
||||
<van-swipe :autoplay="3000" vertical>
|
||||
<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>
|
||||
```
|
||||
|
||||
### 控制滑块大小
|
||||
|
||||
```html
|
||||
<van-swipe :autoplay="3000" :width="300">
|
||||
<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>
|
||||
```
|
||||
|
||||
### 自定义指示器
|
||||
|
||||
```html
|
||||
<van-swipe @change="onChange">
|
||||
<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>
|
||||
|
||||
<div class="custom-indicator" slot="indicator">
|
||||
{{ current + 1 }}/4
|
||||
</div>
|
||||
</van-swipe>
|
||||
```
|
||||
|
||||
```js
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
current: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(index) {
|
||||
this.current = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Swipe Props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
|------|------|------|------|------|
|
||||
| autoplay | 自动轮播间隔,单位为 ms | `Number` | - | - |
|
||||
| duration | 动画时长,单位为 ms | `Number` | `500` | - |
|
||||
| initial-swipe | 初始位置索引值 | `Number` | `0` | - |
|
||||
| loop | 是否开启循环播放 | `Boolean` | `true` | - |
|
||||
| show-indicators | 是否显示指示器 | `Boolean` | `true` | - |
|
||||
| indicator-color | 指示器颜色 | `String` | `#1989fa` | 1.4.5 |
|
||||
| vertical | 是否为纵向滚动 | `Boolean` | `false` | 1.1.1 |
|
||||
| touchable | 是否可以通过手势滑动 | `Boolean` | `true` | 1.1.1 |
|
||||
| width | 滑块宽度 | `Number` | `0` | 1.2.1 |
|
||||
| height | 滑块高度 | `Number` | `0` | 1.2.1 |
|
||||
|
||||
### Swipe Events
|
||||
|
||||
| 事件名 | 说明 | 回调参数 |
|
||||
|------|------|------|
|
||||
| change | 每一页轮播结束后触发 | index, 当前页的索引 |
|
||||
|
||||
### SwipeItem Events
|
||||
|
||||
| 事件名 | 说明 | 回调参数 |
|
||||
|------|------|------|
|
||||
| click | 点击时触发 | - |
|
||||
|
||||
### Swipe 方法
|
||||
|
||||
通过 ref 可以获取到 swipe 实例并调用实例方法
|
||||
|
||||
| 方法名 | 参数 | 返回值 | 介绍 |
|
||||
|------|------|------|------|
|
||||
| swipeTo | index: 目标位置的索引 | void | 滚动到目标位置 |
|
||||
|
||||
### Swipe Slots
|
||||
|
||||
| 名称 | 说明 |
|
||||
|------|------|
|
||||
| default | 轮播内容 |
|
||||
| indicator | 自定义指示器 |
|
||||
Reference in New Issue
Block a user