发布时间:2024-04-29 12:01
偶然发现一个网站页面轮播图样式很好看,就尝试伪实现和使用swiper实现它
左侧是视频,使用的是video标签,也是swiper垂直轮播的实例
右侧是标题栏,功能效果等效于swiper的分页器Pagination的小圆点
起初,我在思考它是怎么将小圆点的样式给改写成这样的!然而后来才发现,只不过是使用swiper的slideTo方法罢了。这里不禁要感叹一下,为啥这么多人分享vue3如何使用swiper8,却很少有人分享如何使用swiper中的methods,幸好我找到一篇才能完美实现上述的功能需求!
swiper8中文官网vue实例demo Swiper Demos
swiper8中文官网api文档 mySwiper.slideTo(index, speed, runCallbacks)_Swiper参数选项
感谢这篇文章的解惑 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的写法,目前还没有尝试成功过!
代码优化如有更好的建议,欢迎大家评论!