总结在使用swiper插件上的一些不常见的常用的属性和遇到的问题
背景
因为一直有在跟进可视化编辑项目,静态页面在设计上很多类似轮播交互,为了避免繁杂的重复工作。Swiper这个插件开源、免费、稳定、使用简单、功能强大,是架构移动终端网站的重要选择。
swiper基础使用
Swiper插件在公司项目里面用到的还是比较多的,基础使用我这里就不会过多介绍。
swiper初始化
const mySwiper=new Swiper('#mySwiper',{
//一些swiper的基础配置
})
基于swiper几个常见的交互
1.swiper的网格分布
常见的交互是作为类似导航的头部,点击切换导航下的内容。
另外也能作为多条内容分组显示,一次只显示一组内容,一组有多个子项
如下图:
导航类型:
分组类型:
代码:
const mySwiper=new Swiper('#mySwiper',{
centeredSlides:false,//激活页始终处于居中状态,这个属性可能会导致第一页和最后一页留白
slidesPerView:1,//一屏显示slides的个数
slidesPerGroup:1,//分组slides,一般和slidesPerView数据一样
spaceBetween:10,//每一个slide之间的间距
centerInsufficientSlides:true,//如果开启这个参数,当slides的总数小于slidesPerView时,slides居中
breakpoints:{
320: {
// 当屏幕宽度大于等于320
slidesPerView: 2,
spaceBetween: 10,
},
768: {
// 当屏幕宽度大于等于768
slidesPerView: 4,
spaceBetween: 10,
},
1280: {
// 当屏幕宽度大于等于1280
slidesPerView: 6,
spaceBetween: 10,
},
}
})
centeredSlides留白 解决方案:可以搭配loop
属性,使swiper首尾闭合,首尾就不会留白。
如果不需要loop
只是想初始化的时候首尾不留白,需要搭配initialSlide
属性,初始化当前激活页。
centerInsufficientSlides
一般用在有自适应页面的要求,根据屏幕大小设置一屏显示slides的个数,但是又需要居中,适用大屏幕下个数较少的情况。搭配breakpoints
效果很好。
breakpoints
断点设定:根据屏幕宽度设置某参数为不同的值,类似于响应式布局的media only screen and (min-width: 480px)
只有部分不需要变换布局方式和逻辑结构的参数支持断点设定,如slidesPerView
、slidesPerGroup
、 spaceBetween
、slidesPerColumn
、slidesPerGroupSkip
,而像loop
、direction
、effect
等则无效。
2.有空间层次感的轮播(ie下切换效果比较卡顿)
插件在切换效果上有5种:默认为"slide"(位移切换),可设置为’slide’(普通切换、默认),“fade”(淡入)“cube”(方块)“coverflow”(3d流)“flip”(3d翻转)。
但是大多数需要的效果,这几个并不能满足。
const mySwiper=new Swiper('#mySwiper',{
watchSlidesProgress:true,
on:{
progress(){
const THIS = this;
const width = THIS.slides.eq(0)[0].offsetWidth / 1.5;
for (let i = 0; i < THIS.slides.length; i++) {
const slide = THIS.slides.eq(i);
const slideProgress = THIS.slides[i].progress;
let modify = 1;
if (Math.abs(slideProgress) > 1) {
modify = (Math.abs(slideProgress) - 1) * 0.08 + 1;
}
const translate = `${slideProgress * modify * width}px`;
const scale = 1 - Math.abs(slideProgress) / 5;
const zIndex = 999 - Math.abs(Math.round(10 * slideProgress));
slide.transform(
`translate3d(${translate}, 0px, 0px) scale(${scale})`,
);
slide.css('zIndex', zIndex);
slide.css('opacity', 1);
if (Math.abs(slideProgress) > 3) {
slide.css('opacity', 0);
}
}
}
}
})
watchSlidesProgress
:Progress(进度、进程)分为swiper的progress 和每个slide单独的progress。
开启这个参数来计算每个slide的progress,Swiper的progress无需设置即开启!
效果如图:
插件的目前遇到的比较难解决的问题
loop模式:会在原本slide前后复制若干个slide(默认一个)并在合适的时候切换,让Swiper看起来是循环的。react我们一般需要在slides里面触发事件的时候就是直接在元素上绑定onClick 。但是这个swiper的复制的是只一个页面,并没有复制页面内部绑定在元素上的事件,因此会造成事件失效。
如果是点击事件的话,插件在初始化的配置里面可以设置整个swiper触发到点击事件。
const mySwiper=new Swiper('#mySwiper', {
loop: true, // 引发问题的根源
on:{
click(e){
if(!e.target.id) return; // e.target.id 主要起传参的作用
console.log(e.target.id)
}
},
});
为什么说这是一个目前比较难解决的问题呢?
结合可视化编辑器项目来说,编辑器里面配置的有一系列组件,包括用swiper制作的一个基础组件;
其中每一个slide都是一个容器,可以拖拽放置其他组件;
而这些组件内部绝大部分都会有自己的初始化操作甚至是绑定事件;
loop模式导致复制过后的slides里无法触发组件绑定的事件,有的甚至可能无法初始化组件,就会有空白页的情况;
而且slide内部组件的不确定性,也导致了上面那个在整个swiper上绑定方法是不可行的。
一个不太聪明的解决方法:
轮播切换会返回两个当前slide的索引,一个是activeIndex
,另外一个是realIndex
;
正常模式下,这两个值是相等的。但是loop模式下,activeIndex
会加上被loop复制出来的个数,还有被复制出来的slide会携带一个叫swiper-slide-duplicate
的class类名。
可以根据这两个条件,对轮播切换的时候做一下限制,如果切换到的slide具有swiper-slide-duplicate
类名,就让当前这个slide切换到当前swiper的realIndex
的那个slide。
const mySwiper=new Swiper('#mySwiper',{
loop:true,
on:{
slideChangeTransitionEnd:()=>{
if (mySwiper) {
const { realIndex, activeIndex } = mySwiper;
const itemSlideDom = mySwiper.slides[activeIndex];
if (itemSlideDom.className.indexOf('swiper-slide-duplicate ') > 0) {
mySwiper.slideToLoop(realIndex, 0, true);
}
}
}
}
})
不过,为什么叫一个不太聪明的解决办法呢?因为slide切换都有自己的过渡效果,这种方法会导致在交互上有一个比较短暂的闪屏,也就是复制出来的白屏会一闪而过到真实的那一屏。