Angular2组件与指令的小实践——实现一个图片轮播组件
如果说模块系统是Angular2的灵魂,那其组件体系就是其躯体,在模块的支持下渲染出所有用户直接看得见的东西,一个项目最表层的东西就是组件呈现的视图。
而除了直接看的见的躯体之外,一个完整的“生物”还需要有感觉器官,用来感知外界与其的交互,这就是指令要做的事情。
本文将使用Angular2提供的强大的组件与指令等功能制作出一个简单的图片轮播控件,继续上文打的比方的话这就像是一个“器官”,功能是呈现图片,并感知用户的点击或手势来切换图片。
一、创建组件
结束上文打的尴尬的比方,着眼于一个待开发的ng2项目,它有一个空白的特性页面,现在需要在上面呈现一个图片轮播窗口。
图片轮播是一个需要给用户看见的东西,所以应该使用ng2的组件(Component)来实现它,并且这个功能较为通用,可以将其独立出来方便以后再次使用到。
所以在项目中的共享模块(SharedModule)下创建这个组件名为 slide-img.component。
使用ng2提供的组建装饰器来将这个TypeScript模块正式变身成ng2的组件:
@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 { }
组件的声明
其参数其实已经不能再明确了:
selector就是其使用时的标签名,
templateUrl即组件关联的界面的模板,
styleUrls即仅在此组件内生效的样式表,
animations定义的是一套ng2动画规则。
讲讲最后的这个动画规则:
ng2的动画其实非常简单,步骤为1.定义触发器名,2.定义状态,3.定义切换样式,4.将此触发器应用到具体的标签中,状态作为触发器的值传入。
当ng2检测到动画状态的值更改了,就会套用定义的切换样式,用法思路还算比较明确(当然实际使用时会有一些尴尬的小问题)
二、实现组件
既然是轮播图片组件,要做的事情首先就得是传入轮播图片然后显示出来。
使用过ng1的朋友一定还记得其在定义指令(angular.directive)的时候是通过scope参数(或者link)来传入数据的,而ng2中使用的是Input装饰器,使用的方法如下:
@Input() public imgs: SlideImg[];
使用了此装饰器的变量imgs将可以在运行时接收其他组件传入的图片列表。使用方法如下:
<my-slide-img [imgs]="imgs"></my-slide-img>
关于这里的方括号“[]”,ng2其实提供了多种方式来进行组件模板中值的传入,其中这种变量名用方括号包起来的方法就是其中之一,代表是输入的值,而后面会见到的圆括号来包围的方式,是代表输出的值。
传入了数据后,下一步就是要如何来显示图片到界面上了,没错就是ng1中ng-for指令的升级版*ngFor,除了写法外表面上的差别不大。
关于轮播图片逻辑的具体实现逻辑,笔者使用的方式就是利用ng2动画的状态切换,设置一个当前图片索引值current,*ngFor渲染的图片将其索引与当前索引比较,如果是相邻的图片则设为'prev'状态与'next'状态,ng2会为其加上位置属性为-100%或者100%,如果是当前图片则设为'on'状态,ng2会将其的位置属性设为0,其余均设为'off'状态,ng2会直接将其隐藏,实现的逻辑很简单,考虑也不算周全,笔者就不继续解释献丑了。
最终的轮播图片组件及其模板文件代码如下:
<div class="imgs">
<img src="{{img.Url}}" alt="" class="img"
*ngFor="let img of imgs;let i=index"
(mySwipe)="Change($event)"
[@imgMove]="ImgState(i)">
</div> <div class="btn" (click)="Prev()">Prev</div>
<div class="btn" (click)="Next()">Next</div>
slide-img.component.html
.imgs{
position: relative;width: 100%;height: 15em;
overflow: hidden;
}
.img{
position: absolute;
width: 100%;
height: 100%;
background: pink;
transition: 0.2s;
}
.btn{
display: inline-block;
padding: 1em 2em;font-size: 1em;border-radius: 0.5em;
border: 1px solid #ddd;cursor: pointer;
margin: 1em;background: #eee;box-shadow: 0.1em 0.1em 0.2em #aaa;
}
.btn:active{
background: #eee;
box-shadow: none;
}
slide-img.component.css
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.component.ts
其中有两个地方为讲到,一个是组件代码引入了一个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;
}
}
swipe.directive.ts
指令的声明甚至简单过组建的声明,因为指令不需要依赖于某个视图模板,只需要有个指令名称就足够了。
需要关心的是指令中定义的输出属性:
@Output() public mySwipe = new EventEmitter<string>();
此属性接收了上文组件中的Change($event)回调方法,在此指令中,所做的事情就是监听组件的滑动,收到滑动事件后就触发这个回调,监听使用的是ng2的HostListener装饰器,用法显而易见了。
现在运行起项目来看看效果吧(比较懒就不截动图了):
总结以及题外话:
本文主要使用了ng2几个比较基本的功能——输入属性(Input装饰器)、输出属性(Outut装饰器)、HostListener装饰器、几个系统指令(ngFor)、ng2动画实现了一个比较基本的图片轮播控件。
使用好ng2的组件以及指令能完成很多的事情,其需要学习的东西绝不仅限与本文提到的,包括其底层的渲染,也很值得去研究。
最后提一个尴尬的问题点:
其实最初写这个轮播图片的时候想过要加上拖动的,也就是图片会随手势的滑动实时更新位置。
但后来发现ng2的动画有个尴尬的地方,那就是一定会从初始状态按照定义好的转换效果变化到目标状态。实时滑动需要我在滑动过程中就改变图片的位置,这样的话在滑动结束需要切换图片时,图片居然强行回到了初始位置然后才开始转换动画,一时还想不到继续使用ng2动画来实现这种实时滑动的完美解决办法,实在是尴尬。
Angular2组件与指令的小实践——实现一个图片轮播组件的更多相关文章
- Vue学习—Vue写一个图片轮播组件
1.先看效果: 熟悉的图片轮播,只要是个网站,百分之90以上会有个图片轮播.我认为使用图片轮播. 第一可以给人以一种美观的感受,而不会显得网站那么呆板, 第二可以增加显示内容,同样的区域可以显示更多内 ...
- Angular2组件与指令的小实践
如果说模块系统是Angular2的灵魂,那其组件体系就是其躯体,在模块的支持下渲染出所有用户直接看得见的东西,一个项目最表层的东西就是组件呈现的视图.而除了直接看的见的躯体之外,一个完整的" ...
- 基于ionic框架封装一个图片轮播指令的几点
在这里我想在项目中封装一个图片轮播的指令 (本项目使用的是ionic框架) 1)定义指令 define(['app'],function(myapp){ myapp.directive('myslid ...
- 如何将angular-ui的图片轮播组件封装成一个指令
在项目开发中我们经常会遇到图片轮播的功能点: 如果我们开发人员自己原生手写,将会花费很多的时间,最终得不偿失. 接下来就详细说说如何使用angular-ui发热图片轮播模块,并且将它写成一个指令(便于 ...
- 如何将angular-ui-bootstrap的图片轮播组件封装成一个指令
在项目开发中我们经常会遇到图片轮播的功能点: 如果我们开发人员自己原生手写,将会花费很多的时间,最终得不偿失. 接下来就详细说说如何使用angular-ui发热图片轮播模块,并且将它写成一个指令(便于 ...
- 一分钟搞定AlloyTouch图片轮播组件
轮播图也涉及到触摸和触摸反馈,同时,AlloyTouch可以把惯性运动打开或者关闭,并且设置min和max为运动区域,超出会自动回弹. 除了一般的竖向滚动,AlloyTouch也可以支持横向滚动,甚至 ...
- Omi-touch实战 移动端图片轮播组件的封装
pc端的轮播,移动端的轮播都很常见.一年前,我还为手机端没有左滑,右滑事件从而封装了一个swipe库,可以自定义超过多少滑动时间就不触发,也可以设置滑动多少距离才触发,这一个功能的代码就达到400多行 ...
- JavaScript实现图片轮播组件
效果: 自动循环播放图片,下方有按钮可以切换到对应图片. 添加一个动画来实现图片切换. 鼠标停在图片上时,轮播停止,出现左右两个箭头,点击可以切换图片. 鼠标移开图片区域时,从当前位置继续轮播. 提供 ...
- EUI Scroller实现图片轮播 组件 ItemScroller
一 自定义组件如下 /** * 文 件 名:ItemScroll.ts * 功 能: 滚动组件 * 内 容: 自定义组件,支持多张图片水平(垂直)切换滚动 * * Example: * 1. 从自定义 ...
随机推荐
- XCode里的模拟器到底在哪里?我的App被放到哪里了?如何寻找真机的沙盒文件?
一. 开发iOS,必然少不了和XCode这个家伙打交道.平时我们调试自己的App的时候,最常用到的就是模拟器Simulator了,调试的时候,我们的App会自动被XCode安装到模拟器中去,不过: 你 ...
- 02 easyui -parser
parser: 页面节点class=“easyui-” +组件名(在plugins里) ,则可以 渲染成相应的组件.
- vuejs 子组件传递父组件的第二种方式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 绘图——Android绘图基础:Canvas、Paint等
Android的绘图应该继承View组件,并重写它的onDraw(Canvas canvas)方法即可. 重写onDraw(Canvas canvas)方法时涉及一个绘图API:Canvas,Canv ...
- Mysql中常用的函数汇总
Mysql中常用的函数汇总: 一.数学函数abs(x) 返回x的绝对值bin(x) 返回x的二进制(oct返回八进制,hex返回十六进制)ceiling(x) 返回大于x的最小整数值exp(x) 返回 ...
- JIRA描述默认值设置
JIRA描述默认值设置 Setting a Default Value in the Description Field 转自https://confluence.atlassian.com/jira ...
- Servlet的请求HttpServletRequest
一.从容器到HttpServlet 1.web容器作了什么 web容器做的事情就是,创建Servlet实例,并完成Servlet的名称注册及URL模式的对应.在请求来到时,web容器会转发给正确的Se ...
- 分布式环境中三种Session管理方法的使用场景及优缺点
在分布式环境,管理Session通常使用下面三种方式: 一.Session Replication 方式管理 (即session复制) 简介:将一台机器上的Session数据广播复制到 ...
- 腾讯面试题:10G 个整数,乱序排列,要求找出中位数。内存限制为 2G。
腾讯面试题:10G 个整数,乱序排列,要求找出中位数.内存限制为 2G. 题目和基本思路都来源网上,本人加以整理. 题目:在一个文件中有 10G 个整数,乱序排列,要求找出中位数.内存限制为 2G.只 ...
- Android 创建Library Project(库项目)与引用操作
由于在开发过程,为了实现未曾了解的某种效果与特定功能,而求助于网上优秀的开源项目,在使用过程中发现引用开源的Library Project(库项目),的确可以解决很多问题,而且也给出了一种思路,好的软 ...