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