偶然发现一个网站页面轮播图样式很好看,就尝试伪实现和使用swiper实现它

 左侧是视频,使用的是video标签,也是swiper垂直轮播的实例

右侧是标题栏,功能效果等效于swiper的分页器Pagination的小圆点

前言

起初,我在思考它是怎么将小圆点的样式给改写成这样的!然而后来才发现,只不过是使用swiper的slideTo方法罢了。这里不禁要感叹一下,为啥这么多人分享vue3如何使用swiper8,却很少有人分享如何使用swiper中的methods,幸好我找到一篇才能完美实现上述的功能需求!

swiper8中文官网vue实例demo        Swiper Demos

swiper8中文官网api文档        mySwiper.slideTo(index, speed, runCallbacks)_Swiper参数选项

Swiper API

感谢这篇文章的解惑        Vue3 初始化swiper以及如何使用swiper的方法与事件  

具体实现

1.使用swiper实现        swiper.vue

<template>
    <div class="swiper-box">
        <swiper :direction="'vertical'" :pagination="{ clickable: true, }" :modules="modules" :autoplay="{ delay: 10000, disableOnInteraction: false }"
            :loop="true" class="mySwiper l-box" @swiper="setControlledSwiper" @slideChange="onSlideChange" ref="mySwiper">
            <swiper-slide v-for="(i, index) in ctnList" style="width: 800px;height:450px;position: relative;">
                <!-- disablePictureInPicture -->
                <video class="video" x5-video-player-type="h5" x-webkit-airplay="true" webkit-playsinline="true" loop
                    autoplay muted controls data-attr="画中画" :id="'video'+index">
                    <source :src="i" type="video/mp4">
                </video>
                <!-- 画中画按钮 -->
                <el-tooltip content="开启画中画">
                    <el-icon class="icon-picture" @click="openPictureInPicture(index)"><VideoCameraFilled /></el-icon>
                </el-tooltip>
            </swiper-slide>
        </swiper>
        <div class="r-box">
            <div class="btn-box" v-for="(i, index) in btnList" :class="active == index ? 'btn-active' : 'btn'"
            @click="ChangeVal(index)">
                <p class="ctn-p">{{ i.name }}</p>
                <span class="ctn-s">{{ i.detail }}</span>
            </div>
        </div>
    </div>
</template>
<script setup>
// 引入swiper组件
import { Swiper, SwiperSlide } from "swiper/vue";
// 引入swiper样式(按需导入)
import 'swiper/css'
import 'swiper/css/pagination' // 轮播图底面的小圆点

// 引入swiper核心和所需模块
import { Autoplay, Pagination, Navigation, Scrollbar } from 'swiper'
// 
import { reactive, ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'

const btnList = [
    { name: '自定义操作', detail: '键鼠手柄自定义映射,体验极致端游操控感' },
    { name: '游戏多开', detail: '同时运行多个游戏,搭配多开同步轻松刷首抽' },
    { name: '多开同步器', detail: '控制多个模拟器进行相同操作,养号刷首抽必备' },
    { name: '操作录制', detail: '录制并重复执行固定操作,解放双手,刷副本必备' }
]
const ctnList = [
    '/video/1.mp4', '/video/2.mp4', '/video/3.mp4', '/video/4.mp4'
]
const active = ref(0)
const mySwiper = ref(null)
let controlledSwiper = null

const onSlideChange = (val) => {
    console.log('slide change',val.activeIndex);
    active.value = val.activeIndex
    
}
const setControlledSwiper = (swiper) => {
    controlledSwiper = swiper
}
const ChangeVal = (val) => {
    console.log(val,Swiper,controlledSwiper)
    controlledSwiper.slideTo(val,1000,false); //核心代码
}
const openPictureInPicture = (index) => {
    let video = document.getElementById("video"+index);
	video.requestPictureInPicture();
}
// 在modules加入要使用的模块
const modules = [Autoplay]  // Pagination
</script>
<style lang="css" scoped>
@import "./swiper.css";
</style>

2.vue3使用定时器和延时器伪实现        img-swiper.vue

<template>
    <div class="swiper-box">
        <div class="left-box">
            <div class="video-box">
                <video v-for="(i, index) in ctnList" class="videobox" :class="active == index ? 'block' : 'none'"
                    x5-video-player-type="h5" x-webkit-airplay="true" webkit-playsinline="true" loop="" autoplay=""
                    muted="">
                    <source :src="i" type="video/mp4">
                </video>
            </div>
        </div>
        <div class="r-box">
            <div class="btn-box" v-for="(i, index) in btnList" :class="active == index ? 'btn-active' : 'btn'"
                @click="changeCtn(index)">
                <p class="ctn-p">{{ i.name }}</p>
                <span class="ctn-s">{{ i.detail }}</span>
            </div>
        </div>
    </div>
</template>

<script setup>
import { reactive, ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'
const btnList = [
    { name: '自定义操作', detail: '键鼠手柄自定义映射,体验极致端游操控感' },
    { name: '游戏多开', detail: '同时运行多个游戏,搭配多开同步轻松刷首抽' },
    { name: '多开同步器', detail: '控制多个模拟器进行相同操作,养号刷首抽必备' },
    { name: '操作录制', detail: '录制并重复执行固定操作,解放双手,刷副本必备' }
]
const ctnList = [
    '/video/1.mp4', '/video/2.mp4', '/video/3.mp4', '/video/4.mp4'
]
const active = ref(0)
let timer = null
let timer1 = null
function changeCtn(val) {
    active.value = val
    console.log("当前点击index:" + val, "5秒后继续轮播")
    timer1 = setTimeout((val) => {
        AutoPlay(val);
    }, 8000);
    
}
function AutoPlay(val) {
    clearTimeout(timer1);
    console.log(val)
    if (!val && val != 0) {
        active.value += 1
    }
    if (active.value === 4) {
        active.value = 0
    }
    console.log(active.value)
}
onMounted(() => {
    timer = setInterval(() => {//每5s刷新数据
        AutoPlay();
    }
        , 8000);
})
onBeforeUnmount(() => {
    clearInterval(timer)
    timer = null;
    clearTimeout(timer1);
    timer1 = null;
})
</script>

<style scoped lang='css'>
@import "./swiper.css";
</style>

公用css

*,
:after,
:before {
    box-sizing: border-box;
}

.swiper-box {
    display: flex;
    width: 80%;
    height: 450px;
    margin: 20px auto;
    flex-flow: row;
    background: #0b0c28;
}

/* 左侧 */
.left-box {
    flex: 1;
    padding-right: 36px;
}

.video-box {
    overflow: hidden;
}

.videobox {
    width: 100%;
    height: 100%;
}

.block {
    display: block;
}

.none {
    display: none;
}

/* 右侧 */
.r-box {
    width: 30%;
    display: flex;
    flex-flow: column;
    /* background: #0b0c28; */
    border-left: 1.2px solid rgba(255, 255, 255, .2);
}

.btn-box {
    height: 25%;
    display: flex;
    flex-flow: column;
    justify-content: center;
    padding-left: 36px;
    position: relative;
    transition: 0.6s;
}

.btn-box .ctn-p {
    width: fit-content;
    font-size: 16px;
    font-weight: 600;
    margin-bottom: 24px;
    color: rgba(255, 255, 255, .8);
    transition: color .3s;
    cursor: pointer;
}

.btn-box .ctn-s {
    width: fit-content;
    font-size: 13px;
    color: rgba(255, 255, 255, .6);
    font-weight: 400;
    transition: color .3s;
    cursor: pointer;
}

/* 选中 */
.btn-active::before {
    content: "";
    position: absolute;
    bottom: 0;
    left: -1.2px;
    width: 2.8px;
    height: 100%;
    overflow: hidden;
    background: linear-gradient(137deg, #3e77ff, #ff4eaa);
    transition: .1s ease-in;
    z-index: 1;
}

.btn-active {
    background: #171838;
}

.btn-active .ctn-p {
    color: #fff;
    background: linear-gradient(104deg, #3e77ff, #ff4eaa);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
}

.btn-active .ctn-s {
    color: #fff;
}
/* swiper 版本 */
.l-box {
    flex: 1;
}
.video {
    width: 100%;
    /* height: 100%; */
    object-fit: contain;   /* cover */
}
.video::before {
    /* 没用 */
    content: attr(data-attr);
    color: #ff4eaa;
}
.swiper-slide:hover .icon-picture {
    position: absolute;
    left: 50%;
    top: 20px;
    font-size: 24px;
    z-index: 1;
    color: rgb(233, 215, 215);
    cursor: pointer;
}

效果预览

对比总结

总的来说,使用swiper的优势体现的淋漓尽致。

1.swiper切换效果平滑流畅,而且支持鼠标滑动,用户交互性强;后者通过display来控制显隐,不能写过渡样式,纯点击切换页面效果

2.不需要借助定时器(仅针对页面代码而言,因为其封装的内部原理应该是延时器吧,要不然...),代码核心逻辑只需要一行controlledSwiper.slideTo(val,1000,false)        代码更简洁易上手,唯一的问题就是寻找成本高,得知道怎么使用

3.于我而言,给与了我一种新思路,也就是自定义swiper功能组件的样式,并不一定要修改其样式,完全可以自定义然后使用功能方法就行了,比如next和prev都有对应slideNext和slidePrev的方法

资源下载

温馨提示:

以上代码建立在vue3+setup语法糖+vite的框架下,没有使用ts;所以你想完美运行需要建立框架,也就是使用vite构建一个vue3项目,当然也可以是vue3+vuecli3;以及需要搭建路由。

不过使用html+vue.js3.x应该也可以,大家自行尝试哈

swiper版本:基于vue3直接下载npm install swiper 就是最新版本了

具体参考demo里面的版本:swiper-rewind-vue - CodeSandbox

 视频资源        一线一笔画电脑版下载_一线一笔画 PC电脑版下载_夜神安卓模拟器

懒的上传了,直接 打开上述网站选中对应位置的视频右键选择另存为,然后放到vue3项目的public的video文件夹里即可

index.vue        入口文件

<template>
  <div>
    <!-- <img-swiper></img-swiper> -->
    <swiper></swiper>
  </div>
</template>

<script setup>
import ImgSwiper from "./img-swiper.vue"
import Swiper from "./swiper.vue"
import { reactive, toRefs, ref, computed, watch, onMounted } from 'vue'
const data = ref('')
onMounted(() => {
  //console.log('我是测试页面')
})
</script>

<style scoped lang='css'>
</style>

最后

一个bug 

更新:2022/11/02

上述bug是swiper轮播中的loop:"true"导致的,在loop模式下slides前后会复制若干个slide,从而形成一个环路。本案例中就会导致activeIndex从原来的0,1,2,3 => 0,1,2,3,4,5并且视频也会错位​​​​​​​,即3,0,1,2,3,0

​​​​​​​

解决办法

1.调整视频输出顺序,以及根据打印信息重新赋值

const ctnList = [
    '/video/2.mp4', '/video/3.mp4', '/video/4.mp4', '/video/1.mp4'
]
const onSlideChange = (val) => {
    console.log('slide change',val.activeIndex);
    let activeIndex = val.activeIndex
    if(activeIndex === 4) {
        active.value = 0
    } else if(activeIndex === 5) {
        active.value = 1
    }
    else {
        active.value = activeIndex
    } 
}

2.删除loop:"true",一劳永逸,就是不能循环鼠标滑动交互了

3.网上说什么回调函数,但大多数都是vue2的写法,目前还没有尝试成功过!

代码优化如有更好的建议,欢迎大家评论!

更多推荐

vue3使用Swiper8实现自定义轮播效果