大家好~ 我是前端uio,本文主要使用vue3制作web端自定义轮播图动画效果(完整版保姆级教程,附源码) 有什么疑问都欢迎私聊或者在评论区留言。如果需要开发web网页或者微信小程序都可以找我私聊,我会尽可能回答我了解的内容。

一、功能说明

1.最终效果

自定义轮播图

2.图片切换

        i)先使用Dom操作获取图片信息

 let currentImg = document.querySelector('.img');

       ii)改变图片的src属性

            ① 因为有多张图片,考虑代码的简洁性,先设置imgArr数组放置所有的轮播图片

const imgArr = [
'http://localhost:8081/img/test.2140f46e.jpeg',
  'http://localhost:8081/img/test1.b5a3016e.jpeg',
 'http://localhost:8081/img/test2.060c4789.jpeg'
]

             请注意:若使用网络图片,则没有影响;若使用的是本地图片,这里的图片不能使用相对路径,需要使用源路径。可先将所需图片放置在页面中,如下图所示,可以在浏览器中,通过点击键盘上的F12,可以通过筛选器找到图片所在的元素后,鼠标悬浮在该元素,即可查看源图片路径。

           ② 通过动态修改imgIndex的值,可实现图片的切换

 currentImg.src= imgArr[imgIndex.value]

       iii)图片切换的动画效果

          ① 以淡入动画为例,在js中,进行图片切换时,使用Dom操作先将图片的透明度,也就是opacity设置为0,再进行0.5s的延时,延时结束后,将图片的opacity设置为1,即可实现简单的淡入动画效果。

3.控制图片切换的左右箭头图标

        i)左右箭头

          ① 图标

                本文使用的是element plus 提供的icon图标 ,可根据需要进行替换

  <el-icon><ArrowLeftBold /></el-icon>
  <el-icon><ArrowRightBold /></el-icon>
         ② 绑定事件(以左边的箭头为例)

              通过v-on,也就是@来绑定点击事件,并传入自定义参数(用于判断是点击了左还是右箭头),在事件函数中修改imgIndex的值,即可完成左右切换图片

 <el-icon size="40" class="left-btn" @click="next(-1)"><ArrowLeftBold /></el-icon>
         ③ 样式(以左箭头为例)

              父元素使用相对定位,左箭头icon使用绝对定位后,通过设置cursor,实现鼠标悬浮时的小手效果,设置top为50%(在绝对定位时),可实现垂直方向的居中

        .left-btn {
            position: absolute;
            cursor: pointer;
            top: 50%;
            left: 30px;
            width: auto;           
    
        }
        ④动画效果

            效果:若鼠标悬浮在轮播图区域内,左右箭头显示;若鼠标未悬浮在轮播图区域内,左右箭头隐藏。

            步骤(以less为例):先设置左右箭头的宽度为0,即可实现初始化时左右箭头隐藏,当鼠标悬浮在轮播图区域内,触发hover伪类选择器,所以在伪类选择器中设置前面提到的③中的样式,即可实现动画效果。

.banner  {
    position: relative;
    width: 100%;
    .left-btn, .right-btn {
        width:  0;
    }
    &:hover {
        .left-btn {
            position: absolute;
            cursor: pointer;
            top: 50%;
            left: 30px;
            width: auto;           
    
             }
                   }
  }

4. 标题过渡动画

         i)标题内容组件

                ① 使用Vue的<transition>组件,可点击跳转到官网进行查看,本文主要讲解用法以及注意事项,这里不再赘述。

                ② 注意:<transition>组件只能放置一个 Vue 组件或 HTML 元素,所以只能在里面放置一个<div>标签,将其他内容放置在<div>标签内,或者使用多个<transition>组件分别进行过渡动画效果的控制。

          ii)从左渐入到中间的过渡动画

                ① 过渡动画的样式

                    在组件中传入name效果名,在style中,使用对应的css样式,具体实现过程与介绍,可查看基于css的过渡效果。本文在进入动画前使用margin-right,实现从左边进入动画,在动画进入前以及动画结束后设置opacity(透明度)为0的动画效果,实现淡入淡出动画。最后在transition设置动画时长。

    .scale-enter-active{
        transition: all 1s ease-in-out;
      }
      
      .scale-leave-active {
        transition: all 1s linear;
      }
      
      .scale-enter-from{
        margin-right: 300px;
        opacity: 0;
      }
      .scale-leave-to {
        opacity: 0;
      }

         iii)标题内容切换

                ① 使用v-for进行遍历,创建多个<transition>组件,通过绑定不同的key来区分不同的<transition>组件,从而实现标题内容的切换。

                ② 由于可能进行频繁的切换,所以使用v-show控制内容的切换。

                ③ v-if 会把元素进行删除,v-show 只是进行隐藏,该元素仍然保留,所以进行频繁的切换的话,使用v-show 效率会更高一点,若不频繁切换显示与隐藏,使用v-if 也可以,具体使用哪一种需要看具体的实现方式以及效果。

            <div v-for="(title,titleIndex) in titles" :key="titleIndex"  class="content">
                <transition name="scale">
                    <div v-show="imgIndex===titleIndex" >
                      <span>{{title}}</span>
                    </div>
                  </transition>
            </div>

5. 轮播图底下的指示圆圈

          i)放置圆圈

                根据图片数组的长度进行循环遍历,添加并动态绑定相应的选中样式即可实现指示圆圈动画效果。

            <div class="dot-content">
                <div v-for="(item,index) in imgArr.length" :key="item" :class="index===imgIndex?'active':'dot-box'">
                </div>
            </div>

        ii)样式

                当前图片css样式为active,未被选中图片的样式dot-box。

    .dot-content {
        display: flex;
        position: absolute;
        bottom: 20px;
        justify-content: space-around;
        align-items: center;
        width: 100px;
        height: 30px;
        .dot-box {
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background-color: #fff;
        }
        .active {
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background-color: red;
        }
    }

        iii)动态样式绑定 

                使用 v-bind 进行动态样式绑定,本质上是一个三元表达式,当问号前面内容判断为true是,使用冒号前的样式;当问号前面内容判断为false时,使用冒号后的样式。

:class="index===imgIndex?'active':'dot-box'"

6.注意事项 

         i)边界条件

                左右切换图片时,第一张图片的左切换,与最后一张图片的右切换需要进行判断处理,本文采用循环轮播的方式进行处理,可根据需要进行调整。

    if(imgIndex.value >= imgArr.length ) {
        imgIndex.value=0
    }
    if(imgIndex.value <0 ) {
        imgIndex.value=imgArr.length-1
    }

二、主要代码

        完整版代码请看下方第三部分《拓展与练习》的第2小点

/**
 * @name    { 轮播图 }
 * @info    { 自定义轮播图以及动画 }
 * @author  { 前端uio 2024-5-20 }
 *
 * @click        funtion => 处理对应的点击事件
 * @transition   组件    => 实现过渡动画
 * @el-icon      组件    => 切换图片的左右箭头图标
 */
<template>
    <div>
        <div  class="banner">
            <img src="../../assets/image/test/test.jpeg" class="img">
            <div v-for="(title,titleIndex) in titles" :key="titleIndex"  class="content">
                <transition name="scale">
                    <div v-show="imgIndex===titleIndex" >
                      <span>{{title}}</span>
                    </div>
                  </transition>
            </div>
            <div class="dot-content">
                <div v-for="(item,index) in imgArr.length" :key="index" :class="index===imgIndex?'active':'dot-box'">
                </div>
            </div>
            <el-icon size="40" class="left-btn" @click="next(-1)"><ArrowLeftBold /></el-icon>
            <el-icon size="40" class="right-btn" @click="next(1)"><ArrowRightBold /></el-icon>
        </div>
    </div>
</template>
<script setup>
import {ref} from 'vue'
const imgIndex = ref(0)
const imgArr = [
'http://localhost:8081/img/test.2140f46e.jpeg',
  'http://localhost:8081/img/test1.b5a3016e.jpeg',
 'http://localhost:8081/img/test2.060c4789.jpeg'
]
const titles = [
    '胡桃🍑🍑🍑🍑🍑🍑','启动⚪⚪⚪⚪⚪⚪','撒花❀❀❀❀❀❀'
]
const next = (e) => {
    let currentImg = document.querySelector('.img');
    e>0? imgIndex.value+=1 : imgIndex.value-=1
    if(imgIndex.value >= imgArr.length ) {
        imgIndex.value=0
    }
    if(imgIndex.value <0 ) {
        imgIndex.value=imgArr.length-1
    }
    currentImg.style.opacity = '0'

    setTimeout(() => {
        currentImg.src= imgArr[imgIndex.value]

        currentImg.style.opacity = '1';
    },500)
}
</script>
<style scoped lang="less">

.banner  {
    position: relative;
    display: flex;
    justify-content: center;
    width: 100%;
    .left-btn, .right-btn {
        width:  0;
    }
    &:hover {
        .left-btn {
            position: absolute;
            cursor: pointer;
            top: 50%;
            left: 30px;
            width: auto;           
    
        }
        .right-btn {
            position: absolute;
            cursor: pointer;
            top: 50%;
            right: 30px;            
            width: auto;
        }
    }
    .img {
        display: none;
        &:first-child {
            display: block;
            height: 60vh;
            opacity: 1;
            transition: opacity 0.5s ease;
        }
    
    }
    .scale-enter-active{
        transition: all 1s ease-in-out;
      }
      
      .scale-leave-active {
        transition: all 1s linear;
      }
      
      .scale-enter-from{
        margin-right: 300px;
        opacity: 0;
      }
      .scale-leave-to {
        opacity: 0;
      }
    .content {
        position: absolute;
        top: 50%;
        font-size: 40px;
        color: #fff;
    }

    .dot-content {
        display: flex;
        position: absolute;
        bottom: 20px;
        justify-content: space-around;
        align-items: center;
        width: 100px;
        height: 30px;
        .dot-box {
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background-color: #fff;
        }
        .active {
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background-color: red;
        }
    }

}
</style>

三、拓展与练习

1.点击轮播图下方的指示圆圈进行图片的切换

        i)绑定点击事件 

             当点击相应的指示圆圈时,触发selectImg事件,传入当前指示圆圈绑定的key,来确定当前点击的元素。

            <div class="dot-content">
                <div v-for="(item,index) in imgArr.length" :key="index" :class="index===imgIndex?'active':'dot-box'" @click="selectImg(index)">
                </div>
            </div>

       ii) 鼠标悬浮样式

             添加了鼠标悬浮时的小手指示样式

    .dot-content {
        display: flex;
        position: absolute;
        bottom: 20px;
        justify-content: space-around;
        align-items: center;
        width: 100px;
        height: 30px;

        .dot-box {
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background-color: #fff;
            cursor: pointer;
        }
        .active {
            cursor: pointer;
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background-color: red;
        }
    }

2.项目源码(完整版)

/**
 * @name    { 轮播图(完整源码) }
 * @info    { 自定义轮播图以及动画 }
 * @author  { 前端uio 2024-5-20 }
 *
 * @click        funtion => 处理对应的点击事件
 * @transition   组件    => 实现过渡动画
 * @el-icon      组件    => 切换图片的左右箭头图标
 */
<template>
    <div>
        <div  class="banner">
            <img src="../../assets/image/test/test.jpeg" class="img">
            <div v-for="(title,titleIndex) in titles" :key="titleIndex"  class="content">
                <transition name="scale">
                    <div v-show="imgIndex===titleIndex" >
                      <span>{{title}}</span>
                    </div>
                  </transition>
            </div>
            <div class="dot-content">
                <div v-for="(item,index) in imgArr.length" :key="index" :class="index===imgIndex?'active':'dot-box'" @click="selectImg(index)">
                </div>
            </div>
            <el-icon size="40" class="left-btn" @click="next(-1)"><ArrowLeftBold /></el-icon>
            <el-icon size="40" class="right-btn" @click="next(1)"><ArrowRightBold /></el-icon>
        </div>
    </div>
</template>
<script setup>
import {ref} from 'vue'
const imgIndex = ref(0)
const imgArr = [
'http://localhost:8081/img/test.2140f46e.jpeg',
  'http://localhost:8081/img/test1.b5a3016e.jpeg',
 'http://localhost:8081/img/test2.060c4789.jpeg'
]
const titles = [
    '胡桃🍑🍑🍑🍑🍑🍑','启动⚪⚪⚪⚪⚪⚪','撒花❀❀❀❀❀❀'
]
const selectImg =(e) => {
    let currentImg = document.querySelector('.img')
    currentImg.style.opacity = '0'
    setTimeout(() => {
        currentImg.src= imgArr[e]
        imgIndex.value = e
        currentImg.style.opacity = '1';
    },500)
} 
const next = (e) => {
    e>0? imgIndex.value+=1 : imgIndex.value-=1
    if(imgIndex.value >= imgArr.length ) {
        imgIndex.value=0
    }
    if(imgIndex.value <0 ) {
        imgIndex.value=imgArr.length-1
    }
    selectImg(imgIndex.value)
}
</script>
<style scoped lang="less">
.banner  {
    position: relative;
    display: flex;
    justify-content: center;
    width: 100%;
    .left-btn, .right-btn {
        width:  0;
    }
    &:hover {
        .left-btn {
            position: absolute;
            cursor: pointer;
            top: 50%;
            left: 30px;
            width: auto;           
    
        }
        .right-btn {
            position: absolute;
            cursor: pointer;
            top: 50%;
            right: 30px;            
            width: auto;
        }
    }
    .img {
        display: none;
        &:first-child {
            display: block;
            height: 60vh;
            opacity: 1;
            transition: opacity 0.5s ease;
        }
    
    }
    .scale-enter-active{
        transition: all 1s ease-in-out;
      }
      
      .scale-leave-active {
        transition: all 1s linear;
      }
      
      .scale-enter-from{
        margin-right: 300px;
        opacity: 0;
      }
      .scale-leave-to {
        opacity: 0;
      }
    .content {
        position: absolute;
        top: 50%;
        font-size: 40px;
        color: #fff;
    }

    .dot-content {
        display: flex;
        position: absolute;
        bottom: 20px;
        justify-content: space-around;
        align-items: center;
        width: 100px;
        height: 30px;

        .dot-box {
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background-color: #fff;
            cursor: pointer;
        }
        .active {
            cursor: pointer;
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background-color: red;
        }
    }

}
</style>

四、总结与思考

1.能不能不使用Dom操作完成轮播图效果(使用滚动条)

2.当前轮播图下方的指示圆圈禁止点击(禁止点击的样式)

Logo

前往低代码交流专区

更多推荐