前言
这段时间跟进公司一个项目用Angular4实现,其中涉及到轮播图的生成,众所周知Angular是一个反DOM操作的框架,习惯了操作DOM的JSer们初用的时候会很不习惯,实现轮播效果仿佛也成了难题。遇到问题第一想法当然是搜一下啦~~ n(*≧▽≦*)n 然而很少有针对Angular2以上版本实现的帖子 大都是基于AngularJS实现的。于是在我实现之后将我的思路与众位道友分享下。
功能实现自动轮播【滑动方式】,鼠标悬停静止,手动切换。步骤如下:
一、基础准备
定义基础组件 banner.component.ts、banner.component.html、banner.component.css(本案例只做效果所以没有从服务器获取图片);
在AppModule中声明 BrowserAnimationsModule
// app.module.ts imports: [ BrowserModule, HttpClientModule, BrowserAnimationsModule, AppRoutingModule ]
在组建中导入Angular动画的基本元素
// banner.component.ts import { trigger, state, animate, transition, style } from '@angular/animations';
在组建中定义图片数据【后期可从服务器获取】
// banner.component.ts bannerPic: any[] = [ {img: '5'}, {img: '1'}, {img: '2'}, {img: '3'}, {img: '4'} ];
定义HTML架构
<!-- banner.component.html --> <div class="show"> <span></span> <ul> <li *ngFor="let img of bannerPic">{{img.img}}</li> <!-- 获取到数据后可用<img>标签显示图片 --> </ul> <span></span> </div>
定义CSS样式
/* banner.component.css */ li{ list-style: none; width: 80vw; margin: 0; height: 30vw; background-color: rgb(200,200,80); text-align: center; line-height: 30vw; float: left; font-weight: bold; font-size: 120px; } .show>span{ position: absolute; width: 30px; height: 50px; top: 50%; margin-top: -25px; background-size: 50px; background-position: center; background-repeat: no-repeat; background-color: rgba(0,0,0,.4); opacity: 0; cursor: pointer; transition: opacity .3s; } .show:hover>span{ opacity: 1; } .show>span:first-child{ left: 10px; background-image: url("images/back.png"); } .show>span:last-child{ right: 10px; background-image: url("images/more.png"); }
至此基本内容定义完成,CSS大家可根据需要自行调整
二、创建动画
在组件元数据中定义一个名为 ‘carousel’ 的动画触发器,它有四个状态用于处理不同的情况其中包括:默认状态 stay,点击‘前一张’时的默认状态 stayR, ‘下一张’的状态 moveLeft, ‘上一张’的状态 moveRight;同时有三个状态间的跳转效果
// banner.component.ts @Component({ selector: 'app-banner', templateUrl: './banner.component.html', styleUrls: ['./banner.component.css'], animations: [ trigger('carousel', [ state('stay', style({ marginLeft: '-80vw' })), state('stayR', style({ marginLeft: '-80vw' })), state('moveLeft', style({ marginLeft: '-80vw' })), state('moveRight', style({ marginLeft: '-80vw' })), transition('* => moveLeft', animate('500ms ease-in-out', style({ marginLeft: '-160vw' }))), transition('* => stay', animate('500ms ease-in-out', style({ marginLeft: '-160vw' }))), transition('* => moveRight', animate('500ms ease-in-out', style({ marginLeft: '0vw' }))) ]) ] })
将动画触发器附加到元素上,并定义状态变量 ‘state’
<!-- banner.component.html --> <ul [@carousel] = 'state'> <li *ngFor="let img of bannerPic">{{img.img}}</li> </ul>
// banner.component.ts state = 'stay';
定义播放事件,并在ngOnInit中调用。Angular的播放效果其实就是状态间的切换,我们设置3秒切换一次
// banner.component.ts timer: any; autoPlay(): void { const me = this; this.timer = setInterval(() => { me.state = me.state === 'stay' ? 'moveLeft' : 'stay'; }, 3000); } ngOnInit() { this.autoPlay(); }
到这我们的动画就可以播放了。
三、使动画循环
Angular不操作DOM 因此在图片的切换上我们直接进行数据交换就可以了。
首先我们要使用Angular自带的动画事件 ’done‘ ,该事件会在动画播放后调用我们指定的函数。
<!-- banner.component.html --> <ul [@carousel] = 'state' (@carousel.done)="afterPlay()"> <li *ngFor="let img of bannerPic">{{img.img}}</li> </ul>
定义函数 ’afterPlay()‘,将第一条图片数据插入到最后一条
// banner.component.ts afterPlay(): void { this.bannerPic.push(this.bannerPic[0]); this.bannerPic.shift(); }
至此我们的轮播图就可以正常实现轮播了。
四、制作点击效果
首先我们要实现在鼠标移到Banner时播放停止 移出则继续
<!-- banner.component.html --> <div class="show" (mouseenter)="stopPlay()" (mouseleave)="autoPlay()"> ... </div>
定义stopPlay() 播放暂停函数
// banner.component.ts stopPlay(): void { clearInterval(this.timer); }
为左右的span定义点击事件
<!-- banner.component.html --> <span (click)="playPre()"></span> <ul [@carousel] = 'state' (@carousel.done)="afterPlay()"> <li *ngFor="let img of bannerPic">{{img.img}}</li> </ul> <span (click)="playNext()"></span>
实现点击播放下一张 palyNext() 函数
// banner.component.ts playNext(): void { this.state = this.state === 'stay' ? 'moveLeft' : 'stay'; }
接下来要实现点击播放上一张的效果 playPreI()
首先,是基础的状态切换
// banner.component.ts playPre(): void { this.state = 'moveRight'; }
到这我们会发现点击后由于之前定义的动画事件会影响到这个效果。我们查看下一张的时候 是将第一张图片插入到最后,而查看上一张的时候恰恰相反。所以这个时候我们要修改之前定义好的 ’afterPlay()‘ 函数:
首先判断当前的状态是否是查看下一张时候的状态 ’ stay ‘ || ’ moveLeft ‘
当状态为 ’ moveRight ‘ 的时候 我们要将最后一条数据插入第一条,并且将状态定义为 stayR 避免下次动画结束后判断的时候又进入到第一条数据插到最后的情况
// banner.component.ts afterPlay(): void { if (this.state === 'stay' || this.state === 'moveLeft') { this.bannerPic.push(this.bannerPic[0]); this.bannerPic.shift(); } else if (this.state === 'moveRight') { const last = this.bannerPic.length - 1; this.bannerPic.unshift(this.bannerPic[last]); this.bannerPic.pop(); this.state = 'stayR'; } }
至此,整个效果就完成了。如果有大神有更好的方式欢迎在评论区留言!(≧▽≦)/
完整代码:
// banner.component.ts import { Component, OnInit } from '@angular/core'; import { trigger, state, animate, transition, style } from '@angular/animations'; @Component({ selector: 'app-banner', templateUrl: './banner.component.html', styleUrls: ['./banner.component.css'], animations: [ trigger('carousel', [ state('stay', style({ marginLeft: '-80vw' })), state('stayR', style({ marginLeft: '-80vw' })), state('moveLeft', style({ marginLeft: '-80vw' })), state('moveRight', style({ marginLeft: '-80vw' })), transition('* => moveLeft', animate('500ms ease-in-out', style({ marginLeft: '-160vw' }))), transition('* => stay', animate('500ms ease-in-out', style({ marginLeft: '-160vw' }))), transition('* => moveRight', animate('500ms ease-in-out', style({ marginLeft: '0vw' }))) ]) ] }) export class BannerComponent implements OnInit { bannerPic: any[] = [ {img: '5'}, {img: '1'}, {img: '2'}, {img: '3'}, {img: '4'} ]; state = 'stay'; timer: any; constructor() { } autoPlay(): void { const me = this; this.timer = setInterval(() => { me.state = me.state === 'stay' ? 'moveLeft' : 'stay'; }, 3000); } ngOnInit() { this.autoPlay(); } afterPlay(): void { if (this.state === 'stay' || this.state === 'moveLeft') { this.bannerPic.push(this.bannerPic[0]); this.bannerPic.shift(); } else if (this.state === 'moveRight') { const last = this.bannerPic.length - 1; this.bannerPic.unshift(this.bannerPic[last]); this.bannerPic.pop(); this.state = 'stayR'; } } stopPlay(): void { clearInterval(this.timer); } playPre(): void { this.state = 'moveRight'; } playNext(): void { this.state = this.state === 'stay' ? 'moveLeft' : 'stay'; } }
<!-- banner.component.html --> <div class="show" (mouseenter)="stopPlay()" (mouseleave)="autoPlay()"> <span (click)="playPre()"></span> <ul [@carousel] = 'state' (@carousel.done)="afterPlay()"> <li *ngFor="let img of bannerPic">{{img.img}}</li> </ul> <span (click)="playNext()"></span> </div>