返回博客首页
← 所有文章

使用 Angular 10 和新的启动选项为应用启动动画增添创意

2020年7月22日 — 作者:William Juan 和 Nathan Walker

除了Angular 10 支持之外,我们还在 NativeScript 中引入了对 APP_INITIALIZER 的正确支持,以及我们想要在此展示的新启动选项。新选项包括:asynclaunchViewbackgroundColor,这些选项可以在引导应用程序时指定。

首先让我们回顾一下如何使用 Angular 的 APP_INITIALIZER 在应用程序启动期间处理异步任务。

APP_INITIALIZER 和 async 启动选项

// can be used in any app.module:
import { NgModule, APP_INITIALIZER } from '@angular/core';

export function asyncBoot(): Function {
  return (): Promise<any> => new Promise(resolve => {
    setTimeout(() => {
      resolve();
    }, 5000);
  })
}

@NgModule({
  imports: [NativeScriptModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent],
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: asyncBoot,
      multi: true
    },
  ]
})
export class AppModule {}

// when using async app initialization, be sure to set the `async` boot option in `main.ts` like this:
platformNativeScriptDynamic({
  async: true
}).bootstrapModule(AppModule);

例如,如果需要,可以在 asyncBoot 工厂函数中注入 HttpClient 来处理各种 API 调用,然后再引导应用程序。

创意启动动画

LaunchView

您可以使用标准的 NativeScript API 创建多种动画启动屏幕,但以下是动画 GIF 中看到的精确动画启动屏幕以及如何使用新的 launchView 启动选项。

import {
  platformNativeScriptDynamic,
  AppLaunchView,
} from "@nativescript/angular";
import { GridLayout } from "@nativescript/core";
import { AppModule } from "./app/app.module";

class LaunchAnimation extends GridLayout implements AppLaunchView {
  circle: GridLayout;
  finished = false;
  complete: () => void;

  constructor() {
    super();
    this.backgroundColor = "#4caef7";
    this.className = "w-full h-full";

    // construct any creative animation
    this.circle = new GridLayout();
    this.circle.width = 30;
    this.circle.height = 30;
    this.circle.borderRadius = 15;
    this.circle.horizontalAlignment = "center";
    this.circle.verticalAlignment = "middle";
    this.circle.backgroundColor = "#fff";

    this.addChild(this.circle);
  }

  async startAnimation() {
    await this.circle.animate({
      scale: { x: 2, y: 2 },
      duration: 800,
    });

    await this.circle.animate({
      scale: { x: 1, y: 1 },
      duration: 800,
    });

    if (this.finished) {
      await this.circle.animate({
        scale: { x: 30, y: 30 },
        duration: 400,
      });
      this.fadeOut();
    } else {
      // keep looping
      this.startAnimation();
    }
  }

  cleanup() {
    return new Promise((resolve) => {
      this.complete = resolve;
      this.finished = true;
    });
  }

  async fadeOut() {
    await this.animate({
      opacity: 0,
      duration: 400,
    });
    this.complete();
  }
}

platformNativeScriptDynamic({
  launchView: new LaunchAnimation()
}).bootstrapModule(AppModule);

自定义 backgroundColor 在仅使用 async 时很有用

如果您一直希望在应用程序首次启动时更好地控制其背景颜色,则可以像这样设置 backgroundColor

platformNativeScriptDynamic({
  backgroundColor: 'purple'
}).bootstrapModule(AppModule);

这在与异步 APP_INITIALIZER 一起使用时特别有用,因为您可能不希望用户在应用程序异步准备启动时停留在空白白屏上(这可能在启动屏幕消失后发生,并且当使用 Angular 的 APP_INITIALIZER 令牌进行异步处理时,您的应用程序可能仍在异步初始化)。

更进一步!使用 Lottie 美化启动屏幕

新的 launchView 选项还可以与Lottie结合使用,从而为动画提供更大的灵活性。如果您之前从未听说过 Lottie,Lottie 是 Airbnb 创建的一个动画库,它允许您使用从 Adobe After Effects 导出的 JSON 文件创建精美的动画。我们可以使用 Brad Martin 的 nativescript-lottie 插件在我们的 Nativescript 应用程序中使用它。该插件还有一些示例效果,我将在本文中使用它们。这里 是一个链接,如果您想尝试一下。

Lottie LaunchView for Angular

Lottie 设置

我们首先需要使用以下命令将 nativescript-lottie 插件添加到我们的项目中,并在我们将添加动画代码的 main.ts 中导入它。

ns plugin add nativescript-lottie

启动动画设置

我们将在此扩展 GridLayout,以便我们可以轻松地分层和定位元素。首先让我们为我们将要使用的每个方法创建一个存根,以及每个方法将执行的操作。

class LaunchAnimation extends GridLayout implements AppLaunchView {

  constructor() {
    super();
    // define our layout, containers, and UI elements
  }

  startAnimation(): void {
    // start and stop animation
  }

  cleanup(): Promise<any> {
    // set flag to stop animation
    // returns a promise to provide further control over resolving when your animation is completely finished
  }

}

定义动画

LottieViewsrc 属性指向一个 lottie json 文件,该文件位于 iOS 的 app/App_Resources/iOS 和 Android 的 app/App_Resources/Android/src/main/assets 中。

import { LottieView } from 'nativescript-lottie';

class LaunchAnimation extends GridLayout implements AppLaunchView {
  lottieView: LottieView;
  complete: () => void;

  constructor() {
    super();
    this.backgroundColor = '#4caef7';
    this.className = 'w-full h-full';

    // setup lottie
    this.lottieView = new LottieView();
    this.lottieView.height = 300;
    this.lottieView.width = 300;
    this.lottieView.src = 'lottie/pinjump.json';

    // add lottieview to container
    this.addChild(this.lottieView);
  }
}

启动和停止动画

如前所述,我们将使用 startAnimation() 方法播放动画,并使用 cleanup() 方法触发动画停止。我们还将添加一个 fadeOut() 方法来从动画过渡到应用程序的第一页。

为了优雅地结束动画,我们将添加一个 finished 标志,当调用 cleanup() 时,该标志将设置为 true,表示动画在完成当前循环后停止。然后,此标志在我们的 startAnimation() 方法中使用,在该方法中,我们让 lottieView.completionBlock 检查此标志,如果 finished 为假,则再次调用 startAnimation(),重新播放动画,或者如果 finished 为真,则调用 fadeOut(),结束动画,然后淡出 LaunchAnimation 并过渡到应用程序的主页。

class LaunchAnimation extends GridLayout implements AppLaunchView {
  lottieView: LottieView;
  finished = false;
  complete: () => void;

  constructor() {
    super();
    this.backgroundColor = '#4caef7';
    this.className = 'w-full h-full';

    // setup lottie
    this.lottieView = new LottieView();
    this.lottieView.height = 300;
    this.lottieView.width = 300;
    this.lottieView.src = 'lottie/pinjump.json';

    // add lottieview to container
    this.addChild(this.lottieView);
  }

  startAnimation() {
    this.lottieView.completionBlock = () => this.finished ? this.fadeOut() : this.startAnimation();
    this.lottieView.playAnimation();
  }

  cleanup() {
    return new Promise(resolve => {
      this.complete = resolve;
      this.finished = true;
    });
  }

  async fadeOut() {
    await this.animate({
      opacity: 0,
      duration: 400,
    });

    this.complete();
  }
}

现在我们已经设置了 LaunchAnimation,可以在引导时定义我们的 launchView 选项。

platformNativeScriptDynamic({
  launchView: new LaunchAnimation(),
}).bootstrapModule(AppModule);