前言:
最近在逛山东博物馆网站的时候,发现该网站主页淡入淡出的轮播图非常的优雅,所以就想来复刻一下,也算是对组件进行了二次的封装和修改
工具准备:
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(); });
-
在组件销毁前清除监听,防止内存泄漏