Vue組件開發--輪播圖的實現

在咱們實際項目中,輪播圖(走馬燈)是一個使用很頻繁的功能組件。今天就本身動手實現一個簡單的輪播圖組件,在實際動手中加深對基礎知識的理解,在項目中更加熟練的去應用。css

首先整理下實現此組件的基本功能以及思路:
1.把幾張圖片放置在一個容器中,每次只顯示一張
2.根據圖片在容器中的偏移來控制當前顯示哪張圖片
3.經過計時器來控制循環顯示
4.根據指示控件可手動控制顯示哪張圖片
5.顯示當前圖片的描述信息
小技巧:圖片播放完最後一張切換到第一張的時候,會有明顯的切換閃爍的痕跡,爲了作到順滑的切換,咱們會在最後位置插入第一張圖片做爲過渡。html

效果圖:vue

首先準備素材圖片,在assets文件夾下新建一個img文件夾,把素材圖片放置在這個目錄下面。webpack

既然是和業務不相關的獨立組件,圖片列表須要從使用的父組件進行傳入,首先定義下父組件須要傳值的數據結構:web

[
{ 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默認配置好的,能夠在vue.config.js(使用vue cli 3以前的版本請在webpack.config.js中配置)中配置resolve、alias。

1、實現圖片輪播

新建一個名稱爲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的計算屬性,來響應式的設置容器大小。ide

到這裏基本內容已經佈局好了,下面就開始讓圖片動起來。
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" :style="sizeStyle">
<div :style="scrollStyle" v-for="(item) in list" :key="item.title">
<a :href="item.url">
<img :src="item.path" :alt="item.title" :style="sizeStyle" />
</a>
</div>
<!-- 過渡圖片 -->
<div :style="scrollStyle">
<a :href="firstItem.url">
<img :src="firstItem.path" :alt="firstItem.title" :style="sizeStyle" />
</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(() => {
this.move();
}, 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的綁定

2、添加動畫

此組件建立了兩個動畫效果:平移和漸變。flex

1.平移

平移效果是使用計時器改變偏移量來實現的,主要代碼以下:

<template>
<div class="carousel" :style="sizeStyle">
<div :style="scrollStyle" v-for="(item) in list" :key="item.title">
<a :href="item.url">
<img
:src="item.path"
:alt="item.title"
:style="sizeStyle"
/>
</a>
</div>
<!-- 過渡圖片 -->
<div :style="scrollStyle">
<a :href="firstItem.url">
<img
:src="firstItem.path"
:alt="firstItem.title"
:style="sizeStyle"
/>
</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;
}
this.move(start, end);
},
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" :style="sizeStyle">
<div :style="scrollStyle" v-for="(item,index) in list" :key="item.title">
<a :href="item.url">
<img
:src="item.path"
:alt="item.title"
:style="sizeStyle"
:class="(currentIndex==index+1)?'selected':'unSelect'"
/>
</a>
</div>
<!-- 過渡圖片 -->
<div :style="scrollStyle">
<a :href="firstItem.url">
<img
:src="firstItem.path"
:alt="firstItem.title"
:style="sizeStyle"
:class="(currentIndex==1)?'selected':'unSelect'"
/>
</a>
</div>
</div>

3、添加指示控件

指示控件和圖片數量一致,並一一對應。當切換到當前圖片後,指示控件高亮顯示,而且能夠手動點擊指示控件來展現對應的圖片。
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指令:用於條件性地渲染一塊內容。
是「真正」的條件渲染,由於它會確保在切換過程當中條件塊內的事件監聽器和子組件適當地被銷燬和重建
3.v-on(縮寫@):監聽DOM事件

4、添加Title描述信息

Title主要是展現圖片的描述信息,而且父組件能夠設置是否顯示。
1.首先在props中添加showTitle參數,默認是true
2.在圖片列表中添加span標籤,來展現title信息
主要代碼以下:

<div class="carousel" :style="sizeStyle">
<div :style="scrollStyle" v-for="(item,index) in list" :key="item.title">
<a :href="item.url">
<img
:src="item.path"
:alt="item.title"
:style="sizeStyle"
:class="(currentIndex==index+1)?'selected':'unSelect'"
/>
</a>
<span v-if="showTitle" class="title">{{item.title}}</span>
</div>
<!-- 過渡圖片 -->
<div :style="scrollStyle">
<a :href="firstItem.url">
<img
:src="firstItem.path"
:alt="firstItem.title"
:style="sizeStyle"
: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" :style="sizeStyle">
  3     <div :style="scrollStyle" 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           :style="sizeStyle"
  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 :style="scrollStyle">
 16       <a :href="firstItem.url">
 17         <img
 18           :src="firstItem.path"
 19           :alt="firstItem.title"
 20           :style="sizeStyle"
 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,
 44       default() {
 45         return [];
 46       }
 47     },
 48     showTitle: {
 49       type: Boolean,
 50       default() {
 51         return true;
 52       }
 53     }
 54   },
 55   data() {
 56     return {
 57       width: 300,
 58       height: 200,
 59       currentIndex: 1,
 60       scrollStyle: { transform: "translateX(0px)" }
 61     };
 62   },
 63   mounted() {
 64     this.begin();
 65   },
 66   computed: {
 67     firstItem() {
 68       return this.list[0];
 69     },
 70     number() {
 71       return this.list.length + 1;
 72     },
 73     sizeStyle() {
 74       return { width: this.width + "px", height: this.height + "px" };
 75     }
 76   },
 77   methods: {
 78     begin() {
 79       timer = setInterval(() => {
 80         if (transtionTimer) {
 81           return;
 82         }
 83         this.scroll();
 84       }, 2000);
 85     },
 86     scroll() {
 87       let start = -(((this.currentIndex - 1) % this.number) * this.width);
 88       let end = -(this.currentIndex % this.number) * this.width;
 89       if (end == 0) {
 90         start = 0;
 91         end = -this.width;
 92       }
 93       this.move(start, end);
 94     },
 95     move(start, end) {
 96       let offset = this.width / 20;
 97       //定時器,實現平移效果
 98       transtionTimer = setInterval(() => {
 99         start = start - offset;
100         if (start <= end) {
101           clearInterval(transtionTimer);
102           transtionTimer = null;
103           start = end;
104           if (this.currentIndex % this.number == 0) {
105             this.currentIndex = 1;
106           } else {
107             this.currentIndex++;
108             // 過渡效果:移動到最後一張圖後(咱們在最後加的第一張圖片),把偏移量設置爲0,自動切換成第一圖
109             if (this.currentIndex == this.number) {
110               this.currentIndex = 1;
111               start = 0;
112             }
113           }
114         }
115         this.scrollStyle = {
116           transform: "translateX(" + start + "px)"
117         };
118       }, 20);
119     },
120     handleSwitch(index) {
121       clearInterval(transtionTimer);
122       transtionTimer = null;
123       clearInterval(timer);
124       timer = null;
125       this.currentIndex = index + 1;
126 
127       this.scrollStyle = {
128         transform: "translateX(" + -(index % this.number) * this.width + "px)",
129         transition: "opacity 0.6s linear"
130       };
131       this.begin();
132     }
133   },
134   destroyed() {
135     clearInterval(timer);
136     timer = null;
137     clearInterval(transtionTimer);
138     transtionTimer = null;
139   }
140 };
141 </script>
142 
143 <style scoped>
144 .carousel {
145   display: flex;
146   overflow: hidden;
147   position: relative;
148   margin: 0 auto;
149   width: 100%;
150   height: 100%;
151 }
152 
153 /* 動畫效果 */
154 .selected {
155   opacity: 1;
156   animation: myOpacity 0.6s;
157 }
158 .unSelect {
159   opacity: 0.1;
160 }
161 
162 @keyframes myOpacity {
163   0% {
164     opacity: 0.1;
165   }
166   25% {
167     opacity: 0.25;
168   }
169   50% {
170     opacity: 0.5;
171   }
172   75% {
173     opacity: 0.75;
174   }
175   100% {
176     opacity: 1;
177   }
178 }
179 
180 .dotList {
181   display: flex;
182   position: absolute;
183   z-index: 1000;
184   right: 20px;
185   bottom: 40px;
186 }
187 .dot {
188   width: 10px;
189   height: 10px;
190   margin: 0 2px;
191   background: #fff;
192   border-radius: 50%;
193   display: flex;
194   cursor: pointer;
195 }
196 
197 .dot-actived {
198   width: 10px;
199   height: 10px;
200   border-radius: 50%;
201   background: orange;
202 }
203 
204 .title {
205   height: 30px;
206   background: rgba(213, 213, 230, 0.4);
207   text-align: center;
208   position: absolute;
209   transform: translateY(-100%);
210   color: #fff;
211   display: flex;
212   width: 100%;
213   justify-content: center;
214 }
215 </style>
View Code

以上就是建立自定義輪播圖組件的整個流程,實現了最基本的輪播、手動點擊切換。

寫在最後:

一個看似簡單的功能,在實際實現過程當中可能會遇到不少意想不到的問題,遇到問題、分析問題、並經過不一樣的方案解決問題,纔能有效的提高本身。

相關文章
相關標籤/搜索