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默認配置好的,可以在vue.config.js(使用vue cli 3之前的版本請在webpack.config.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" :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的綁定

二、添加動畫

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

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>

三、添加指示控件

指示控件和圖片數量一致,並一一對應。當切換到當前圖片后,指示控件高亮顯示,並且可以手動點擊指示控件來展示對應的圖片。
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事件

四、添加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

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

寫在最后:

一個看似簡單的功能,在實際實現過程中可能會遇到很多意想不到的問題,遇到問題、分析問題、並通過不同的方案解決問題,才能有效的提升自己。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM