import { Component, OnInit, Input, animate, style, transition, trigger, state, HostListener } from '@angular/core'; import { SlideImg } from './slide-img.interface'; @Component({ selector: 'my-slide-img', templateUrl: 'slide-img.component.html', styleUrls: ['slide-img.component.css'], animations: [ trigger('imgMove', [ /** 不显示 */ state('off', style({'display': 'none', 'z-index': '0', 'transform': 'translateX(0)'})), /** 上一张图片 */ state('prev', style({'z-index': '1', 'transform': 'translateX(-100%)'})), /** 下一张图片 */ state('next', style({'z-index': '2', 'transform': 'translateX(100%)'})), /** 当前图片 */ state('on', style({'z-index': '3', 'transform': 'translateX(0)'})), transition('prev=>on', [ animate('0.3s ease-in') ]), transition('next=>on', [ animate('0.3s ease-in') ]), transition('on=>prev', [ animate('0.3s ease-in') ]), transition('on=>next', [ animate('0.3s ease-in') ]) ]) ] }) export class SlideImgComponent { @Input() public imgs: SlideImg[]; public current; constructor() { this.current = 0; } public ImgState(index) { if (this.imgs && this.imgs.length) { if (this.current === 0) { return index === 0 ? 'on' : index === 1 ? 'next' : index === this.imgs.length - 1 ? 'prev' : 'off'; } else if (this.current === this.imgs.length - 1) { return index === this.imgs.length - 1 ? 'on' : index === this.imgs.length - 2 ? 'prev' : index === 0 ? 'next' : 'off'; } switch (index - this.current) { case 0: return 'on'; case 1: return 'next'; case -1: return 'prev'; default: return 'off'; } } else { return 'off'; } } public Next() { this.current = (this.current + 1) % this.imgs.length; } public Prev() { this.current = this.current - 1 < 0 ? this.imgs.length - 1 : this.current - 1; } public Change(e) { if (e === 'left') { this.Next(); } else if (e === 'right') { this.Prev(); } } }
其中有两个地方为讲到,一个是组件代码引入了一个slide-img.interface 模块,这个仅仅用来规范一下轮播图片的格式,二是在html中还有一个节点名为(mySwipe)这就是接下来要讲的输出属性,目前知道的它的作用是,当用户滑动图片时,将触发此节点配置的回调方法,所做的事情就是判断发生了左滑事件还是右滑事件,分别触发上一张图或下一张图的切换。
三、给轮播图片控件加上手势效果
轮播图片在移动端很需要加上手势滑动的效果,所以接下来要给这个轮播组件加上一个指令,用于响应用户的滑动手势。代码如下:
import { Directive, Input, HostListener, Output, EventEmitter } from '@angular/core'; @Directive({ selector: '[mySwipe]' }) export class SwipeDirective { @Output() public mySwipe = new EventEmitter<string>(); private touchStartX; private touchStartY; @HostListener('touchstart', ['$event']) public onTouchStart(e) { this.touchStartX = e.changedTouches[0].clientX; this.touchStartY = e.changedTouches[0].clientY; } @HostListener('touchend', ['$event']) public onTouchEnd(e) { let moveX = e.changedTouches[0].clientX - this.touchStartX; let moveY = e.changedTouches[0].clientY - this.touchStartY; if (Math.abs(moveY) < Math.abs(moveX)) { /** * Y轴移动小于X轴 判定为横向滑动 */ if (moveX > 50) { this.mySwipe.emit('right'); } else if (moveX < -50) { this.mySwipe.emit('left'); } } else if (Math.abs(moveY) > Math.abs(moveX)) { /** * Y轴移动大于X轴 判定为纵向滑动 */ if (moveY > 50) { this.mySwipe.emit('down'); } else if (moveY < -50) { this.mySwipe.emit('up'); } } this.touchStartX = this.touchStartY = -1; } }
指令的声明甚至简单过组建的声明,因为指令不需要依赖于某个视图模板,只需要有个指令名称就足够了。
需要关心的是指令中定义的输出属性:
@Output() public mySwipe = new EventEmitter<string>();
此属性接收了上文组件中的Change($event)回调方法,在此指令中,所做的事情就是监听组件的滑动,收到滑动事件后就触发这个回调,监听使用的是ng2的HostListener装饰器,用法显而易见了。
现在运行起项目来看看效果吧(比较懒就不截动图了):
总结以及题外话:
本文主要使用了ng2几个比较基本的功能——输入属性(Input装饰器)、输出属性(Outut装饰器)、HostListener装饰器、几个系统指令(ngFor)、ng2动画实现了一个比较基本的图片轮播控件。
使用好ng2的组件以及指令能完成很多的事情,其需要学习的东西绝不仅限与本文提到的,包括其底层的渲染,也很值得去研究。
最后提一个尴尬的问题点:
其实最初写这个轮播图片的时候想过要加上拖动的,也就是图片会随手势的滑动实时更新位置。