这段时间的主业是完成一个家政类小程序,终于是过审核发布了。不得不说微信的这个小程序生态还是颇有想法的,抛开他现有的一些问题不说,其提供的组件系统乍一看还是蛮酷的。比如其提供的一个叫swiper的视图组件,就可以在写界面的时候省不少时间和代码,轮播图片跟可滑动列表都可以用。导致现在回来写angular项目时也想整一个这样的组件出来,本文就将使用angular的组件能力和服务能力完成这么一个比较通用,耦合度较低的swiper出来。
首先要选择使用的技术,要实现的是与界面打交道的东西,自然是实现成一个组件,最终要实现的效果是写下这样的代码就可以完成一个可以滑动的视图来:
<swipers> <swiper>视图1</swiper> <swiper>视图2</swiper> </swipers>
然后要把最基本的组件定义写出来,显然这里要定义两个组件。第一个是父级组件,选择器名字就叫ytm-swipers,目前做的事情仅仅是做一个外壳定义基本样式,使用时的子标签都会插入在ng-content标签中。
@Component({ selector: 'ytm-swipers', template: ` <div> <ng-content></ng-content> </div> `, styles: [` .view-body{height: 100%;width: 100%;overflow: hidden;position: relative;} `] })
第二个就是子视图了,在父级组件下,每个子组件都会沾满父级组件,只有当前的子组件会显示,当切换视图时实际做的就是更改这些子组件的显示方式,说的最简单的话,这个子组件还是仅仅用来加一个子外壳,给外壳添加基本样式,实际的页面内容原封不动放在ng-content标签中。
@Component({ selector: 'swiper', template: ` <div *ngIf="swiper.displayList.indexOf(childId) >= 0" [ngClass]="{'active': swiper.displayList[0] === childId, 'prev': swiper.displayList[2] === childId, 'next': swiper.displayList[1] === childId}"> <ng-content></ng-content> </div> `, styles: [` .view-child{ height: 100%;width: 100%;position: absolute;top: 0; transition: 0.5s linear;background: #fff; overflow-x: hidden; } .view-child.active{left: 0;z-index: 9;} .view-child.next{left: 100%;z-index: 7;} .view-child.prev{left: -100%;z-index: 8;} `] })
下一步是要让这两个父子组件完成心灵的沟通,讲道理其实可以直接使用ElementRef强行取到DOM来操作,不过这里使用的是组件内服务。和普通的服务使用上没差别,不过其provider是声明在某个组件里的,所以此服务只有在此组件以及子组件中可以注入使用。
@Injectable() class SwiperService { public swiperList: number[]; public displayList: number[]; // 0为当前 1为下一个 2为上一个 public current: number; private changing: boolean; constructor() { this.changing = false; this.swiperList = []; this.displayList = []; this.current = 0; } public Add(id: number) { this.swiperList.push(id); switch (this.swiperList.length) { case 1: this.displayList[0] = id; return; case 2: this.displayList[1] = id; return; default: this.displayList[2] = id; return; } } public Next(): Promise<any> { if (this.changing) { return new Promise<any>((resolve, reject) => { return reject('on changing'); }); } this.changing = true; let c = this.swiperList.indexOf(this.displayList[0]); let n = this.swiperList.indexOf(this.displayList[1]); let p = this.swiperList.indexOf(this.displayList[2]); p = c; c = n; n = (c + 1) % this.swiperList.length; this.displayList[0] = this.swiperList[c]; this.displayList[2] = this.swiperList[p]; this.displayList[1] = -1; setTimeout(() => { this.displayList[1] = this.swiperList[n]; this.changing = false; }, 500); return new Promise<any>((resolve, reject) => { return resolve(this.displayList[0]); }); } public Prev(): Promise<any> { if (this.changing) { return new Promise<any>((resolve, reject) => { return reject('on changing'); }); } this.changing = true; let c = this.swiperList.indexOf(this.displayList[0]); let n = this.swiperList.indexOf(this.displayList[1]); let p = this.swiperList.indexOf(this.displayList[2]); n = c; c = p; p = p - 1 < 0 ? this.swiperList.length - 1 : p - 1; this.displayList[0] = this.swiperList[c]; this.displayList[1] = this.swiperList[n]; this.displayList[2] = -1; setTimeout(() => { this.displayList[2] = this.swiperList[p]; this.changing = false; }, 500); return new Promise<any>((resolve, reject) => { return resolve(this.displayList[0]); }); } public Skip(index: number): Promise<any> { let c = this.swiperList.indexOf(this.displayList[0]); if (this.changing || c === index) { return new Promise<any>((resolve, reject) => { reject('on changing or no change'); }); } this.changing = true; let n = (index + 1) % this.swiperList.length; let p = index - 1 < 0 ? this.swiperList.length - 1 : index - 1; this.displayList[0] = this.swiperList[index]; if (index > c) { this.displayList[2] = this.swiperList[p]; this.displayList[1] = -1; setTimeout(() => { this.displayList[1] = this.swiperList[n]; this.changing = false; }, 500); return new Promise<any>((resolve, reject) => { return resolve(this.displayList[0]); }); } else { this.displayList[1] = this.swiperList[n]; this.displayList[2] = -1; setTimeout(() => { this.displayList[2] = this.swiperList[p]; this.changing = false; }, 500); return new Promise<any>((resolve, reject) => { return resolve(this.displayList[0]); }); } } }