Vue组件开发--轮播图的实现
在我们实际项⽬中,轮播图(⾛马灯)是⼀个使⽤很频繁的功能组件。今天就⾃⼰动⼿实现⼀个简单的轮播图组件,在实际动⼿中加深对基础知识的理解,在项⽬中更加熟练的
去应⽤。
⾸先整理下实现此组件的基本功能以及思路:
1.把⼏张图⽚放置在⼀个容器中,每次只显⽰⼀张
2.根据图⽚在容器中的偏移来控制当前显⽰哪张图⽚
3.通过计时器来控制循环显⽰
4.根据指⽰控件可⼿动控制显⽰哪张图⽚
5.显⽰当前图⽚的描述信息
⼩技巧:图⽚播放完最后⼀张切换到第⼀张的时候,会有明显的切换闪烁的痕迹,为了做到顺滑的切换,我们会在最后位置插⼊第⼀张图⽚作为过渡。
效果图:
⾸先准备素材图⽚,在assets⽂件夹下新建⼀个img⽂件夹,把素材图⽚放置在这个⽬录下⾯。
既然是和业务不相关的独⽴组件,图⽚列表需要从使⽤的⽗组件进⾏传⼊,⾸先定义下⽗组件需要传值的数据结构:
[
{ title: "1", path: require("@/assets/img/1.jpg"), url: "#" },
{ title: "2", path: require("@/assets/img/2.jpg"), url: "#" },
{ title: "3", path: require("@/assets/img/3.jpg"), url: "#" },
{ title: "4", path: require("@/assets/img/4.jpg"), url: "#" },
{ title: "5", path: require("@/assets/img/5.jpg"), url: "#" }
]
title:显⽰图⽚的标题信息
path:图⽚加载的路径
url:点击图⽚后跳转的地址
知识点:
其中的@符号是Vue中的别名,表⽰src⽬录。这是Vue默认配置好的,可以在fig.js(使⽤vue cli 3之前的版本请在fig.js中配置)中配置resolve、alias。
⼀、实现图⽚轮播
新建⼀个名称为Carousel的vue组件。在props中定义个名称为list的参数,类型定义为数组,默认值为空,并且作为必传值。如下:
props: {
list: {
type: Array,
required: true,
default() {
return []
}
}
}
知识点:
1.⽗⼦组件传值:通过Prop向⼦组件传递数据。Prop类型可以是⼀个组数或是⼀个对象,数组⽅式⽆法指定参数的类型。如上⾯通过对象声明的list参数为例,类型类数组(Array),必传(required),默认值(default)实现步骤:
1.在模板中展⽰list数据,为了切换的更加顺滑,我们把数据的第⼀条数据提取出来,为了更改的获取第⼀条数据,我们把它放在计算属性firstItem中。
2.更改样式,让图⽚显⽰在同⼀⾏,并且隐藏滚动条。
3.设置图⽚容器⼤⼩,和⼀张图⽚的⼤⼩保持⼀致,只允许显⽰⼀张图⽚。在data中定义两个属性,width和height。在这⾥我们定义sizeStyle的计算属性,来响应式的设置容器
⼤⼩。
到这⾥基本内容已经布局好了,下⾯就开始让图⽚动起来。
1.在methods中新增begin⽅法,在mounted中调⽤
2.在begin⽅法中定义⼀个计时器,2s触发⼀次
3.然后在methods中定义⼀个move⽅法,在begin⽅法中调⽤此⽅法
4.在move⽅法中定义根据当前需要显⽰的图⽚index计算偏移量,并绑定到容器的style attribute上
代码如下:
<style scoped>
.carousel {
display: flex;
overflow: hidden;
position: relative;
margin: 0 auto;
width: 100%;
height: 100%;
}
</style>
<template>
<div class="carousel" :>
<div : v-for="(item) in list" :key="item.title">
<a :href="item.url">
<img :src="item.path" :alt="item.title" :/>
</a>
</div>
<!-- 过渡图⽚ -->
<div :>
<a :href="firstItem.url">
<img :src="firstItem.path" :alt="firstItem.title" :/>
</a>
</div>
</div>
</template>
<script>
let timer;
export default {
name: "Carousel",
props: {
list: {
type: Array,
required: true,
default() {
return [];
}
}
},
data() {
return {
width: 300,
height: 200,
currentIndex: 1,
scrollStyle: { transform: "translateX(0px)" }
};
},
mounted() {
this.begin();
},
computed: {
firstItem() {
return this.list[0];
},
sizeStyle() {
return { width: this.width + "px", height: this.height + "px" };
}
},
methods: {
begin() {
timer = setInterval(() => {
}, 2000);
},
move() {
const index = this.currentIndex % this.list.length;
let end = -index * this.width;
this.scrollStyle = {
transform: "translateX(" + end + "px)"
};
this.currentIndex++;
}
},
destroyed() {
clearInterval(timer);
timer = null;
}
};
</script>
知识点:
1.v-for指令:列表渲染
2.v-bind指令(缩写:):响应式的更改html attribute
3.class和style的绑定
⼆、添加动画
此组件创建了两个动画效果:平移和渐变。
1.平移
平移效果是使⽤计时器改变偏移量来实现的,主要代码如下:<template>
<div class="carousel" :>
<div : v-for="(item) in list" :key="item.title">
<a :href="item.url">
<img
:src="item.path"
:alt="item.title"
:
/>
</a>
</div>
<!-- 过渡图⽚ -->
<div :>
<a :href="firstItem.url">
<img
:src="firstItem.path"
:alt="firstItem.title"
:
/>
</a>
</div>
</div>
</template>
<script>
let timer;
let transtionTimer;
export default {
name: "Carousel",
props: {
list: {
type: Array,
required: true,
default() {
return [];
}
}
},
data() {
return {
width: 300,
height: 200,
currentIndex: 1,
scrollStyle: { transform: "translateX(0px)" }
};
},
mounted() {
this.begin();
},
computed: {
firstItem() {
return this.list[0];
},
number() {
return this.list.length + 1;
},
sizeStyle() {
return { width: this.width + "px", height: this.height + "px" };
}
},
methods: {
begin() {
timer = setInterval(() => {
if (transtionTimer) {
return;
}
this.scroll();
}, 2000);
},
scroll() {
let start = -(((this.currentIndex - 1) % this.number) * this.width);
let end = -(this.currentIndex % this.number) * this.width;
if (end == 0) {
start = 0;
end = -this.width;
}
},
move(start, end) {
let offset = this.width / 20;
//定时器,实现平移效果
transtionTimer = setInterval(() => {
start = start - offset;
if (start <= end) {
clearInterval(transtionTimer);
transtionTimer = null;
start = end;
if (this.currentIndex % this.number == 0) {
this.currentIndex = 1;
} else {
this.currentIndex++;
// 过渡效果:移动到最后⼀张图后(我们在最后加的第⼀张图⽚),把偏移量设置为0,⾃动切换成第⼀图
if (this.currentIndex == this.number) {
this.currentIndex = 1;
start = 0;
}
}
}
this.scrollStyle = {
transform: "translateX(" + start + "px)"
};
}, 20);
}
},
destroyed() {
clearInterval(timer);
timer = null;
clearInterval(transtionTimer);
transtionTimer = null;
}
};
</script>
<style scoped>
.carousel {
display: flex;
overflow: hidden;
position: relative;
margin: 0 auto;
width: 100%;
height: 100%;
}
</style>
2.渐变效果
渐变效果主要是通过css动画来实现的。未显⽰的图⽚可见度默认为0.1,展⽰后设置为1,然后通过css animation实现动画效果。
主要代码如下:
/* 动画效果 */
.selected {
opacity: 1;
animation: myOpacity 0.6s;
}
.unSelect {
opacity: 0.1;
}
@keyframes myOpacity {
0% {
opacity: 0.1;
}
25% {
opacity: 0.25;
}
50% {
opacity: 0.5;
}
75% {
opacity: 0.75;
}
100% {
opacity: 1;
}
}
<div class="carousel" :>
<div : v-for="(item,index) in list" :key="item.title">
<a :href="item.url">
<img
:src="item.path"
:alt="item.title"
:
:class="(currentIndex==index+1)?'selected':'unSelect'"
/>
</a>
</div>
<!-- 过渡图⽚ -->
<div :>
<a :href="firstItem.url">
<img
:src="firstItem.path"
:alt="firstItem.title"
:
:class="(currentIndex==1)?'selected':'unSelect'"
/>
</a>
</div>
</div>
三、添加指⽰控件
指⽰控件和图⽚数量⼀致,并⼀⼀对应。当切换到当前图⽚后,指⽰控件⾼亮显⽰,并且可以⼿动点击指⽰控件来展⽰对应的图⽚。
1.⾸先根据图⽚数组来加载指⽰控件
2.在控件上添加click监听事件
3.如果当前图⽚被展⽰,指⽰控件⾼亮显⽰
代码如下:
html:
<div class="dotList">
<span @click="handleSwitch(index)" class="dot" v-for="(item,index) in list" :key="item.title">
<div v-show="currentIndex==index+1" class="dot-actived"></div>
</span>
</div>
css:
.dotList {
display: flex;
position: absolute;
z-index: 1000;
right: 20px;
bottom: 40px;
}
.dot {
width: 10px;
height: 10px;
margin: 0 2px;
background: #fff;
border-radius: 50%;
display: flex;
cursor: pointer;
}
.dot-actived {
width: 10px;
height: 10px;
border-radius: 50%;
background: orange;
}
js:
handleSwitch(index) {
clearInterval(transtionTimer);
transtionTimer = null;
clearInterval(timer);
timer = null;
this.currentIndex = index + 1;
this.scrollStyle = {
transform: "translateX(" + -(index % this.number) * this.width + "px)",
transition: "opacity 0.6s linear"
};
this.begin();
}
知识点:1.v-show指令:⽤于条件性地渲染⼀块内容。元素总是会被渲染,并且只是简单地基于 CSS
进⾏切换2.v-for指令:⽤于条件性地渲染⼀块内容。是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件和⼦组件适当地被销毁四、添加Title描述信息
Title主要是展⽰图⽚的描述信息,并且⽗组件可以设置是否显⽰。
1.⾸先在props中添加showTitle参数,默认是true
2.在图⽚列表中添加span标签,来展⽰title信息
主要代码如下:
<div class="carousel" :>
<div : v-for="(item,index) in list" :key="item.title">
<a :href="item.url">
<img
:src="item.path"
:alt="item.title"
:
:class="(currentIndex==index+1)?'selected':'unSelect'"
/>
</a>
<span v-if="showTitle" class="title">{{item.title}}</span>
</div>
<!-- 过渡图⽚ -->
<div :>
<a :href="firstItem.url">
<img
:src="firstItem.path"
:
alt="firstItem.title"
:
:class="(currentIndex==1)?'selected':'unSelect'"
/>
</a>
<span v-if="showTitle" class="title">{{firstItem.title}}</span>
</div>
<!-- 指⽰控件 -->
<div class="dotList">
<span @click="handleSwitch(index)" class="dot" v-for="(item,index) in list" :key="item.title">
<div v-show="currentIndex==index+1" class="dot-actived"></div>
</span>
</div>
</div>
props: {
list: {
type: Array,
required: true,
default() {
return [];
}
},
showTitle: {
type: Boolean,
default() {
return true;
}
}
}
.title {
height: 30px;
background: rgba(213, 213, 230, 0.4);
text-align: center;
position: absolute;
transform: translateY(-100%);
color: #fff;
display: flex;
width: 100%;
justify-content: center;
}
完整代码如下:
1<template>
2<div class="carousel" :>
3<div : v-for="(item,index) in list" :key="item.title">
4<a :href="item.url">
5<img
6:src="item.path"
7          :alt="item.title"
8          :
9          :class="(currentIndex==index+1)?'selected':'unSelect'"
10/>
11</a>
12<span v-if="showTitle" class="title">{{item.title}}</span>
13</div>
14<!-- 过渡图⽚ -->
15<div :>
16<a :href="firstItem.url">
17<img
18:src="firstItem.path"
19          :alt="firstItem.title"
20          :
21          :class="(currentIndex==1)?'selected':'unSelect'"
22/>
23</a>
24<span v-if="showTitle" class="title">{{firstItem.title}}</span>
25</div>
26<!-- 指⽰控件 -->
27<div class="dotList">
28<span @click="handleSwitch(index)" class="dot" v-for="(item,index) in list" :key="item.title">
29<div v-show="currentIndex==index+1" class="dot-actived"></div>
30</span>
31</div>
32</div>
33</template>
34
35<script>
36 let timer;
37 let transtionTimer;
38 export default {
39  name: "Carousel",
40  props: {
41    list: {
42      type: Array,
43      required: true,
44default() {
45return [];
46      }
47    },
48    showTitle: {
49      type: Boolean,
50default() {
51return true;
52      }
53    }
54  },
55  data() {
56return {
57      width: 300,
58      height: 200,
59      currentIndex: 1,
60      scrollStyle: { transform: "translateX(0px)" }
61    };
62  },
63  mounted() {
64this.begin();
65  },
66  computed: {
67    firstItem() {
68return this.list[0];
69    },
70    number() {
71return this.list.length + 1;
72    },
73    sizeStyle() {
74return { width: this.width + "px", height: this.height + "px" };
75    }
76  },
77  methods: {
78    begin() {
79      timer = setInterval(() => {
80if (transtionTimer) {
81return;
82        }
83this.scroll();
84      }, 2000);
85    },
86    scroll() {
87      let start = -(((this.currentIndex - 1) % this.number) * this.width);
js实现轮播图最简代码
88      let end = -(this.currentIndex % this.number) * this.width;
89if (end == 0) {
90        start = 0;
91        end = -this.width;
92      }
94    },
95    move(start, end) {
96      let offset = this.width / 20;
97//定时器,实现平移效果
98      transtionTimer = setInterval(() => {
99        start = start - offset;
100if (start <= end) {
101          clearInterval(transtionTimer);
102          transtionTimer = null;
103          start = end;
104if (this.currentIndex % this.number == 0) {
105this.currentIndex = 1;
106          } else {
107this.currentIndex++;
108// 过渡效果:移动到最后⼀张图后(我们在最后加的第⼀张图⽚),把偏移量设置为0,⾃动切换成第⼀图109if (this.currentIndex == this.number) {
110this.currentIndex = 1;
111              start = 0;
112            }
113          }
114        }
115this.scrollStyle = {
116          transform: "translateX(" + start + "px)"
117        };
118      }, 20);
119    },

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。