基于Vue3的Web端自定义轮播图动画效果(完整版保姆级教程,附源码)

1,325 阅读8分钟

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

一、功能说明

1.最终效果

可点击查看演示视频 live.csdn.net/v/388761

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的组件,可点击跳转到官网进行查看,本文主要讲解用法以及注意事项,这里不再赘述。

                ② 注意:组件只能放置一个 Vue 组件或 HTML 元素,所以只能在里面放置一个

标签,将其他内容放置在
标签内,或者使用多个组件分别进行过渡动画效果的控制。

          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进行遍历,创建多个组件,通过绑定不同的key来区分不同的组件,从而实现标题内容的切换。

                ② 由于可能进行频繁的切换,所以使用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.当前轮播图下方的指示圆圈禁止点击(禁止点击的样式)