接上篇
- 先总结下上篇技巧:
- 提供一个v-model进行父子组件间数据交换。
- 使用vue内置动画实现移动的动画效果。
- 另外用watch监控value的变动来显示子组件变化。
- 这次要实现的功能:
- 完成可以正序播放和逆序播放功能。
- 完成鼠标移入停止,移出继续轮播功能。
- 加入轮播图小点功能,点击小点可以切换轮播页面。
- 建立index.html支持手机端大小。
- 加入轮播图左右按钮,可左右切换轮播。
代码
App.vue
<template>
<swiper v-model="selected" autoplay>
<swiperItem name="box1">
<div class="content" style="background:red">页面1</div>
</swiperItem>
<swiperItem name="box2">
<div class="content" style="background:green">页面2</div>
</swiperItem>
<swiperItem name="box3">
<div class="content" style="background:yellow">页面3</div>
</swiperItem>
</swiper>
</template>
<script>
import swiperItem from "./components/SwiperItem";
import swiper from "./components/Swiper";
export default {
data: () => {
return { selected: "box3" };
},
components: {
swiper,
swiperItem
}
};
</script>
<style lang="stylus">
.content {
width: 100%;
height: 300px;
text-align: center;
margin: auto;
}
</style>
Swiper.vue
<template>
<div class="swiper" @mouseenter="mouseEnter" @mouseleave="mouseLeave"
@touchstart="touchstart"
@touchend ="touchend"
@touchmove="touchmove"
>
<div class="viewport">
<slot></slot>
<div class="dot">
<span
v-for="(item,index) in len "
:key="index"
:class="{active:active===index}"
@click="select(index)"
>{{item}}</span>
</div>
<div class="sidebutton">
<div @click="leftclick">左</div>
<div @click="rightclick">右</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: ""
},
autoplay: {
type: Boolean,
default: true
}
},
methods: {
touchmove(e){
console.log('移动');
console.log(e);
},
touchstart(e){
this.startx = e.touches[0].clientX
clearInterval(this.timer)
},
touchend(e){
let endx = e.changedTouches[0].clientX
let distance = endx-this.startx
let trifNum = 20
if(distance>trifNum){
this.select(this.active-1)
}else if(distance < -trifNum){
this.select(this.active+1)
}
this.autoShow()
},
leftclick() {
let newIndex = this.active - 1;
this.select(newIndex);
},
rightclick() {
let newIndex = this.active + 1;
this.select(newIndex);
},
select(newIndex) {
this.prev = this.active;
if (newIndex === this.names.length) newIndex = 0;
if (newIndex === -1) newIndex = this.names.length - 1;
this.$emit("input", this.names[newIndex]);
},
showChild() {
this.currentName = this.value || this.$children[0].name;
this.$children.forEach(vm => {
this.$nextTick(() => {
vm.selected = this.currentName;
});
let updateIndex = this.active;
let reverse = updateIndex > this.prev ? false : true;
if (this.prev === 0 && updateIndex === this.len - 1) reverse = true;
if (this.prev === this.len - 1 && updateIndex === 0) reverse = false;
vm.reverse = reverse;
});
},
autoShow() {
if (this.autoplay) {
this.timer = setInterval(() => {
let index = this.active;
let newIndex;
let isreverse = false;
if (isreverse) {
newIndex = index - 1;
} else {
newIndex = index + 1;
}
this.select(newIndex);
}, 2000);
}
},
mouseEnter() {
clearInterval(this.timer);
this.timer = null;
},
mouseLeave() {
if (!this.timer) {
this.autoShow();
}
}
},
computed: {
active() {
return this.names.indexOf(this.currentName);
}
},
watch: {
value() {
this.showChild();
}
},
beforeDestroy() {
clearInterval(this.timer);
},
data() {
return { currentName: "", len: 0 };
},
mounted() {
this.names = this.$children.map(vm => vm.name);
this.len = this.names.length;
this.showChild();
this.autoShow();
this.prev = this.active;
}
};
</script>
<style lang="stylus">
.swiper {
border: 5px solid black;
height: 300px;
margin: auto;
text-align: center;
}
.viewport {
position: relative;
overflow: hidden;
width: 100%;
height: 100%;
}
.dot {
display: flex;
justify-content: center;
position: absolute;
left: 0;
right: 0;
bottom: 0;
span {
height: 30px;
width: 30px;
border-radius: 100%;
background-color: white;
text-align: center;
line-height: 30px;
border: 1px solid black;
margin: 0 10px 0;
cursor: pointer;
&.active {
background-color: red;
}
}
}
.sidebutton {
display: flex;
justify-content: space-between;
position: absolute;
width: 100%;
height: w = 30px;
top: 50%;
transform: translateY(-50%);
text-align: center;
line-height: w;
font-size: 15px;
div {
background-color: purple;
padding: 1px;
&:nth-child(1) {
border-top-left-radius: 80%;
border-bottom-left-radius: 80%;
}
&:nth-child(2) {
border-top-right-radius: 80%;
border-bottom-right-radius: 80%;
}
}
}
</style>
SwiperItem.vue
<template>
<transition>
<div class="swiper-item" v-if="isShow" :class="{reverse}">
<slot></slot>
</div>
</transition>
</template>
<script>
export default {
props: {
name: {
type: String,
required: true
}
},
data() {
return { selected: "", reverse: "" };
},
mounted() {},
computed: {
isShow() {
return this.name === this.selected;
}
}
};
</script>
<style lang="stylus">
.v-enter-active, .v-leave-active {
transition: all 0.5s linear;
}
.v-leave-to {
transform: translate(-100%);
}
.v-enter {
transform: translate(100%);
}
.v-leave-to.reverse {
transform: translate(100%);
}
.v-enter.reverse {
transform: translate(-100%);
}
.v-leave-active {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
</style>
- html就是直接感叹号出来的就不发了。使用vue serve 默认挂到id为app的节点。
- 总的来说还有不少缺点,样式不够美观,功能没完全放给用户,再学习一段时间后准备重弄个更好的试试。