简单实用,可实现H5页面的丝滑翻页,我是在做公司app年度报告功能时使用的(类似网易云个人年终总结那种动画)
参考文档:swiper官网-vue版
一、下载插件
npm install swiper
二、页面中引用(注意是vue模式)
import 'swiper/less';
import { Swiper, SwiperSlide } from 'swiper/vue';
三、使用
<swiper direction="vertical">
<swiper-slide>页面1</swiper-slide>
<swiper-slide>页面2</swiper-slide>
<swiper-slide>页面3</swiper-slide>
</swiper>
到这里我们的基本效果完成,页面已经可以竖直方向滑动。
四、拓展
1. 实际应用中,每个页面的内容和逻辑可能很多,那么可以把每个页面封装为组件:
// 异步引入组件,减少首屏加载时间
const DemoA = defineAsyncComponent(() => import('./DemoA.vue'));
const DemoB = defineAsyncComponent(() => import('./DemoB.vue'));
const DemoC = defineAsyncComponent(() => import('./DemoC.vue'));
<swiper direction="vertical">
<swiper-slide>
<DemoA />
</swiper-slide>
<swiper-slide>
<DemoB />
</swiper-slide>
<swiper-slide>
<DemoC />
</swiper-slide>
</swiper>
2. 如果某个页面中有动画效果,且需求是:每次滑动到该页面时动效触发,我们可以通过swiper插件提供的v-slot来实现:
<swiper direction="vertical">
<swiper-slide>
<DemoA />
</swiper-slide>
<swiper-slide>
<DemoB />
</swiper-slide>
<swiper-slide v-slot="{ isActive }">
<DemoC :is-active="isActive" />
</swiper-slide>
</swiper>
在子组件DemoC中接收isActive作为动效是否触发的参数:
<script setup>
/* 页面3-组件DemoC */
const props = defineProps({
// 是否滑动到当前页面
isActive: { type: Boolean, default: false },
});
</script>
<template>
<div class="container">
<transition enter-active-class="animate__animated animate__backInRight">
<div v-show="isActive">测试动效文字</div>
</transition>
</div>
</template>
<style lang="less" scoped>
.container {
position: relative;
height: 100%;
}
</style>
这里我使用的动效插件是animate.css,参考文档:animate.css官网
3. 如果有一些组件内容很多或引入的文件过大,导致整个滑动效果加载时卡顿,可以使用swiper插件提供的虚拟化加载,我们可以结合动态组件一起使用:
import { Virtual } from 'swiper/modules';
let slideList = ref([
{ name: DemoA, data: '' },
{ name: DemoB, data: '' },
{ name: DemoC, data: '' },
])
<swiper :modules="[Virtual]" virtual direction="vertical">
<swiper-slide v-for="(item, index) in slideList" :key="index" v-slot="{ isActive }"
:virtual-index="index">
<!-- 动态组件 -->
<component :is="item.name" :data="item.data" :is-active="isActive" />
</swiper-slide>
</swiper>
即模式设为Virtual,用virtual-index来控制只需要加载当前展示的页面。使用动态组件的好处:某些页面可能在特定条件下不展示,我们可以写入逻辑来控制slideList的数据,这样virtual-index都能对应到正确的下标。
4. 如果有自动跳转到某个页面的需求,例如最后一个页面中有回到第一页的功能(我在年度报告开发中就有最后一页有‘再看一次’按钮),可通过swiper插件提供的回调函数@swiper来实现:
<swiper :modules="[Virtual]" virtual direction="vertical" @swiper="onSwiper">
<swiper-slide v-for="(item, index) in slideList" :key="index" v-slot="{ isActive }" :virtual-index="index">
<!-- 动态组件 -->
<component :is="item.name" :data="item.data" :is-active="isActive" @jump="onJumpToFirstPage" />
</swiper-slide>
</swiper>
let mySwiper = ref(null);
const onSwiper = e => {
// 将swiper实例对象存储起来
mySwiper.value = e;
};
// 跳转到第一页
const onJumpToFirstPage = () => {
mySwiper.value.slideTo(0, 0, false);
};
即调用实例对象中的slideTo方法实现跳转,slideTo方法的三个参数(跳到第几页,时间,是否触发回调函数)
5. 假如要实现每个页面中有个‘向上滑动’提示标,需要考虑最后一个页面中不展示这个标识,而我们使用了swiper虚拟化,此时插件提供的@reachEnd方法是不起作用的,因为此时每一页理论上来说都是最后一页,所以我们只能在最后一个组件中触发方法回调。
最后,贴上整体代码
首页中:
<script setup>
import 'swiper/less';
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Virtual } from 'swiper/modules';
// 异步引入组件,减少首屏加载时间
const DemoA = defineAsyncComponent(() => import('./DemoA.vue'));
const DemoB = defineAsyncComponent(() => import('./DemoB.vue'));
const DemoC = defineAsyncComponent(() => import('./DemoC.vue'));
let slideList = ref([
{ name: DemoA, data: '' },
{ name: DemoB, data: '' },
{ name: DemoC, data: '' },
]);
let mySwiper = ref(null);
const onSwiper = e => {
// 将swiper实例对象存储起来
mySwiper.value = e;
};
// 跳转到第一页
const onJumpToFirstPage = () => {
mySwiper.value.slideTo(0, 0, false);
};
// 是否展示'向上滑动'
let isShowSlideUp = ref(true);
// 隐藏图标
const onHideIcon = item => {
isShowSlideUp.value = false;
};
</script>
<template>
<div class="swiper-wrap">
<swiper :modules="[Virtual]" virtual direction="vertical" :height="height" @swiper="onSwiper">
<swiper-slide v-for="(item, index) in slideList" :key="index" v-slot="{ isActive }" :virtual-index="index">
<!-- 动态组件 -->
<component :is="item.name" :data="item.data" :is-active="isActive" @jump="onJumpToFirstPage" @hide="onHideIcon" />
</swiper-slide>
</swiper>
<div v-if="isShowSlideUp" class="slide-tip">
<img src="../assets/image/slide-icon.png" class="slide-icon" />
<div class="slide-text">向上滑动</div>
</div>
</div>
</template>
<style scoped lang="less">
.swiper-wrap {
position: relative;
height: 100%;
width: 100%;
}
.slide-tip {
position: absolute;
bottom: 40px;
left: 0;
right: 0;
display: flex;
flex-direction: column;
align-items: center;
z-index: 9;
.slide-icon {
width: 12px;
height: 10px;
margin-bottom: 6px;
animation: jump 0.3s ease-in infinite alternate;
}
.slide-text {
color: #000;
font-size: 12px;
}
}
@keyframes jump {
from {
margin-bottom: 6px;
width: 12px;
height: 10px;
}
to {
margin-bottom: 12px;
width: 15px;
height: 12.5px;
}
}
</style>
组件3中:
<script setup>
/* 页面3-组件DemoC */
const props = defineProps({
// 首页传递过来的数据
data: { type: String, default: '' },
// 是否滑动到当前页面
isActive: { type: Boolean, default: false },
});
const emits = defineEmits(['jump', 'hide']);
// 点击'再看一次'
const onClickViewAgain = () => {
emits('jump');
};
watch(
() => props.isActive,
newValue => {
// 该页面展示时,隐藏底部icon
newValue && emits('hide');
}
);
</script>
<template>
<div class="container">
<transition enter-active-class="animate__animated animate__backInRight">
<div v-show="isActive">测试动效文字</div>
</transition>
<div @click="onClickViewAgain">再看一次</div>
</div>
</template>
<style lang="less" scoped>
.container {
position: relative;
height: 100%;
}
</style>