vue使用swiper.js实现页面组件滑动特效

在实现app端和小程序端我们经常能看见这种页面:

当我们滑动页面时,窗口会切换到下一个页面,同时导航栏也会切换到相对应的导航栏,当我们点击导航栏其中的一个元素时,窗口也会自动的滑动到对应的页面。

我在swiper.js官网的样例中并没有看到这样的样式,官网上所有的样式底部导航栏是小圆圈,而不是具体的navigation,因此我通过使用swiper.js提供的api来自行实现了这个功能。

我的思路:

  1. 第一步:滑动页面时,导航栏同步

    是通过swiper-slide组件内部包一个PageMeesage组件,通过子父组件的传值defineProps,将swiper-slide的当前页面的页数的信息传给子组件PageMessage,之后在PageMessage中进行页面信息的整理后,将页面的是否呈现在窗口的信息通过localStorage传到show.vue中,再在此组件中实现bottomNav的渲染。

  2. 第二步:点击导航栏,窗口页面同步

    通过使用swiper.js自带的swiper对象的slideTo函数实现。

下面是我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<!-- show.vue -->
<template>
<swiper
:scrollbar="{hide: false}"
:modules="modules"
class="mySwiper"
@swiper="onSwiper"
@slideChange="onSlideChange"
>
<swiper-slide><PageMessage :pageNum=1></PageMessage></swiper-slide>
<swiper-slide><PageMessage :pageNum=2></PageMessage></swiper-slide>
<swiper-slide><PageMessage :pageNum=3></PageMessage></swiper-slide>
<swiper-slide><PageMessage :pageNum=4></PageMessage></swiper-slide>
</swiper>
<div class="bottomNav">
<div class="bottomNavItem partOne" :class="{ active: ActivePageNum === 1 }" @click="isActiveItem(1)">part 1</div>
<div class="bottomNavItem partTwo" :class="{ active: ActivePageNum === 2 }" @click="isActiveItem(2)">part 2</div>
<div class="bottomNavItem partThree" :class="{ active: ActivePageNum === 3 }" @click="isActiveItem(3)">part 3</div>
<div class="bottomNavItem partFour" :class="{ active: ActivePageNum === 4 }" @click="isActiveItem(4)">part 4</div>
</div>
</template>
<script setup>
//从swiper库引入相关的组件
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Scrollbar } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/scrollbar';
import './show.css'

//我这里创建了一个PageMessage组件,这个组件的功能是存储页面信息,同时呈现页面的内容
import PageMessage from "../components/pageMessage.vue";
import { ref } from "vue";


let ActivePageNum = ref(1);
let pageMessage = ref();
let ActivePage = ref();
let swiperRef = ref();


//这里直接使用return swiper无法在别的函数中调用这个swiper对象,
//因此,我用了一个响应式对象swiperRef将swiper复制给这个ref对象,实现了别的函数中成功调用这个swiper对象
const onSwiper = (swiper) => {
swiperRef.value = swiper;
};

//这里设置时间函数的原因是:在PageMessage页面中localStorage.setItem和这个函数的localStorage.getItem同时进行了,使得我的localStorage.getItem只能得到前一步的页面的活跃信息。
//这里通过一个时间函数可以解决这个问题
const onSlideChange = () => {
let messageString ;
setTimeout(()=>
{
messageString = localStorage.getItem('message');
pageMessage.value = JSON.parse(messageString);
ActivePage.value = pageMessage.value.find(item => item.pageIsActive === true);
ActivePageNum.value = ActivePage.value.pageNumber;
},0)
};

const modules = [Scrollbar]


//这里调用了上面的ref对象swiperRef,成功的通过使用swiper对象,来实现了当点击导航栏时,窗口滑动到对应的页面
function isActiveItem(index){
swiperRef.value.slideTo(index-1);
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/*show.css*/
#app {
height: 100%;
}
html,
body {
position: relative;
height: 100%;
}

body {
background: #eee;
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
font-size: 14px;
color: #000;
margin: 0;
padding: 0;
}

.swiper {
width: 100%;
height: 100%;
bottom: 60px;
}

.swiper-slide {
text-align: center;
font-size: 18px;
background: #fff;
/* Center slide text vertically */
display: flex;
justify-content: center;
align-items: center;
}

.swiper-slide img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}

.bottomNav{
position: fixed;
bottom: 0;
height: 60px;
display: flex;
width: 100%;
flex-direction: row;
align-items: center;
justify-content: space-around;
font-size: 15px;
}
.bottomNavItem.active{
font-size: 20px;
background-color:white;
border-radius: 10px;
padding: 5px;
border: 1px solid #000000;
}

/*这里动画有一个问题,不知道该如何解决,就是我设置动画时,最后一个元素总是没有成功绑定上动画,下面的代码中我的partThree并没有绑定上这个动画*/
.partOne,
.partTwo,
.partFour,
.partThree:hover{
transform: scale(1.05);
transition: 0.3s;
}
.partThree,
.partFour:hover{
transform: scale(1.05);
transition: 0.3s;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<!-- pageMessage -->
<template>
<p>page {{pageNum}} is {{ swiperSlide.isActive ? 'active' : 'not active'}}</p>
</template>
<script setup>
import { useSwiperSlide } from 'swiper/vue';
import {ref, watch} from "vue";

//这里通过defineProps,得到了父组件传来的数据
const props = defineProps(
{
pageNum:Number
}
)


//这里调用了 useSwiperSlide
const swiperSlide = useSwiperSlide();

let change = ref(true);

//这里初始化了页面信息
let pageMessage = ref([
{
pageNumber:1,
pageIsActive:false
},
{
pageNumber:2,
pageIsActive:false
},
{
pageNumber:3,
pageIsActive:false
},
{
pageNumber:4,
pageIsActive:false
}
])


//这里设置了一个监听函数,当swiperSlide发生变化时,进行传值
//当我没有使用判断时,我发现swiperSlide返回的并不是一个页面的isActive信息,意味着当一个页面渲染时,他的前页面和后页面同时也会被渲染,但是我需要的只是当前呈现在窗口的页面,因此我是用了一个判断语句,来筛选出来了当前页面的信息,并且合并信息。
//同时使用localStorage来实现子父组件之间的传值。
watch(swiperSlide, () => {
if (swiperSlide.value.isActive) {
// console.log(props.pageNum);
pageMessage.value[props.pageNum-1] = {
pageNumber:props.pageNum,
pageIsActive:swiperSlide.value.isActive
}
// console.log("child emit")
const messageString = JSON.stringify(pageMessage.value);
localStorage.setItem("message",messageString)
}
});
</script>

具体的源代码可以看这里