Vue3.0组件—banner轮播图(渐入渐隐效果)

Vue3.0
组件—banner轮播图(渐入渐隐效果)
组件产生
最近遇到一个需求,项目首页banner轮播,开始是直接使用element3.0的el-carousel
走马灯效果,但是产品觉得切换太快,给用户的体验效果不好,经过多次修改产品给出切换的具体描述前一张图渐隐一个效果,后一张图渐显一个效果,然后停留几秒,于是单独写了一个banner轮播组件。
组件封装思路
-
主要内容:
①组件图片切换有三种方式,第一种是图片两边按钮切换,第二种是点击图片下方小横条切换,第三种是自动切换;
②子组件props的属性:listBanner(播放的图片源)、interval(图片播放的间隔时间)、 autoSwitc(是否自动播放banner)、 setHeight(banner的高度);
③给切换图片的标签添加ref,改变ref属性中的opacity,切换透明度,达到图片渐隐渐显的效果,再给对应标签样式中添加
transition-duration: 1.5s
;(图片透明度切换的时间
//上一张和下一张图片透明度切换
const changePicture = (num) => {
for (let i of rotationRef.value) {
i.style.opacity = "0";//所有的图片透明度设置为0
}
//调用该方法的时候传入对应图片的索引,该图片赋透明度为1
if (rotationRef.value.length !== 0) {
rotationRef.value[num].style.opacity = "1";
}
};
-
组件思路
①图片源是数组形式,首先用v-for遍历图片源数组,让图片先平铺到页面中
②实现点击左右两边按钮切换图片,点击右边按钮,图片switchIndex++(图片的索引)累加,点击左边按钮switchIndex–累减;从右边切换图片已经遍历完的时候,switchIndex赋值为0(第一张图片的索引),从头开始播放;从左边切换时,切换到图片switchIndex等于-1的时候,switchIndex赋值为图片源数组的长度-1(最后一张图片的索引)从最后一张图片播放。
③定时器切换图片,先写一个定时器,默认停留时间为4000ms,在定时器中调用右边切换的方法,因为定时器是顺时针播放图片的。onMounted中调用定时器方法,在 onUnmounted中要记得销毁定时器,不然会出现一些奇怪的问题。
④小横条切换图片,根据图片源数组的长度循环显示小横条,点击小横条的时候传入对应横条的索引,把这个索引赋值到对应切换图片的方法中,就会显示对应的索引的图片。
⑤鼠标停留
mouseenter
在图片上停止自动播放,鼠标离开mouseleave
图片自动播放。停留的时候调用stopInterval();
(清除定时器的方法),离开的时候调用addTimer();
(开启定时器)
组件效果
完整代码
<template>
<div
class="banner-container"
:style="{ height: height + 'px' }"
@mouseenter="mouseenterEvent"
@mouseleave="mouseleaveEvent"
>
<ul class="fade-banner">
<li
class="rotation-banner"
ref="rotationRef"
:style="{ height: height + 'px' }"
v-for="(item, index) in listBanner"
:key="index"
>
<img class="banner-img" @click="bannerLink" :src="item.img" :title="description" />
</li>
</ul>
<span class="left-button" @click="switchBanner('left')"
><img src="@/assets/icons/common/left_icon.png" class="icon"
/></span>
<span class="right-button" @click="switchBanner('right')"
><img src="@/assets/icons/common/right_icon.png" class="icon"
/></span>
<ul class="banner-indicator">
<span
v-for="index in listBanner.length"
:key="index"
class="barExternal"
@click="swatchIndicator(index - 1)"
>
<li
ref="indicatorRef"
class="default-indicator"
:class="{ 'active-indicator': index - 1 === 0 }"
></li>
</span>
</ul>
</div>
</template>
<script>
import { ref, onMounted, unref, onUnmounted } from "vue";
export default {
name: "RollBanner",
props: {
//播放的banner
listBanner: {
type: Array,
default: () => []
},
//banner自动播放的间隔时间
interval: {
type: Number,
default: 3000
},
//是否自动播放banner
autoSwitch: {
type: Boolean,
default: false
},
//banner高度
setHeight: {
type: Number,
default: 400
}
},
setup(props) {
let tiemr = ref(null);
const switchIndex = ref(0);
const rotationRef = ref();
const indicatorRef = ref();
const description = ref(null); //图片描述
let height = unref(props.setHeight);
//banner播放方式
const addTimer = () => {
if (props.autoSwitch === true) {
//自动播放
tiemr = setInterval(() => {
switchBanner("right");
}, props.interval);
}
};
// 清除定时器
const stopInterval = () => {
clearInterval(tiemr);
};
//banner文字提示(非必须)
const switchDescription = () => {
if (props.listBanner) {
props.listBanner.map((item, index) => {
if (index === switchIndex.value) {
description.value = item.description;
}
});
}
};
//鼠标进入停止自动播放(非必须)
const mouseenterEvent = () => {
stopInterval();
switchDescription();
};
//鼠标离开自动播放(非必须)
const mouseleaveEvent = () => {
addTimer();
};
//上一张和下一张图片透明度切换
const changePicture = (num) => {
for (let i of rotationRef.value) {
i.style.opacity = "0";
}
toBannerBar(num);
if (rotationRef.value.length !== 0) {
rotationRef.value[num].style.opacity = "1";
}
};
//底部小横条颜色切换
const toBannerBar = (val) => {
for (let i of indicatorRef.value) {
i.style.backgroundColor = "rgba(221, 221, 221, 0.541)";
}
if (indicatorRef.value.length !== 0) {
indicatorRef.value[val].style.backgroundColor = "#ffffff";
}
};
//左右切换banner
const switchBanner = (value) => {
if (value === "right") {
switchIndex.value++;
if (switchIndex.value === props.listBanner.length) {
switchIndex.value = 0;
}
} else {
switchIndex.value--;
if (switchIndex.value === -1) {
switchIndex.value = props.listBanner.length - 1;
}
}
changePicture(switchIndex.value);
switchDescription();
};
//底部横条切换banner
const swatchIndicator = (value) => {
switchIndex.value = value;
changePicture(switchIndex.value);
switchDescription();
};
//banner链接跳转((非必须)
const bannerLink = () => {
if (props.listBanner) {
props.listBanner.map((item, index) => {
if (index === switchIndex.value && item.url !== "" && item.url !== null) {
window.location.href = item.url;
}
});
}
};
onMounted(() => {
addTimer();
});
onUnmounted(() => {
stopInterval();
});
return {
addTimer,
bannerLink,
switchIndex,
indicatorRef,
swatchIndicator,
changePicture,
rotationRef,
toBannerBar,
height,
switchBanner,
stopInterval,
mouseenterEvent,
mouseleaveEvent,
description,
switchDescription
};
}
};
</script>
<style lang="scss" scoped>
.banner-container {
position: relative;
.fade-banner {
position: relative;
list-style: none;
}
.rotation-banner {
position: absolute;
opacity: 0;
// transition-duration: 3s;
transition-duration: 1.5s;
width: 100%;
&:first-child {
opacity: 1;
}
}
.banner-img {
width: 100%;
height: 100%;
object-fit: cover;
cursor: pointer;
}
.left-button {
position: absolute;
cursor: pointer;
top: 50%;
left: 25px;
width: 36px;
height: 36px;
display: flex;
justify-content: center;
align-items: center;
background-color: rgb(31, 45, 61, 0.3);
border-radius: 50%;
opacity: 0;
}
.right-button {
position: absolute;
width: 36px;
height: 36px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
top: 50%;
right: 25px;
background-color: rgb(31, 45, 61, 0.3);
border-radius: 50%;
opacity: 0;
}
.icon {
height: 20px;
width: 20px;
}
.banner-indicator {
position: absolute;
left: 50%;
bottom: 15px;
transform: translateX(-50%);
cursor: pointer;
display: flex;
.barExternal {
height: 20px;
display: flex;
justify-content: center;
align-items: center;
.default-indicator {
width: 30px;
height: 2px;
background: rgba(221, 221, 221, 0.541);
display: block;
float: left;
margin-right: 10px;
// &:first-child {
// background: #ffffff;
// }
}
.active-indicator {
background: #ffffff;
}
}
}
&:hover {
.left-button {
opacity: 1;
}
.right-button {
opacity: 1;
}
}
}
</style>




更多推荐
所有评论(0)