前言:
最近在逛山東博物館網站的時候,發現該網站主頁淡入淡出的輪播圖非常的優雅,所以就想來復刻一下,也算是對組件進行了二次的封裝和修改
工具準備:
Vue3+Element Plus走馬燈組件
注意事項:
Element Plus的走馬燈有以下幾個屬性需要注意下:
.el-carousel__item 所有輪播圖元素的樣式。
. is-active 樣式來確定當前展示的元素 。
.is-animating 演示來實現過渡的間隔。
還有一個行內樣式,組件內部是通過添加或修改行內樣式實現切換的效果(這個比較重要)。
案例代碼
HTML
<template><div class="home-container"><div class="home-container_banner"><el-carousel height="100vh" @change="change" :interval="3000" :pause-on-hover="false"><el-carousel-item v-for=" item in bannerList" :key="item"><div class="container-pic" :style="{ backgroundImage: `url(${getImage(item)})` }"></div></el-carousel-item></el-carousel></div></div>
</template>
JavaScript
背景圖片需要用絕對路徑,所以封裝一個小的方法
<script setup>
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
const getImage = (imgUrl) => {return new URL(`../image/${imgUrl}`, import.meta.url).href
}// 組件名稱定義
defineOptions({name: 'MuseumDash'
});// 輪播圖圖片列表
const bannerList = ref(['1.jpg', '2.jpg', '3.jpg', '4.jpg']);// 幻燈片切換后的樣式更改
const change = () => {nextTick(() => {const picItems = document.querySelectorAll('.el-carousel__item');picItems.forEach(item => {item.style.transform = 'scale(1)';});setTimeout(() => {const isActive = document.querySelector('.el-carousel__item.is-active');if (isActive) {isActive.style.transform = 'scale(1.08)';}}, 10);});
};
change()
// 使用 MutationObserver 監控并覆蓋 translate 樣式
const observer = new MutationObserver(mutations => {mutations.forEach(mutation => {if (mutation.type === 'attributes' && mutation.attributeName === 'style') {mutation.target.style.transform = mutation.target.style.transform.replace(/translateX\([^)]*\)/, '');}});
});// 監聽頁面加載和卸載事件
onMounted(() => {const items = document.querySelectorAll('.el-carousel__item');items.forEach(item => {observer.observe(item, {attributes: true,attributeFilter: ['style']});});
});onBeforeUnmount(() => {observer.disconnect();
});
</script>
Css
<style lang="scss" scoped>
.home-container_banner {height: 100vh;width: 100vw;overflow: hidden;position: relative;.container-pic {height: 100%;width: 100%;background-size: cover;background-position: center;background-repeat: no-repeat;}
}.el-carousel__item {transition: opacity 0.5s linear, transform 2s ease-in-out !important;position: absolute !important;top: 0;left: 0;right: 0;bottom: 0;opacity: 0;z-index: 1;
}.el-carousel__item.is-active {opacity: 1;z-index: 2;
}
</style>
解析:
-
修改 .el-carousel__item 樣式,讓元素本身實現放大的效果。這里使用 !important 進行加權,防止組件內部的樣式文件對其進行覆蓋。
-
通過 JS 控制覆蓋掉組件自身邏輯添加的style(覆蓋樣式是實現功能重要的點之一),使用 nextTick 是為了保證先獲取元素再進行邏輯執行,這里使用 setTimeout 是為了延遲執行放大效果,如果不使用的話,當頁面刷新時幻燈片的第一個頁面直接就會被放大,就沒有逐漸放大的效果了。整體的邏輯是在 組件切換的時候執行的,為了讓頁面創建時就開始執行,要在 created 周期中執行一次,讓第一張幻燈片實現動效。
const change = () => {nextTick(() => {const picItems = document.querySelectorAll('.el-carousel__item');picItems.forEach(item => {item.style.transform = 'scale(1)';});setTimeout(() => {const isActive = document.querySelector('.el-carousel__item.is-active');if (isActive) {isActive.style.transform = 'scale(1.08)';}}, 10);}); }; change()
-
通過 MutationObserver 監聽 Dom 添加元素或屬性事件,解決更改視口大小動效錯亂的問題。因為 Element 組件會根據視口位置自動計算當前位置,計算完畢后會添加上 style 屬性那么就會將我們修改后的 style 屬性進行覆蓋。(重要的優化邏輯)(這個方法不是很常用,大家可以去 MDN 查看對應的 Api)
const observer = new MutationObserver(mutations => {mutations.forEach(mutation => {if (mutation.type === 'attributes' && mutation.attributeName === 'style') {mutation.target.style.transform = mutation.target.style.transform.replace(/translateX\([^)]*\)/, '');}}); });// 監聽頁面加載和卸載事件 onMounted(() => {const items = document.querySelectorAll('.el-carousel__item');items.forEach(item => {observer.observe(item, {attributes: true,attributeFilter: ['style']});}); });onBeforeUnmount(() => {observer.disconnect(); });
-
在組件銷毀前清除監聽,防止內存泄漏