0%

Angualr Swiper 使用

之前也使用过swiper,但现在我们可以在angular中可以更好的使用angular方式使用swiper

安装

使用npm或yarn安装swiper:

1
2
npm i swiper
yarn add swiper

使用

需要在当前模块中导入swiperModule

1
2
3
4
5
6
7
8
import {SwiperModule} from 'swiper/angular';
@NgModule({
imports: [
SwiperModule,
],
})
export class YouModule{
}

我们先用下基本的,我们希望有基本的swpier滑动,还需要swiper展示页数的小点,并展示左右切换的按钮。

首先在组件ts中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import {SwiperOptions} from 'swiper';
import SwiperCore, {Pagination, Navigation} from 'swiper';
SwiperCore.use([Pagination, Navigation])

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements AfterViewChecked, AfterViewInit, DoCheck {
config: SwiperOptions = {
slidesPerView: 1,
spaceBetween: 50,
navigation: {
},
pagination: {
clickable: true,
}
}
}

然后在组件模板中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div class="swiper-area">
<swiper [config]="config">
<ng-template swiperSlide>
<div class="swiper-item">
1
</div>
</ng-template>
<ng-template swiperSlide>
<div class="swiper-item">
2
</div>
</ng-template>
<ng-template swiperSlide>
<div class="swiper-item">
3
</div>
</ng-template>
</swiper>
</div>

需要注意的是,我们需要导入css文件,否则程序运行的时候会找不到样式而错乱。

我们这里用的是scss,那么需要在styles.scss里面导入:

1
2
3
@import 'swiper/scss';
@import 'swiper/scss/navigation';
@import 'swiper/scss/pagination';

注意,这里在webstorm里面可能是识别不到的,会有红色下划线,这是没关系的,忽略它。

然后我们可以看到程序正常运行。

特别的需求

程序运行了很开心,啪,老板的需求来了:我的swiper总共有五页,我希望

  1. 左右切换按钮放在右侧中间。
  2. 在swiper上面加两个按钮,当点击第一个按钮的时候切换到第1页,点击第二个按钮的时候切换到最后一页
  3. 在页面上展示当前是第几页(别问,问就是需求特殊)

🤔

定制切换按钮

借助TypeScript,我们可以很容易查看下navigation的参数配置,它的定义时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface NavigationOptions {
// String with CSS selector or HTML element of the element that will work
nextEl?: CSSSelector | HTMLElement | null;

// String with CSS selector or HTML element of the element that will work
prevEl?: CSSSelector | HTMLElement | null;

/**
* Toggle navigation buttons visibility after click on Slider's container
*/
hideOnClick?: boolean;

// CSS class name added to navigation button when it becomes disabled
disabledClass?: string;

// CSS class name added to navigation button when it becomes hidden
hiddenClass?: string;

// CSS class name added to navigation button when it is disabled
lockClass?: string;
}

我们需要创建自定义的导航按钮:

1
2
3
4
5
6
<div class="swiper-area">
<div class="customer-navigation">
<div class="prev-btn"> - </div>
<div class="next-btn"> + </div>
</div>
</div>

然后去搞定样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.customer-navigation{
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
display: flex;
justify-content: flex-end;
align-items: center;
z-index: 10;
.next-btn, .prev-btn{
width: 40px;
height: 40px;
border: 1px solid #ccc;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
}
.disable{
color: #555;
}
}

需要注意的是,这里用了绝对定位,我们需要给这个区域加上一个z-index提高层级,否则被slide遮住了点不到就很尴尬。

然后去组件ts里面配置:

1
2
3
4
5
6
7
8
9
10
11
12
config: SwiperOptions = {
slidesPerView: 1,
spaceBetween: 50,
navigation: {
nextEl: '.next-btn',
prevEl: '.prev-btn',
disabledClass: 'disable'
},
pagination: {
clickable: true,
}
}

再次运行我们可以看到满足老板的需求,我们不用写ts方法就能通过配置的方式完美实现需求。
swiper

按钮控制切换页码

这个就需要用到当前swiper实例了,借助angular的ViewChild,我们可以很方便的访问swiper实例。

首先组件模板中增加一个按钮组:

1
2
3
4
<div class="handle-group">
<div class="btn-1" (click)="jump('first')">跳到第一页</div>
<div class="btn-2" (click)="jump('last')">跳到最后一页</div>
</div>

然后将swiper组件绑一个模板变量:

1
<swiper [config]="config" #mySwiper></swiper>

然后在组件ts中通过ViewChild获取swiper实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export class AppComponent implements AfterViewChecked, AfterViewInit, DoCheck {
@ViewChild('mySwiper', {static: false}) mySwiper: SwiperComponent;
jump(type: string) {
const len = this.mySwiper.swiperRef.slides.length || 0;
switch (type) {
case 'first':
this.mySwiper.swiperRef.slideTo(0)
break;
case 'last':
this.mySwiper?.swiperRef.slideTo(len);
break;
}
}
}

这样通过swiper实例可以随意操控,比如swiperRef.slideNext()切换到下一页之类的。slideTo方法可以直接跳转到任意索引的页面。

在页面上展示页码

我们在文档的swiper events里面可以查看所有能处理的事件,我们需要在页面切换的时候拿到当前的页码索引。我们可以使用indexChange(number)事件。

首先在组件模板的swiper上加上这个时间的处理方法:

1
<swiper [config]="config" #mySwiper (indexChange)="onIndexChange($event)"></swiper>

我们定义onIndexChange这个方法来接收swiper组件的indexChange事件。在组件ts中:

1
2
3
4
5
6
7
// 定义追踪页面索引
currentIndex: number = 0;

// 接收到indexChange事件后的处理方法,将swiper的当前页码索引赋值给组件的变量,用以视图的更新。
onIndexChange(e: any) {
this.currentIndex = e;
}

然后我们在组件模板中创建一个区域来放置页码:

1
<div class="page-number">{{currentIndex}}</div>

然后刷新页面,看运行的效果:

angular swiper

可以看到,只有我们点击上面自己的按钮的时候,页面索引才会正常显示;滑动slider或者点击swiper的左右切换按钮,索引数字都不动。

这是为啥?

swiperjs angular里面有句话:

Note that Swiper Angular component all events emits outside of NgZone for better performance. Dont forget to use ngzone.run or ChangeDetector if you need to change view (e.g slides) in event handles (e.g slideChange).

为了更好的性能考虑,swiper的事件都在ngZone之外发出。所以当我们在swiper发出的事件里面处理业务的时候,如果有需要视图也发生变更的,那么需要使用ngzone.runChangeDetector手动处理变更检测,让模板更新数据。

我们需要去修改组件的ts,去手动处理变更检测:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent implements AfterViewChecked, AfterViewInit, DoCheck {
constructor(
private cd: ChangeDetectorRef,
) {
}

onIndexChange(e: any) {
this.currentIndex = e;
this.cd.detectChanges();
}
}

ChangeDetectorRef里面的detectChanges()方法可以显式的让变更检测进行更新。

现在再次刷新我们可以看到正确的结果:
angular swiper

思考

在样式文件角度上上,当我们全量引入的时候:

1
2
@import '~swiper/css/bundle';

打包的大小:

1
2
3
4
5
Initial Chunk Files               | Names         |      Size
main.218a964c3766cbbfefa4.js | main | 230.22 kB
polyfills.8ffc90708769d5d770b0.js | polyfills | 36.21 kB
styles.0761949cc4f4dc497c0f.css | styles | 14.88 kB
runtime.25b65ed6c90e3c1e04ea.js | runtime | 1.04 kB

然后看下分量的引入方式:

1
2
3
@import 'swiper/scss';
@import 'swiper/scss/navigation';
@import 'swiper/scss/pagination';

打包的大小:

1
2
3
4
main.218a964c3766cbbfefa4.js      | main          | 230.22 kB
polyfills.8ffc90708769d5d770b0.js | polyfills | 36.21 kB
styles.884372c6a15e19ce19ae.css | styles | 10.71 kB
runtime.25b65ed6c90e3c1e04ea.js | runtime | 1.04 kB

样式文件上小了4kb,虽然不怎么显著,但能将一些不用的模块样式甩掉,也算不错。

再看看js层面。我们上面都是使用的swiper/angular,也是按模块引入的,那我们现在直接使用swiper来看看:

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
import Swiper from 'swiper';

export class AppComponent implements AfterViewChecked, AfterViewInit, DoCheck {

currentIndex: number = 0;

constructor(
private cd: ChangeDetectorRef,
) {
}

ngAfterViewInit() {
const mySwiper = new Swiper('.swiper-container', {
navigation: {
nextEl: '.next-btn',
prevEl: '.prev-btn',
disabledClass: 'disable'
},
pagination: {
clickable: true,
}
})
}
}

然后我们打包看看:

1
2
3
4
main.12365308f754f1192502.js      | main          | 168.36 kB
polyfills.8ffc90708769d5d770b0.js | polyfills | 36.21 kB
styles.0761949cc4f4dc497c0f.css | styles | 14.88 kB
runtime.25b65ed6c90e3c1e04ea.js | runtime | 1.04 kB

纳尼,包还小了???😒

虽然小了几十kb,但这样我们无法使用swiper component,而且运行的时候鼠标在swiper上的移动都会触发变更检测,这很蛋疼。

看起来,swiper angular的方式没有减小js的打包,但方便了使用,同时性能上更优化。


今天的蚌埠就住到这里吧,后续有更好的场景了会随缘更新。

Swiper Angular 文档:

码字辛苦,打赏个咖啡☕️可好?💘