8.7 版本发布—WinterCG 兼容性第一部分
了解更多

NativeScript 提供了一个简单而强大的 API,允许您为应用程序中的几乎所有原生组件创建动画。单个 API 允许您以相同的方式为 iOS 和 Android 创建动画。您始终可以选择在需要时直接使用平台动画,但让我们看看这个简单的 API 涵盖面有多广,因为它非常适合大多数常见的动画需求。

为了方便起见,有两种常用的创建动画的方式

  • 声明式 - 简单易用的 CSS3 动画 API
  • 命令式 - 通过直接使用代码调用动画方法来完全控制任何动画

声明式:使用 CSS 为 UI 组件创建动画

CSS 动画基于 CSS3 动画 API 标准。您可以使用它们为几乎所有原生视图创建动画,而无需了解 JavaScript。您可以使用多个帧并更改动画方向。使用 CSS 动画,您可以在需要时将动画代码与应用程序逻辑分离。

CSS 动画由两个部分组成:描述 CSS 动画的样式和一组关键帧,这些关键帧指示动画样式的起始和结束状态,以及可能的中间途径。

如下所示,使用 @keyframes 在 CSS 中添加动画

css
@keyframes example {
  from {
    background-color: red;
  }
  to {
    background-color: green;
  }
}

.example {
  animation-name: example;
  animation-duration: 4s;
  animation-fill-mode: forwards;
}

动画在具有 .example 类的组件加载后立即开始。

要稍后触发它,请将组件的 className 属性设置为 .view,您希望动画从这里开始

typescript
button.className = 'example'
xml
<Button id="myButton" text="{N}" />

注意

如果未指定 animation-duration 属性,则动画将使用默认值 - 0.3 秒。

基本的 @keyframes 规则有两个途径。from 属性表示动画时间的 0%(开始),to 表示 100%(最终值)。

css
@keyframes example {
  from {
    background-color: red;
  }
  to {
    background-color: green;
  }
}

您可以使用百分比(%)添加更多途径。

css
@keyframes example {
  0% {
    background-color: red;
    transform: translate(0, 0);
  }
  25% {
    background-color: yellow;
    transform: translate(200, 0);
  }
  50% {
    background-color: blue;
    transform: translate(200, 200);
  }
  75% {
    background-color: green;
    transform: translate(0, 200);
  }
  100% {
    background-color: red;
    transform: translate(0, 0);
  }
}

可动画属性

NativeScript 中的 CSS 动画支持与基于代码的动画中使用的相同可动画属性

  • 不透明度
  • background-color:对应于 backgroundColor
  • transform: translate:对应于 translateXtranslateY 属性。
  • transform: scale:对应于 scaleXscaleY 属性。
  • transform: rotate:对应于 rotate 属性。

注意

您不能在缩放和平移中设置单个 x 或 y 字段。如果您只在平移中设置 x,则 y 将假定为 0;如果您只在缩放中设置 y,则 x 将假定为 1。

CSS 动画属性

CSS 动画是通过使用 animation 属性及其以下列表中的子属性来定义的。实际的动画外观使用 @keyframes 规则定义。

  • animation-name:指定应使用的 @keyframes 规则的名称。
  • animation-delay:指定应用样式和动画开始之间的时间。
  • animation-duration:动画的长度(以秒为单位)。
  • animation-iteration-count:指定应播放动画的次数。默认为 1。要无限重复动画,请使用 infinite。
  • animation-timing-function:通过建立加速度曲线来定义动画如何通过关键帧过渡。
  • animation-fill-mode:配置动画在结束时应用于视图组件的值。
  • animation-direction:配置动画是否应在每次运行序列时交替方向或重置到起点并重复自身。
  • animation:简写属性允许在一行中设置所有动画属性。

设置动画延迟

animation-delay 属性指定动画开始前的延迟(以秒为单位)

css
.view {
  ....
  animation-delay: 2s;
}

设置动画迭代次数

animation-iteration-count 属性定义动画应运行的次数。具有以下值的动画将在停止前播放两次。

css
.view {
  ....
  animation-iteration-count: 2;
}

对于无限动画,请将此属性设置为 infinite

css
animation-iteration-count: infinite;

设置动画的速度曲线

animation-timing-function 属性指定动画的速度曲线。它可以具有以下值之一

  • ease:指定一个开始缓慢、然后变快、然后缓慢结束的动画(这是默认值)。
  • linear:指定从头到尾速度相同的动画。
  • ease-in:指定一个开始缓慢的动画。
  • ease-out:指定一个结束缓慢的动画。
  • ease-in-out:指定一个开始缓慢和结束缓慢的动画。
  • spring:指定一个弹簧动画。
  • cubic-bezier(n,n,n,n):允许您在三次贝塞尔函数中定义自己的值,如下所示
css
.view {
  ...
  animation-timing-function: cubic-bezier(0.1, 0.1, 1, 1);
}

设置动画结束状态

animation-fill-mode 属性确定组件的动画结束样式。其默认值为 none。在这种情况下,所有动画值将重置为动画之前具有的状态。要保留动画值,请设置 forwards

css
.view {
...
  animation-fill-mode: forwards;
}

动画属性简写

要在一行中设置上述所有属性,请使用 animation 属性

css
.view {
  animation: example 4s ease-in-out 2s infinite reverse forwards;
}

属性的顺序如下

animation: name duration timing-function delay iteration-count direction fill-mode;

您可以通过使用逗号在 animation 属性中组合两个动画

css
.view {
  animation: example 4s ease-in-out 2s infinite reverse, second-animation-example
      5s ease-out;
}

伪选择器

Button 组件有一个内置的特殊状态 highlighted,用于 touch 事件。如下所示,为该状态设置动画

css
.button {
  background-color: green;
}

.button:highlighted {
  animation-name: highlight;
  animation-duration: 2s;
  animation-fill-mode: forwards;
}

@keyframes highlight {
  from {
    background-color: yellow;
  }
  to {
    background-color: red;
  }
}

宽度和高度动画

命令式:使用代码为 UI 组件创建动画

为单个 View 创建动画最简单的方法是使用 View.animate 方法,该方法接受一个 AnimationDefinition。它会立即启动并返回其完成的 Promise。

typescript
view.animate({
  translate: { x: 0, y: 100 },
  duration: 1000,
  curve: CoreTypes.AnimationCurve.easeIn,
})

注意

您应该创建一个 Animation 类,以便能够取消动画。

AnimationDefinition 接口

AnimationDefinition 接口定义了以下要动画化的属性

  • target:要动画化其属性的视图。
  • opacity:动画化视图的不透明度。值应为 0.0 到 1.0 之间的数字。
  • backgroundColor:动画化视图的 backgroundColor。
  • translate:动画化视图的平移仿射变换。值应为 { x: number; y: number; }
  • scale:动画化视图的缩放仿射变换。值应为 { x: number; y: number; }
  • rotate:动画化视图的旋转仿射变换。值应为一个数字,以度为单位指定旋转量。
  • duration:动画的长度(以毫秒为单位)。默认持续时间为 300 毫秒。
  • delay:延迟启动动画的时间量(以毫秒为单位)。
  • iterations:指定应播放动画的次数。默认为 1。iOS 动画支持小数迭代,例如 1.5。要无限重复动画,请使用 Number.POSITIVE_INFINITY
  • curve:可选的动画曲线。可能的值包含在 CoreTypes.AnimationCurve 中。或者,您可以传递类型 UIViewAnimationCurve(适用于 iOS)或 android.animation.TimeInterpolator(适用于 Android)的实例。
  • width:动画化视图的宽度。
  • height:动画化视图的高度。

接口的所有成员都是可选的,并具有以下例外情况的默认值

  • 当调用 View 实例的 animate 方法时,target 仅为可选,因为它是自动为您设置的。
  • 您必须至少指定以下一项:opacity、backgroundColor、scale、rotate 或 translate。

Animation 类

Animation 类表示一组一个或多个 AnimationDefinitions,它们可以同时或顺序播放。Animation 类的构造函数接受一个 AnimationDefinitions 数组和一个布尔参数,指示是否按顺序播放动画。创建 Animation 类的实例不会开始动画播放。调用 play 方法以启动动画。

  • play:启动动画并返回其被调用实例的方法,用于流畅的动画链式调用。
  • cancel:停止动画的空方法。
  • finished:一个 Promise,当动画完成时会被 resolved,当动画被取消或因其他原因停止时会被 rejected。
  • isPlaying:一个布尔属性,如果动画当前正在播放则返回 true

从不同组件的原点进行动画

视图具有 originXoriginY 属性。默认情况下,它们的值为 0.5。要创建复杂的旋转动画,您可以更改这些属性。该对表示视图将围绕其进行变换的原点。

View Origin

使用 Promise 链式调用动画

使用 animate 方法返回的 Promise 来链式调用动画。

typescript
view
  .animate({ opacity: 0 })
  .then(() => view.animate({ opacity: 1 }))
  .then(() => view.animate({ translate: { x: 100, y: 100 } }))
  .then(() => view.animate({ translate: { x: 0, y: 0 } }))
  .then(() => view.animate({ scale: { x: 3, y: 3 } }))
  .then(() => view.animate({ scale: { x: 1, y: 1 } }))
  .then(() => view.animate({ rotate: 180 }))
  .then(() => view.animate({ rotate: 0 }))
  .then(() => {
    console.log('Animation finished')
  })
  .catch((e) => {
    console.log(e.message)
  })

从代码中访问 CSS 动画

要触发在 CSS 中定义的动画,您可以更改视图的 className 属性。

ts
const view = page.getViewById('view')
view.className = 'transparent'

要访问在 CSS 中定义的 @keyframes,请使用 Page 视图的 getKeyframeAnimationWithName 方法。这允许进一步自定义动画属性。

ts
import { KeyframeAnimation, View } from '@nativescript/core'

const view = page.getViewById('view') as View
const animationInfo = page.getKeyframeAnimationWithName('bounce')
animationInfo.duration = 2000

const animation = KeyframeAnimation.keyframeAnimationFromInfo(animationInfo)
animation.play(view).then(() => {
  console.log('Played with code!')
})

TouchManager 8.2+

TouchManager 为您的应用程序交互提供了几个便利。

为所有点击绑定自动动画化触摸按下/抬起

例如,您可以通过在应用程序引导之前(通常在 app.tsmain.ts 中)启用 TouchManager.enableGlobalTapAnimations,轻松快速地在每个具有 tap 事件绑定的视图上启用一致的触摸按下/抬起动画。

ts
import { TouchManager } from '@nativescript/core'

TouchManager.enableGlobalTapAnimations = true
TouchManager.animations = {
  down: {
    scale: { x: 0.95, y: 0.95 },
    duration: 200,
    curve: CoreTypes.AnimationCurve.easeInOut,
  },
  up: {
    scale: { x: 1, y: 1 },
    duration: 200,
    curve: CoreTypes.AnimationCurve.easeInOut,
  },
}

// bootstrap the app...

这将自动为任何具有 tap 绑定的视图在触摸按下和抬起时使用这些特定的动画进行动画化。

如果您有一些需要忽略的“可点击”视图。

xml
<Button text="Global tap animations simply ignored" ignoreTouchAnimation="true" />

除了表达 NativeScript 动画 API(它们方便、简单易用)之外,您还可以定义纯原生动画,例如 iOS UIView 动画 或甚至 Android 动态弹簧物理动画,例如。

ts
touchAnimation = {
  down(view: View) {
    if (global.isIOS) {
      UIView.animateWithDurationAnimations(0.25, () => {
        view.ios.transform = CGAffineTransformMakeScale(0.95, 0.95)
      })
    } else if (global.isAndroid) {
      const lib = androidx.dynamicanimation.animation
      const spring = new lib.SpringForce(0.95)
        .setDampingRatio(lib.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
        .setStiffness(lib.SpringForce.STIFFNESS_MEDIUM)
      let animation = new lib.SpringAnimation(
        view.android,
        lib.DynamicAnimation().SCALE_X,
        float(0.95)
      )
      animation.setSpring(spring).setStartVelocity(0.7).setStartValue(1.0)
      animation.start()
      animation = new lib.SpringAnimation(
        view.android,
        lib.DynamicAnimation().SCALE_Y,
        float(0.95)
      )
      animation.setSpring(spring).setStartVelocity(0.7).setStartValue(1.0)
      animation.start()
    }
  },
  up(view: View) {
    if (global.isIOS) {
      UIView.animateWithDurationAnimations(0.25, () => {
        view.ios.transform = CGAffineTransformIdentity
      })
    } else if (global.isAndroid) {
      const lib = androidx.dynamicanimation.animation
      const spring = new lib.SpringForce(1)
        .setDampingRatio(lib.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
        .setStiffness(lib.SpringForce.STIFFNESS_MEDIUM)
      let animation = new lib.SpringAnimation(
        view.android,
        lib.DynamicAnimation().SCALE_X,
        float(1)
      )
      animation.setSpring(spring).setStartVelocity(0.7).setStartValue(0.95)
      animation.start()
      animation = new lib.SpringAnimation(
        view.android,
        lib.DynamicAnimation().SCALE_Y,
        float(1)
      )
      animation.setSpring(spring).setStartVelocity(0.7).setStartValue(0.95)
      animation.start()
    }
  },
}

touchAnimation 和 ignoreTouchAnimation

您还可以通过指定其自己的 touchAnimation 属性,在任何特定视图上声明性地定义自定义触摸动画(*这将覆盖任何全局 TouchManager 设置*)。

xml
<Button touchAnimation="{{ touchAnimation }}" />

这将使用以下视图绑定设置对触摸按下和抬起进行动画化。

ts
touchAnimation = {
  down: {
    scale: { x: 0.95, y: 0.95 },
    backgroundColor: new Color('yellow'),
    duration: 250,
    curve: CoreTypes.AnimationCurve.easeInOut,
  },
  up: {
    scale: { x: 1, y: 1 },
    backgroundColor: new Color('#63cdff'),
    duration: 250,
    curve: CoreTypes.AnimationCurve.easeInOut,
  },
}

当使用 TouchManager.enableGlobalTapAnimations 时,您可以声明任何需要忽略的视图,以防某些视图需要从全局动画设置中排除。

xml
<Button text="Global tap animations simply ignored" ignoreTouchAnimation="true" />

限制

  • 无法对 SpanFormattedString 进行动画化。SpanFormattedString 组件没有扩展 View 类,而仅扩展 ViewBase。因此,SpanFormattedString 都不是 UI 组件,这使得无法对其进行动画化,并在 iOS 上导致崩溃。
下一页
常用 API