Swiper 是纯javascript打造的滑动特效插件,面向手机、平板电脑等移动终端。中文版文档见Swiper中文网
在应用到Vue的工程中时,有 Vue-Awesome-Swiper 可用,但是也发现一些问题,最后自己简单封装一下Swiper绕过。
先看原始版直接在Vue中使用:
<!DOCTYPE html>
<html lang="zh-CN" >
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>swiper demo</title>
<link rel="dns-prefetch" href="https://cdnjs.cloudflare.com">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.5.0/css/swiper.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.8/vue.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.5.0/js/swiper.min.js"></script>
<style type="text/css">
html, body {position: relative; height: 100%; font-size: 18px; }
body {margin: 0; padding: 0; }
#app{height: 100%;}
.swiper-container {width: 100%; height: 100%; }
.swiper-slide { background: #fff; display: flex; justify-content: center; align-items: center; }
</style>
</head>
<body>
<div id="app">
<div class="swiper-container">
<div class="swiper-wrapper">
<div v-for="(c, i) in bgColors" class="swiper-slide" :style="{background: c}">Slide {{i+1}}</div>
</div>
<div class="swiper-pagination"></div>
</div>
</div>
<script type="text/javascript">
var app = new Vue({
el: '#app',
data:{
bgColors: ['Linen', 'Cornsilk', 'FloralWhite', 'LemonChiffon', 'MistyRose']
},
mounted: function(){
this.$nextTick(function(){
swiper = new Swiper('.swiper-container',{
pagination: {
el: '.swiper-pagination'
}
});
});
}
});
</script>
</body>
</html>
swiper的实例会附加在dom节点上,在vue内部可以通过this.$el.swiper访问到。只不过使用swiper比较多的话,需要在各种地方写很多实例化swiper的代码。把swiper封装成一个vue组件就很有必要了,而且有现成的可用。
下面是使用vue-awesome-swiper的代码:
<script type="text/javascript" src="dist/js/vue-awesome-swiper.js"></script>
<div id="app">
<swiper :options="swiperOption">
<swiper-slide v-for="(c, i) in bgColors" :style="{background: c}">Slide {{i+1}}</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
<script type="text/javascript">
Vue.use(window.VueAwesomeSwiper)
var app = new Vue({
el: '#app',
data:{
bgColors: ['Linen', 'Cornsilk', 'FloralWhite', 'LemonChiffon', 'MistyRose'],
swiperOption: {
pagination: {
el: '.swiper-pagination'
}
}
}
});
</script>
但是如果异步加载的数据,延时渲染swiper-slide中的内容,会导致width或者height初始计算不对,就算再调用swiper.update()已无法更新。而且如果需要在swiper-wrapper同级加入元素只能放置在slot为pagination, button-prev, button-next, scrollbar 中,而且无法给swiper-wrapper所在的节点增加额外的class。于是借鉴vue-awesome-swiper的思路自己来一个。
<div id="app">
<swiper :options="swiperOption">
<div class="swiper-slide" v-for="(c, i) in bgColors" :style="{background: c}">Slide {{i+1}}</div>
<template #footer>
<div class="swiper-pagination"></div>
</template>
</swiper>
</div>
<script type="text/javascript">
Vue.component('swiper', {
template:`
<div class="swiper-container">
<slot name="header"></slot>
<div class="swiper-wrapper" :class="extWrapperClass">
<slot></slot>
</div>
<slot name="footer"></slot>
</div>
`,
props: ['options', 'extWrapperClass'],
data (){
return {
}
},
mounted: function(){
this.update();
},
updated (){
this.update();
},
beforeDestroy() {
this.$nextTick(function() {
if (this.$el.swiper && this.$el.swiper.destroy) {
this.$el.swiper.destroy()
}
})
},
methods: {
init (){
var opts = Object.assign({}, this.options);
if(opts.on){
Object.keys(opts.on).forEach(key => {
opts.on[key] = opts.on[key].bind(this);
});
}
var swiper = new Swiper(this.$el, opts);
return swiper;
},
update () {
this.$nextTick(()=>{
this.init();
});
}
}
});
var app = new Vue({
el: '#app',
data:{
bgColors: ['Linen', 'Cornsilk', 'FloralWhite', 'LemonChiffon', 'MistyRose'],
swiperOption: {
pagination: {
el: '.swiper-pagination'
}
}
}
});
</script>
增加了extWrapperClass属性可以可swiper-wrapper增加其他的class了。事件的监听不用vue代理一层,而直接交给swiper,并且切换了上下文为vue实例,即事件方法里this本来指向swiper实例,现在指向vue实例,方便访问vue。如果要访问swiper实例,用this.$el.swiper可以访问到。
swiperOption:{
pagination: {
el: '.swiper-pagination'
},
on: {
touchMove: function(e){
console.log(this.$el.swiper.translate);
}
}
}
在使用swiper的自动播放autoplay和循环loop两个特性时,如果动态的swiper-slide只有一个就比较尴尬了。甚至在有时候一个swiper-slide也没有,根本就不需要swiper实例的存在。于是需要在合适的时候初始化swiper,并在一些时候销毁swiper。
完整版代码如下:
<!DOCTYPE html>
<html lang="zh-CN" >
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>swiper demo</title>
<link rel="dns-prefetch" href="https://cdnjs.cloudflare.com">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.5.0/css/swiper.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.8/vue.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.5.0/js/swiper.min.js"></script>
<style type="text/css">
html, body {position: relative; height: 100%; font-size: 18px; }
body {margin: 0; padding: 0; }
#app{height: 100%;}
.swiper-container {width: 100%; height: 100%; }
.swiper-slide { background: #fff; display: flex; justify-content: center; align-items: center; }
</style>
</head>
<body>
<div id="app">
<swiper :options="swiperOption">
<div class="swiper-slide" v-for="(c, i) in bgColors" :style="{background: c}">Slide {{i+1}}</div>
<template #footer>
<div class="swiper-pagination"></div>
</template>
</swiper>
</div>
<script type="text/javascript">
Vue.component('swiper', {
template:`
<div class="swiper-container">
<slot name="header"></slot>
<div class="swiper-wrapper" :class="extWrapperClass">
<slot></slot>
</div>
<slot name="footer"></slot>
</div>
`,
props: ['options', 'extWrapperClass'],
data (){
return {
}
},
mounted: function(){
this.update();
},
updated (){
this.update();
},
beforeDestroy() {
this.$nextTick(function() {
if (this.$el.swiper && this.$el.swiper.destroy) {
this.$el.swiper.destroy()
}
})
},
methods: {
init (){
var opts = Object.assign({}, this.options);
if(opts.on){
Object.keys(opts.on).forEach(key => {
opts.on[key] = opts.on[key].bind(this);
});
}
var swiper = new Swiper(this.$el, opts);
return swiper;
},
update () {
this.$nextTick(()=>{
//根据slide的数量决定swiper的初始化及更新
var len = this.$el.querySelectorAll('.swiper-wrapper:first-of-type > .swiper-slide').length;
var swiper = this.$el.swiper;
if(len == 0) {
if(swiper){
swiper.destroy();
}
return;
}
if(swiper){
swiper.update();
}else{
swiper = this.init();
}
if(this.options.autoplay){
if( this.options.loop ? len > 3 : len > 1){
swiper.autoplay.start();
}else{
swiper.autoplay.stop();
}
}
});
}
}
});
var app = new Vue({
el: '#app',
data:{
bgColors: ['Linen', 'Cornsilk', 'FloralWhite', 'LemonChiffon', 'MistyRose'],
swiperOption: {
pagination: {
el: '.swiper-pagination'
}
}
}
});
</script>
</body>
</html>
本文介绍了在Vue项目中如何简单封装Swiper组件,解决Vue-Awesome-Swiper存在的问题,包括异步数据渲染导致的尺寸计算错误、swiper-wrapper额外类名设置以及事件处理。通过封装,可以更灵活地控制Swiper实例,并在需要时动态初始化和销毁。
2448

被折叠的 条评论
为什么被折叠?



