2017-01-16 02:45:11 -06:00
|
|
|
|
<template>
|
2017-01-16 02:59:46 -06:00
|
|
|
|
<div :class="classes">
|
2018-04-18 15:33:35 +09:00
|
|
|
|
<button type="button" :class="arrowClasses" class="left" @click="arrowEvent(-1)">
|
2018-06-25 14:19:12 +08:00
|
|
|
|
<Icon type="ios-arrow-back"></Icon>
|
2017-01-17 14:29:51 -06:00
|
|
|
|
</button>
|
2017-01-16 04:00:52 -06:00
|
|
|
|
<div :class="[prefixCls + '-list']">
|
2019-09-19 11:54:59 +08:00
|
|
|
|
<div :class="[prefixCls + '-track', showCopyTrack ? '' : 'higher']" :style="trackStyles" ref="originTrack" @click="handlerClickEvent('currentIndex')">
|
2017-01-16 04:00:52 -06:00
|
|
|
|
<slot></slot>
|
|
|
|
|
</div>
|
2019-09-19 11:54:59 +08:00
|
|
|
|
<div :class="[prefixCls + '-track', showCopyTrack ? 'higher' : '']" :style="copyTrackStyles" @click="handlerClickEvent('copyTrackIndex')" ref="copyTrack" v-if="loop">
|
2017-10-24 16:47:12 +08:00
|
|
|
|
</div>
|
2017-01-16 04:00:52 -06:00
|
|
|
|
</div>
|
2018-04-18 15:33:35 +09:00
|
|
|
|
<button type="button" :class="arrowClasses" class="right" @click="arrowEvent(1)">
|
2018-06-25 14:19:12 +08:00
|
|
|
|
<Icon type="ios-arrow-forward"></Icon>
|
2017-01-17 14:29:51 -06:00
|
|
|
|
</button>
|
|
|
|
|
<ul :class="dotsClasses">
|
|
|
|
|
<template v-for="n in slides.length">
|
2017-03-02 15:36:39 +08:00
|
|
|
|
<li :class="[n - 1 === currentIndex ? prefixCls + '-active' : '']"
|
|
|
|
|
@click="dotsEvent('click', n - 1)"
|
|
|
|
|
@mouseover="dotsEvent('hover', n - 1)">
|
2018-04-18 15:33:35 +09:00
|
|
|
|
<button type="button" :class="[radiusDot ? 'radius' : '']"></button>
|
2017-01-17 14:29:51 -06:00
|
|
|
|
</li>
|
|
|
|
|
</template>
|
|
|
|
|
</ul>
|
2017-01-16 02:45:11 -06:00
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<script>
|
2017-01-16 02:59:46 -06:00
|
|
|
|
import Icon from '../icon/icon.vue';
|
2017-01-17 20:03:49 -06:00
|
|
|
|
import { getStyle, oneOf } from '../../utils/assist';
|
2017-07-10 13:33:14 +08:00
|
|
|
|
import { on, off } from '../../utils/dom';
|
2017-01-16 02:59:46 -06:00
|
|
|
|
|
|
|
|
|
const prefixCls = 'ivu-carousel';
|
2017-01-16 02:45:11 -06:00
|
|
|
|
|
|
|
|
|
export default {
|
2017-01-16 02:59:46 -06:00
|
|
|
|
name: 'Carousel',
|
2017-01-17 17:31:51 -06:00
|
|
|
|
components: { Icon },
|
2017-01-16 02:59:46 -06:00
|
|
|
|
props: {
|
2017-01-17 14:29:51 -06:00
|
|
|
|
arrow: {
|
|
|
|
|
type: String,
|
2017-01-17 20:03:49 -06:00
|
|
|
|
default: 'hover',
|
|
|
|
|
validator (value) {
|
|
|
|
|
return oneOf(value, ['hover', 'always', 'never']);
|
|
|
|
|
}
|
2017-01-16 02:59:46 -06:00
|
|
|
|
},
|
|
|
|
|
autoplay: {
|
|
|
|
|
type: Boolean,
|
2017-01-18 11:49:34 +08:00
|
|
|
|
default: false
|
2017-01-16 02:59:46 -06:00
|
|
|
|
},
|
|
|
|
|
autoplaySpeed: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: 2000
|
|
|
|
|
},
|
2017-10-24 16:47:12 +08:00
|
|
|
|
loop: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false
|
|
|
|
|
},
|
2017-01-16 02:59:46 -06:00
|
|
|
|
easing: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: 'ease'
|
|
|
|
|
},
|
|
|
|
|
dots: {
|
2017-01-17 14:29:51 -06:00
|
|
|
|
type: String,
|
2017-01-17 20:03:49 -06:00
|
|
|
|
default: 'inside',
|
|
|
|
|
validator (value) {
|
|
|
|
|
return oneOf(value, ['inside', 'outside', 'none']);
|
|
|
|
|
}
|
2017-01-17 14:29:51 -06:00
|
|
|
|
},
|
2017-10-24 16:47:12 +08:00
|
|
|
|
radiusDot: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false
|
|
|
|
|
},
|
2017-01-17 14:29:51 -06:00
|
|
|
|
trigger: {
|
|
|
|
|
type: String,
|
2017-01-17 20:03:49 -06:00
|
|
|
|
default: 'click',
|
|
|
|
|
validator (value) {
|
|
|
|
|
return oneOf(value, ['click', 'hover']);
|
|
|
|
|
}
|
2017-01-16 02:59:46 -06:00
|
|
|
|
},
|
2017-03-02 15:36:39 +08:00
|
|
|
|
value: {
|
2017-01-16 04:12:46 -06:00
|
|
|
|
type: Number,
|
|
|
|
|
default: 0
|
2017-01-17 16:39:07 -06:00
|
|
|
|
},
|
|
|
|
|
height: {
|
|
|
|
|
type: [String, Number],
|
2017-01-17 20:03:49 -06:00
|
|
|
|
default: 'auto',
|
|
|
|
|
validator (value) {
|
2017-02-14 14:41:21 -06:00
|
|
|
|
return value === 'auto' || Object.prototype.toString.call(value) === '[object Number]';
|
2017-01-17 20:03:49 -06:00
|
|
|
|
}
|
2017-01-16 02:59:46 -06:00
|
|
|
|
}
|
|
|
|
|
},
|
2017-01-16 04:00:52 -06:00
|
|
|
|
data () {
|
|
|
|
|
return {
|
|
|
|
|
prefixCls: prefixCls,
|
|
|
|
|
listWidth: 0,
|
|
|
|
|
trackWidth: 0,
|
2017-01-17 16:39:07 -06:00
|
|
|
|
trackOffset: 0,
|
2017-10-24 16:47:12 +08:00
|
|
|
|
trackCopyOffset: 0,
|
|
|
|
|
showCopyTrack: false,
|
2017-01-16 04:00:52 -06:00
|
|
|
|
slides: [],
|
2017-01-16 04:12:46 -06:00
|
|
|
|
slideInstances: [],
|
2017-01-17 16:39:07 -06:00
|
|
|
|
timer: null,
|
2017-03-02 15:36:39 +08:00
|
|
|
|
ready: false,
|
2017-10-24 16:47:12 +08:00
|
|
|
|
currentIndex: this.value,
|
|
|
|
|
trackIndex: this.value,
|
|
|
|
|
copyTrackIndex: this.value,
|
|
|
|
|
hideTrackPos: -1, // 默认左滑
|
2017-01-17 17:31:51 -06:00
|
|
|
|
};
|
2017-01-16 04:00:52 -06:00
|
|
|
|
},
|
2017-01-16 02:59:46 -06:00
|
|
|
|
computed: {
|
|
|
|
|
classes () {
|
|
|
|
|
return [
|
2017-01-17 16:39:07 -06:00
|
|
|
|
`${prefixCls}`
|
2017-01-16 02:59:46 -06:00
|
|
|
|
];
|
2017-01-16 04:00:52 -06:00
|
|
|
|
},
|
|
|
|
|
trackStyles () {
|
2019-09-19 15:49:20 +08:00
|
|
|
|
// #6076
|
|
|
|
|
const visibleStyle = this.trackIndex === -1 ? 'hidden' : 'visible';
|
2017-01-16 04:00:52 -06:00
|
|
|
|
return {
|
2017-01-16 04:12:46 -06:00
|
|
|
|
width: `${this.trackWidth}px`,
|
2017-10-24 16:47:12 +08:00
|
|
|
|
transform: `translate3d(${-this.trackOffset}px, 0px, 0px)`,
|
2019-09-19 15:49:20 +08:00
|
|
|
|
transition: `transform 500ms ${this.easing}`,
|
|
|
|
|
visibility : visibleStyle
|
2017-01-16 04:00:52 -06:00
|
|
|
|
};
|
2017-01-17 14:29:51 -06:00
|
|
|
|
},
|
2017-10-24 16:47:12 +08:00
|
|
|
|
copyTrackStyles () {
|
|
|
|
|
return {
|
|
|
|
|
width: `${this.trackWidth}px`,
|
|
|
|
|
transform: `translate3d(${-this.trackCopyOffset}px, 0px, 0px)`,
|
|
|
|
|
transition: `transform 500ms ${this.easing}`,
|
|
|
|
|
position: 'absolute',
|
2019-09-19 15:49:20 +08:00
|
|
|
|
//top: 0
|
2017-10-24 16:47:12 +08:00
|
|
|
|
};
|
|
|
|
|
},
|
2017-01-17 14:29:51 -06:00
|
|
|
|
arrowClasses () {
|
|
|
|
|
return [
|
|
|
|
|
`${prefixCls}-arrow`,
|
|
|
|
|
`${prefixCls}-arrow-${this.arrow}`
|
2017-01-17 17:31:51 -06:00
|
|
|
|
];
|
2017-01-17 14:29:51 -06:00
|
|
|
|
},
|
|
|
|
|
dotsClasses () {
|
|
|
|
|
return [
|
|
|
|
|
`${prefixCls}-dots`,
|
2017-01-17 14:39:17 -06:00
|
|
|
|
`${prefixCls}-dots-${this.dots}`
|
2017-01-17 17:31:51 -06:00
|
|
|
|
];
|
2017-01-16 04:00:52 -06:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
2019-09-19 11:54:59 +08:00
|
|
|
|
handlerClickEvent(type){
|
|
|
|
|
this.$emit('on-click',this[type]);
|
|
|
|
|
},
|
2017-01-16 04:00:52 -06:00
|
|
|
|
// find option component
|
|
|
|
|
findChild (cb) {
|
|
|
|
|
const find = function (child) {
|
|
|
|
|
const name = child.$options.componentName;
|
|
|
|
|
|
|
|
|
|
if (name) {
|
|
|
|
|
cb(child);
|
|
|
|
|
} else if (child.$children.length) {
|
|
|
|
|
child.$children.forEach((innerChild) => {
|
|
|
|
|
find(innerChild, cb);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2017-01-17 16:39:07 -06:00
|
|
|
|
if (this.slideInstances.length || !this.$children) {
|
2017-01-16 04:00:52 -06:00
|
|
|
|
this.slideInstances.forEach((child) => {
|
|
|
|
|
find(child);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
this.$children.forEach((child) => {
|
|
|
|
|
find(child);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
2017-10-24 16:47:12 +08:00
|
|
|
|
// copy trackDom
|
|
|
|
|
initCopyTrackDom () {
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.$refs.copyTrack.innerHTML = this.$refs.originTrack.innerHTML;
|
|
|
|
|
});
|
|
|
|
|
},
|
2017-03-02 15:36:39 +08:00
|
|
|
|
updateSlides (init) {
|
2017-01-16 04:00:52 -06:00
|
|
|
|
let slides = [];
|
|
|
|
|
let index = 1;
|
|
|
|
|
|
|
|
|
|
this.findChild((child) => {
|
|
|
|
|
slides.push({
|
|
|
|
|
$el: child.$el
|
|
|
|
|
});
|
|
|
|
|
child.index = index++;
|
|
|
|
|
|
|
|
|
|
if (init) {
|
|
|
|
|
this.slideInstances.push(child);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.slides = slides;
|
2017-01-16 18:02:51 -06:00
|
|
|
|
this.updatePos();
|
2017-01-16 04:00:52 -06:00
|
|
|
|
},
|
|
|
|
|
updatePos () {
|
|
|
|
|
this.findChild((child) => {
|
|
|
|
|
child.width = this.listWidth;
|
2017-01-17 16:39:07 -06:00
|
|
|
|
child.height = typeof this.height === 'number' ? `${this.height}px` : this.height;
|
2017-01-16 04:00:52 -06:00
|
|
|
|
});
|
2019-09-19 15:49:20 +08:00
|
|
|
|
const slidesLength = this.slides.length || 0
|
|
|
|
|
this.trackWidth = slidesLength * this.listWidth;
|
2017-01-16 04:00:52 -06:00
|
|
|
|
},
|
|
|
|
|
// use when slot changed
|
|
|
|
|
slotChange () {
|
2017-01-16 18:10:31 -06:00
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.slides = [];
|
|
|
|
|
this.slideInstances = [];
|
|
|
|
|
|
|
|
|
|
this.updateSlides(true, true);
|
|
|
|
|
this.updatePos();
|
2017-01-20 11:54:25 -06:00
|
|
|
|
this.updateOffset();
|
2017-01-16 18:10:31 -06:00
|
|
|
|
});
|
2017-01-16 04:00:52 -06:00
|
|
|
|
},
|
|
|
|
|
handleResize () {
|
2017-01-17 16:39:07 -06:00
|
|
|
|
this.listWidth = parseInt(getStyle(this.$el, 'width'));
|
|
|
|
|
this.updatePos();
|
2017-01-20 11:54:25 -06:00
|
|
|
|
this.updateOffset();
|
2017-01-16 04:12:46 -06:00
|
|
|
|
},
|
2017-10-24 16:47:12 +08:00
|
|
|
|
updateTrackPos (index) {
|
|
|
|
|
if (this.showCopyTrack) {
|
|
|
|
|
this.trackIndex = index;
|
|
|
|
|
} else {
|
|
|
|
|
this.copyTrackIndex = index;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
updateTrackIndex (index) {
|
|
|
|
|
if (this.showCopyTrack) {
|
|
|
|
|
this.copyTrackIndex = index;
|
|
|
|
|
} else {
|
|
|
|
|
this.trackIndex = index;
|
|
|
|
|
}
|
2018-06-28 17:41:25 +08:00
|
|
|
|
this.currentIndex = index;
|
2017-10-24 16:47:12 +08:00
|
|
|
|
},
|
2017-01-16 17:43:20 -06:00
|
|
|
|
add (offset) {
|
2017-10-24 16:47:12 +08:00
|
|
|
|
// 获取单个轨道的图片数
|
|
|
|
|
let slidesLen = this.slides.length;
|
|
|
|
|
// 如果是无缝滚动,需要初始化双轨道位置
|
|
|
|
|
if (this.loop) {
|
|
|
|
|
if (offset > 0) {
|
|
|
|
|
// 初始化左滑轨道位置
|
|
|
|
|
this.hideTrackPos = -1;
|
|
|
|
|
} else {
|
|
|
|
|
// 初始化右滑轨道位置
|
|
|
|
|
this.hideTrackPos = slidesLen;
|
|
|
|
|
}
|
|
|
|
|
this.updateTrackPos(this.hideTrackPos);
|
|
|
|
|
}
|
|
|
|
|
// 获取当前展示图片的索引值
|
2018-02-28 15:38:20 +08:00
|
|
|
|
const oldIndex = this.showCopyTrack ? this.copyTrackIndex : this.trackIndex;
|
|
|
|
|
let index = oldIndex + offset;
|
2017-10-24 16:47:12 +08:00
|
|
|
|
while (index < 0) index += slidesLen;
|
2017-10-25 09:30:46 +08:00
|
|
|
|
if (((offset > 0 && index === slidesLen) || (offset < 0 && index === slidesLen - 1)) && this.loop) {
|
2017-10-24 16:47:12 +08:00
|
|
|
|
// 极限值(左滑:当前索引为总图片张数, 右滑:当前索引为总图片张数 - 1)切换轨道
|
|
|
|
|
this.showCopyTrack = !this.showCopyTrack;
|
|
|
|
|
this.trackIndex += offset;
|
|
|
|
|
this.copyTrackIndex += offset;
|
|
|
|
|
} else {
|
|
|
|
|
if (!this.loop) index = index % this.slides.length;
|
|
|
|
|
this.updateTrackIndex(index);
|
|
|
|
|
}
|
2018-02-28 15:38:20 +08:00
|
|
|
|
this.currentIndex = index === this.slides.length ? 0 : index;
|
|
|
|
|
this.$emit('on-change', oldIndex, this.currentIndex);
|
|
|
|
|
this.$emit('input', this.currentIndex);
|
2017-01-16 17:43:20 -06:00
|
|
|
|
},
|
2017-01-21 20:40:31 -06:00
|
|
|
|
arrowEvent (offset) {
|
|
|
|
|
this.setAutoplay();
|
|
|
|
|
this.add(offset);
|
|
|
|
|
},
|
2017-01-17 14:29:51 -06:00
|
|
|
|
dotsEvent (event, n) {
|
2017-10-24 16:47:12 +08:00
|
|
|
|
let curIndex = this.showCopyTrack ? this.copyTrackIndex : this.trackIndex;
|
|
|
|
|
if (event === this.trigger && curIndex !== n) {
|
|
|
|
|
this.updateTrackIndex(n);
|
2017-03-02 15:36:39 +08:00
|
|
|
|
this.$emit('input', n);
|
2017-01-20 11:54:25 -06:00
|
|
|
|
// Reset autoplay timer when trigger be activated
|
|
|
|
|
this.setAutoplay();
|
2017-01-17 14:29:51 -06:00
|
|
|
|
}
|
2017-01-16 04:28:34 -06:00
|
|
|
|
},
|
2017-01-16 04:12:46 -06:00
|
|
|
|
setAutoplay () {
|
2017-01-16 04:28:34 -06:00
|
|
|
|
window.clearInterval(this.timer);
|
2017-01-16 04:12:46 -06:00
|
|
|
|
if (this.autoplay) {
|
|
|
|
|
this.timer = window.setInterval(() => {
|
2017-01-21 20:40:31 -06:00
|
|
|
|
this.add(1);
|
2017-01-16 04:12:46 -06:00
|
|
|
|
}, this.autoplaySpeed);
|
|
|
|
|
}
|
2017-01-17 16:39:07 -06:00
|
|
|
|
},
|
|
|
|
|
updateOffset () {
|
|
|
|
|
this.$nextTick(() => {
|
2017-10-24 16:47:12 +08:00
|
|
|
|
/* hack: revise copyTrack offset (1px) */
|
|
|
|
|
let ofs = this.copyTrackIndex > 0 ? -1 : 1;
|
|
|
|
|
this.trackOffset = this.trackIndex * this.listWidth;
|
|
|
|
|
this.trackCopyOffset = this.copyTrackIndex * this.listWidth + ofs;
|
2017-01-17 16:39:07 -06:00
|
|
|
|
});
|
2017-01-16 02:59:46 -06:00
|
|
|
|
}
|
2017-01-16 04:00:52 -06:00
|
|
|
|
},
|
2017-01-16 04:12:46 -06:00
|
|
|
|
watch: {
|
|
|
|
|
autoplay () {
|
|
|
|
|
this.setAutoplay();
|
|
|
|
|
},
|
2017-01-16 04:28:34 -06:00
|
|
|
|
autoplaySpeed () {
|
|
|
|
|
this.setAutoplay();
|
|
|
|
|
},
|
2017-10-24 16:47:12 +08:00
|
|
|
|
trackIndex () {
|
|
|
|
|
this.updateOffset();
|
|
|
|
|
},
|
|
|
|
|
copyTrackIndex () {
|
2017-01-17 16:39:07 -06:00
|
|
|
|
this.updateOffset();
|
|
|
|
|
},
|
|
|
|
|
height () {
|
|
|
|
|
this.updatePos();
|
2017-03-02 15:36:39 +08:00
|
|
|
|
},
|
|
|
|
|
value (val) {
|
2018-06-28 18:09:22 +08:00
|
|
|
|
// this.currentIndex = val;
|
|
|
|
|
// this.trackIndex = val;
|
|
|
|
|
this.updateTrackIndex(val);
|
|
|
|
|
this.setAutoplay();
|
2017-01-16 04:12:46 -06:00
|
|
|
|
}
|
|
|
|
|
},
|
2017-03-02 15:36:39 +08:00
|
|
|
|
mounted () {
|
|
|
|
|
this.updateSlides(true);
|
2017-01-16 04:00:52 -06:00
|
|
|
|
this.handleResize();
|
2017-01-16 04:28:34 -06:00
|
|
|
|
this.setAutoplay();
|
2017-07-10 13:33:14 +08:00
|
|
|
|
// window.addEventListener('resize', this.handleResize, false);
|
|
|
|
|
on(window, 'resize', this.handleResize);
|
2017-01-16 04:00:52 -06:00
|
|
|
|
},
|
|
|
|
|
beforeDestroy () {
|
2017-07-10 13:33:14 +08:00
|
|
|
|
// window.removeEventListener('resize', this.handleResize, false);
|
|
|
|
|
off(window, 'resize', this.handleResize);
|
2017-01-16 02:59:46 -06:00
|
|
|
|
}
|
2017-01-16 02:45:11 -06:00
|
|
|
|
};
|
|
|
|
|
</script>
|