美文网首页Angular我爱编程
Angular5开发移动——App过场动画

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

作者: 小玉1991 | 来源:发表于2018-03-16 18:29 被阅读255次

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

    • Angular 1.x 我习惯称为AngularJs
    • Angular 2.x 3 4.x 5.x 我习惯称为Angular。
      两者可以看做两种框架学习。Angular5有自己想项目结构,用cli来管理项目。
      具体基本知识,不在此文的探讨中,本文主要阐述Angular5的过场动画
    打开子页面,父级页面左滑退出,子页面右滑进入。退出时相反。

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

    通过几天的研究,发现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

    相关文章

      网友评论

        本文标题:Angular5开发移动——App过场动画

        本文链接:https://www.haomeiwen.com/subject/yasrqftx.html