Angular我爱编程

Angular5开发移动——App过场动画

2018-03-16  本文已影响255人  小玉1991

众所周知,Angular是目前比较火的一个前端框架。与React和Vue基本齐名。
因工作需要,要开发一款hybired的混合式app。公司将此大任交付与我,因是安卓和java出身,经过几天的对比,决定用Angular。因为typescript语言与java很接近,容易上手。

打开子页面,父级页面左滑退出,子页面右滑进入。退出时相反。

就是移动端常见的过场动画。打开子页面,父级页面左滑退出,子页面右滑进入。退出时相反。

通过几天的研究,发现Angular5的动画可以自定义动画状态,但在路由切换前后,dom元素要卸载和挂载,会重新走生命周期。并且,dom元素(界面)在没挂载前,和卸载后的状态都只能用void这个特殊状态表示。
Angular5的动画基础知识参见 https://www.angular.cn/guide/animations

受上边基础教程中“范例:从不同的状态下进场和离场”启发,我本来是想自定义一个boolen或者状态字符标志,通过

进入方式两种(一个是第一次的时候左边进,子页面退出的时候,右边进)
void-->active
void -->inactive

退出方式两种(同上类似)
active -->void
inactive -->void

而改变状态的方法,是通过路由传值。事实证明这种方案行不通。因为路由传值,在构造方法中获得传过来的值的时候,页面已经加载完毕,动画也完了,所以没效果。

最后我的思路是通过路由的钩子方法,在页面打开前,静态注入状态值的。

当然也是参考互联网上的代码。详见 https://embed.plnkr.co/cOeDfXCetaYuXFaZ7WeO/

我自己的Demo已经上传,地址详见:https://gitee.com/null_555_2102/Angular5_AppDemo
上我的代码吧!

//动画文件horizontalslideanim.ts 注释掉的是原版的angular2的代码,不适合angular5
import {animate, state, style, transition, trigger} from '@angular/animations';

// const statesSlidedIn = [
//   state('fromLeft' , style({})),
//   state('fromRight' , style({}))
// ];
// const styleSlidedLeft = style({transform: 'translateX(-100%)', display: 'none'});
// const styleSlidedRight = style({transform: 'translateX(100%)', display: 'none'});
// const stateSlidedLeft = state('left', styleSlidedLeft);
// const stateSlidedRight = state('right', styleSlidedRight);
// const transitionsSlideLeft = [
//   transition('fromLeft => void', animate('.3s ease-out', styleSlidedRight)),
//   transition('void => fromLeft', [styleSlidedLeft, animate('.3s ease-out')])
// ];
// const transitionsSlideRight = [
//   transition('fromRight => void', animate('.3s ease-out', styleSlidedLeft)),
//   transition('void => fromRight', [styleSlidedRight, animate('.3s ease-out')])
// ];
// export const slideHorizontal = trigger('slideHorizontal', [
//     ...statesSlidedIn,
//   stateSlidedLeft,
//   stateSlidedRight,
//   ...transitionsSlideLeft,
//   ...transitionsSlideRight
// ]);

export const slideHorizontal = trigger('slideHorizontal', [

// * 表示任何状态
  state('*', style({ position: 'fixed', 'width': '100%', 'height': '100%' })),
  // 定义void表示空状态下
  state('void', style({ position: 'fixed', 'width': '100%', 'height': '100%' })),

  state('fromLeft', style({
    position: 'fixed', 'width': '100%', 'height': '100%',
    })),

  state('fromRight', style({
    position: 'fixed', 'width': '100%', 'height': '100%',
    })),


  transition('void => fromLeft', [
    style({transform: 'translate3d(-100%,0,0) '}),
    animate('0.5s ease-in-out',style({transform: 'translate3d(0,0,0)'}))
  ]),


  transition('fromLeft => void', [
    style({transform: 'translate3d(0,0,0) '}),
    animate('0.5s ease-in-out',style({transform: 'translate3d(100%,0,0)'}))
  ]),

  transition('void => fromRight', [
    style({transform: 'translate3d(100%,0,0) '}),
    animate('0.5s ease-in-out')
  ]),

  transition('fromRight => void', [
    style({transform: 'translate3d(0,0,0)'}),
    animate('0.5s ease-in-out', style({transform: 'translate3d(-100%,0,0)'}))
  ])
]);

路由文件

export const appRoutes=[

   {
       path:'home',
   component:HomepageComponent,
   data: {
     slideIndex: 1,
   },
   canDeactivate: [CanDeactivateAfterChangeDetectionGuard]
   },


   {
       path:'cutdowntime',
   component:CutdowntimeComponent,
   data: {
     slideIndex: 2,
   },
   canDeactivate: [CanDeactivateAfterChangeDetectionGuard]
   },
 {
   path:'calc',
   component:CalculatorpageComponent,
   data: {
     slideIndex: 2,
   },
   canDeactivate: [CanDeactivateAfterChangeDetectionGuard]
 },

 {
   path:'help',
   component:HelppageComponent,
   data: {
     slideIndex: 3,
   },
   canDeactivate: [CanDeactivateAfterChangeDetectionGuard]
 },
   {
   path:'**',//fallback router must in the last
   redirectTo:'home'
   // loadChildren:'./page/home.module#HomeModule'

   }
];
@NgModule({
 imports: [RouterModule.forRoot(appRoutes)],
 exports: [RouterModule]
})
export class AppRoutingModule { }

不要嫌麻烦,这个功能就是不好实现,继续

两个service

import { Injectable } from '@angular/core';
import {WaitForChangeDetection} from './routeslidedirectionservice.service';
import {CanDeactivate} from '@angular/router';

@Injectable()
export class CanDeactivateAfterChangeDetectionGuard implements CanDeactivate<WaitForChangeDetection> {
  canDeactivate(component: WaitForChangeDetection): Promise<boolean> {
    return component.waitForChangeDetection();
  }
}
//分割线,两个文件
import { Injectable } from '@angular/core';

@Injectable()
export class RouteSlideDirectionService {

  constructor() { }

  direction: string;

  setDirection(direction: string) {
    this.direction = direction;
  }
  getDirection(): string {
    return this.direction;
  }

}
export declare abstract class WaitForChangeDetection {
  abstract waitForChangeDetection(): Promise<boolean>;
}

两个父类

import {ChangeDetectorRef, Component, HostBinding} from '@angular/core';
import {RouteSlideDirectionService} from '../../service/routeslidedirectionservice.service';
import {WaitForChangeDetectionImpl} from '../waitforchangedetectionimpl/waitforchangedetectionimpl.component';

@Component({
  selector: 'slidable',
  templateUrl: './slidable.html',
  styleUrls: ['./slidable.css']
})
export class Slidable extends WaitForChangeDetectionImpl {
  constructor(protected cdRef: ChangeDetectorRef, private routeSlideDirectionService: RouteSlideDirectionService){
    super(cdRef);
  }

  @HostBinding('@slideHorizontal')
  get slideHorizontal(){
    let slideDirection = this.routeSlideDirectionService.getDirection();

    if(slideDirection){
      slideDirection = (slideDirection==="right"?"fromRight" :"fromLeft");
      console.log("slideDirection:"+slideDirection);
      return slideDirection;
    }
    return null;
  }
}
//分割线  两个文件
import {AfterViewChecked, ChangeDetectorRef, Component} from '@angular/core';
import {WaitForChangeDetection} from '../../service/routeslidedirectionservice.service';
import {Subject} from 'rxjs/Subject';

@Component({
  selector: 'wait',
  templateUrl: './wait.html',
  styleUrls: ['./wait.css']
})
export class WaitForChangeDetectionImpl implements AfterViewChecked, WaitForChangeDetection {
  constructor(protected cdRef: ChangeDetectorRef){
    this.viewChecked$ = new Subject<void>();
  }

  viewChecked$: Subject<void>;

  waitForChangeDetection(): Promise<boolean>{
    this.cdRef.detectChanges();
    return new Promise((resolve) => this.viewChecked$.subscribe(() => resolve(true)));
  }

  ngAfterViewChecked(){
    this.viewChecked$.next();
  }
}

在module和app.component中的配置

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';

import {CutDowntimeviewViewComponent} from './component/cutdowntimeview/app.cutdowntimeview';
import {TabviewComponent} from './component/tabview/tabview.component';
import {DrawerviewComponent} from './component/drawerview/drawerview.component';
import {HomepageComponent} from './page/homepage/homepage.component';
import {CutdowntimeComponent} from './page/cutdowntime/cutdowntime.component';
import {appRoutes, AppRoutingModule} from './app.routes';
import {AppComponent} from './app.component';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {HeaderComponent} from './component/header/header.component';
import {CalculatorpageComponent} from './page/calculatorpage/calculatorpage.component';
import {FeedbackpageComponent} from './page/feedbackpage/feedbackpage.component';
import {HelppageComponent} from './page/helppage/helppage.component';
import { WaitForChangeDetectionImpl } from './component/waitforchangedetectionimpl/waitforchangedetectionimpl.component';
import { Slidable } from './component/slidable/slidable.component';
import {RouterModule} from '@angular/router';
import {CanDeactivateAfterChangeDetectionGuard} from './service/candeactivateafterchangedetectionguard.service';
import {RouteSlideDirectionService} from './service/routeslidedirectionservice.service';

@NgModule({
 declarations: [
   AppComponent,
   CutDowntimeviewViewComponent,
   TabviewComponent,
   DrawerviewComponent,
   HomepageComponent,
   CutdowntimeComponent,
   HeaderComponent,
   CalculatorpageComponent,
   FeedbackpageComponent,
   HelppageComponent,
   WaitForChangeDetectionImpl,
   Slidable
 ],
 imports: [
   BrowserModule,
   AppRoutingModule,
   BrowserAnimationsModule,// 引入动画模块
   RouterModule.forRoot(appRoutes),
 ],
 providers: [
   RouteSlideDirectionService,
   CanDeactivateAfterChangeDetectionGuard
 ],
 bootstrap: [AppComponent]
})
export class AppModule {

}
//分割线

import {Component} from '@angular/core';
import {RouteSlideDirectionService} from './service/routeslidedirectionservice.service';
import {ActivatedRoute, Router, RoutesRecognized} from '@angular/router';
import * as _ from 'lodash';


@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.css'],
})
export class AppComponent {
 constructor(private router: Router, private route: ActivatedRoute, private routeSlideDirectionService: RouteSlideDirectionService){
   this.router.events.subscribe((event) => {
     if (event instanceof RoutesRecognized) {
       let leavingSlideIndex = _.get(event, 'state.root.firstChild.data.slideIndex');
       let enteringSlideIndex = _.get(this.route, 'snapshot.firstChild.data.slideIndex');

       // console.log("event: "+event);
       //
       // console.log("leavingSlideIndex :"+leavingSlideIndex);
       // console.log("enteringSlideIndex :"+enteringSlideIndex);


       if(leavingSlideIndex && enteringSlideIndex){
         this.routeSlideDirectionService.setDirection(leavingSlideIndex > enteringSlideIndex ? 'right' : 'left');
       } else {
         this.routeSlideDirectionService.setDirection(null);
       }
     }
   });
 }
}


最后,在两个页面上,这样用

//页面1
import {Component, HostBinding, OnInit} from '@angular/core';
import {slideHorizontal} from '../../animates/horizontalslideanim';
import {Slidable} from '../../component/slidable/slidable.component';

@Component({
  selector: 'app-homepage',
  templateUrl: './homepage.component.html',
  styleUrls: ['./homepage.component.css'],
  animations:[
    slideHorizontal
  ]

})
export class HomepageComponent extends Slidable{

}
//页面1 的html
<div class="content">
  <div class="content-padded grid-demo">

    <div class="row" style="margin-top: 1rem;">
      <a class="col-33 box" routerLink="/cutdowntime"><img src="../../../favicon.ico"/></a>
      <div class="col-33 box">33%</div>
      <div class="col-33 box">33%</div>
    </div>
    <div class="row" style="margin-top: 1rem;">
      <a class="col-33 box" routerLink="/calc"><img src="../../../favicon.ico"/></a>
      <div class="col-33 box">33%</div>
      <div class="col-33 box">33%</div>
    </div>
    <div class="row" style="margin-top: 1rem;">
      <div class="col-33 box"><img src="../../../favicon.ico"/></div>
      <div class="col-33 box">33%</div>

    </div>
  </div>

</div>


//页面2

@Component({
  selector: 'app-calculatorpage',
  templateUrl: './calculatorpage.component.html',
  styleUrls: ['./calculatorpage.component.css'],
  animations: [
    slideHorizontal
  ]
})
export class CalculatorpageComponent  extends Slidable{


  mTitle:string="计算器";
  mHref:string="/home";

}
//页面2 的html
<div class="content native-scroll">

  <app-header [mTitle]="mTitle" [mHref]="mHref"></app-header>
  <div class="content-block">


    <a routerLink="/help">打开子页面</a>
  </div>
</div>

最后,把项目地址,贴到这里,大家可以参考一下。https://gitee.com/null_555_2102/Angular5_AppDemo

上一篇下一篇

猜你喜欢

热点阅读