Skip to content

转场动画

ArkUI 开发框架提供了转场动画,可以划分为页面转场动画、组件转场动画和共享元素转场动画,本节将分别介绍偶一下他们的使用。

页面转场动画

页面转场动画是指页面在打开或者关闭时添加的动画效果,它是通过在全局 pageTransition 方法内配置页面入场组件 PageTransitionEnter 和页面退场组件 PageTransitionExit 来自定义页面转场动效。

页面入场和退场定义介绍

bash

// 入场动效接口定义
interface PageTransitionEnterInterface extends CommonTransition<PageTransitionEnterInterface> {
  (value: { type?: RouteType; duration?: number; curve?: Curve | string; delay?: number }): PageTransitionEnterInterface;
  onEnter(event: (type?: RouteType, progress?: number) => void): PageTransitionEnterInterface;
}

// 退场动效接口定义
interface PageTransitionExitInterface extends CommonTransition<PageTransitionExitInterface> {
  (value: { type?: RouteType; duration?: number; curve?: Curve | string; delay?: number }): PageTransitionExitInterface;
  onExit(event: (type?: RouteType, progress?: number) => void): PageTransitionExitInterface;
}

type:

设置页面的路由类型,RouteType 说明如下:

  1. None:没有样式
  2. Push:PageA 跳转到 PageB 时,PageA 为 Exit + Push,PageB 为 Enter + Push。
  3. Pop:PageB 返回至 PageA 时,PageA 为 Enter + Pop, PageB 为 Exit + Pop。

duration:

设置动画执行时间,单位毫秒,默认为 0 。

curve:

动画曲线,默认值为 Linear 。

onEnter:

页面入场时的事件回调,其中 progress 取值范围为:[0 ~ 1]。

onExit:

页面入场时的事件回调,其中 progress 取值范围为:[0 ~ 1]。

页面间转场动画属性介绍

入场动效 PageTransitionEnter 和退场动效 PageTransitionExit 都间接继承自 CommonTransitionCommonTransition 定义的属性方法如下所示:

bash

declare class CommonTransition<T> {
  slide(value: SlideEffect): T;
  translate(value: { x?: number | string; y?: number | string; z?: number | string }): T;
  scale(value: { x?: number; y?: number; z?: number; centerX?: number | string; centerY?: number | string }): T;
  opacity(value: number): T;
}

slide:

设置页面入场或者退场的方向效果,SlideEffect 说明如下:

  1. Left:设置到入场时表示从左边滑入,出场时表示滑出到左边。

  2. Right:设置到入场时表示从右边滑入,出场时表示滑出到右边。

  3. Top:设置到入场时表示从上边滑入,出场时表示滑出到上边。

  4. Bottom:设置到入场时表示从下边滑入,出场时表示滑出到下边。

translate:

设置页面转场时的平移效果,为入场时起点和退场时终点的值,和 slide 同时设置时默认生效 slide。

scale:

设置页面转场时的缩放效果,为入场时起点和退场时终点的值。

opacity:

设置入场的起点透明度值或者退场的终点透明度值。

简单样例如下所示:

bash

// 第一个页面
@Entry @Component struct Index {

  @State scale: number = 1;                     // 默认放缩比例
  @State opacity: number = 1;                   // 默认不透明度

  build() {
    Stack() {
      Image("pages/test.jpeg")
        .size({width: "100%", height: '100%'})
        .onClick(() => {
          router.push({                         // 使用router打开第二个页面
            url: "pages/second"
          })
        })
      Text('第一个页面')
        .fontSize(35)
        .fontColor(Color.Red)
    }
    .size({width: "100%", height: '100%'})
    .scale({
      x: this.scale,                            // 设置当前页面X轴方向上的放缩比
      y: this.scale                             // 设置当前页面Y轴方向上的放缩比
    })
    .opacity(this.opacity)                      // 设置当前页面不透明度
  }

  pageTransition() {                            // 添加全局pageTransition方法
    PageTransitionEnter({                       // 设置当前页面入场动画的配置信息
      duration: 1200,                           // 动画执行时间
      curve: Curve.Linear                       // 设置动画曲线
    })
    .onEnter((type?: RouteType, progress?: number) => {// 每一帧的动画事件回调
      this.scale = progress;
      this.opacity = progress;
      console.log("enter routeType: " + type)
    })

    PageTransitionExit({                        // 设置当前页面退场动画的配置信息
      duration: 1500,                           // 设置动画的执行时间
      curve: Curve.Ease                         // 设置动画曲线
    })
    .onExit((type?: RouteType, progress?: number) => {// 每一帧的动画事件回调
      console.log("exit routeType: " + type)
      this.scale = 1 - progress;
      this.opacity = 1;
    })
  }
}

// 第二个页面
@Entry @Component struct Second {

  @State scale: number = 1
  @State opacity: number = 1

  build() {
    Stack() {
      Navigator({target: "", type: NavigationType.Back}) {
        Image("pages/icon_test.jpeg")
          .size({width: "100%", height: '100%'})
      }
      Text('第二个页面')
        .fontSize(35)
        .fontColor(Color.Red)
    }
    .size({width: '100%', height: '100%'})
    .scale({
      x: this.scale
    })
    .opacity(this.opacity)
  }

  pageTransition() {
    PageTransitionEnter({
      duration: 1200,
      curve: Curve.Linear
    })
    .onEnter((type: RouteType, progress: number) => {
      this.scale = 1
      this.opacity = progress
    })

    PageTransitionExit({
      duration: 1500,
      curve: Curve.Ease
    })
    .onExit((type: RouteType, progress: number) => {
      this.scale = 1 - progress
      this.opacity = 1
    })
  }
}

运行结果如下图所示:

图片

笔者当前使用的 SDK 版本是 API 8 (3.1.5.5 版本),经过测试,页面转场动画的配置必须是在全局方法 pageTransition 中,否则不生效。

组件内转场动画

组件内转场动画主要用于容器组件在子组件添加和删除时添加的动画效果,目的在于提升用户体验,它通过 transition 属性方法配置动画参数,需要注意的是组件内转场动画需要配合 animateTo 才能生效,动效时长、曲线、延时跟随 animateTo 中的配置。

组件内转场动画定义介绍

bash

declare class CommonTransition<T> {
  transition(value: TransitionOptions): T;
}

declare interface TransitionOptions {
  type?: TransitionType;
  opacity?: number;
  translate?: TranslateOptions;
  scale?: ScaleOptions;
  rotate?: RotateOptions;
}

value:

设置组件转场的动画效果,TransitionOptions 说明如下:

  • type:设置组件添加和删除时的动画效果,TransitionType 参数说明如下:

    1. All(默认值):设置组件添加和删除时的动画效果。
    2. Insert:设置组件添加时的动画效果。
    3. Delete:设置组件删除时的动画效果。
  • opacity:设置组件转场时的透明度效果,为插入时起点和删除时终点的值。

  • translate:设置组件转场时的平移效果,为插入时起点和删除时终点的值。TranslateOptions 参数说明如下:

    1. x:设置组件在 X 轴上的位移。
    2. y:设置组件在 Y 轴上的位移。
    3. z:设置组件在 Z 轴上的位移。
  • scale:设置组件转场时的缩放效果,为插入时起点和删除时终点的值。ScaleOptions 参数说明如下:

    1. x:设置组件在 X 轴上的缩放。
    2. y:设置组件在 Y 轴上的缩放。
    3. z:设置组件在 Z 轴上的缩放。
    4. centerX:设置组件缩放的中心点 X 坐标。
    5. centerY:设置组件缩放的中心点 Y 坐标。
  • rotate:设置组件转场时的旋转效果,为插入时起点和删除时终点的值。RotateOptions 参数说明如下:

    1. x:设置组件在 X 轴上的旋转。
    2. y:设置组件在 Y 轴上的旋转。
    3. z:设置组件在 Z 轴上的旋转。
    4. centerX:设置组件旋转的中心点 X 坐标。
    5. centerY:设置组件旋转的中心点 Y 坐标。

简单样例如下所示:

bash

@Entry @Component struct Index {

  @State show: boolean = false;               // 控制组件的添加和删除

  build() {
    Column({space: 10}) {
      Stack() {
        if (this.show) {
          Image("pages/test.jpeg")
            .objectFit(ImageFit.Cover)
            .size({width: '100%', height: "100%"})
            .transition({                     // 设置Image转场动画
              type: TransitionType.Insert,    // 设置Image的入场动画
              opacity: 1,                     // 设置Image的不透明度
              translate: {                    // 设置Image的位移参数
                x: '100%'                     // 设置Image从父容器右侧入场
              }
            })
            .transition({                     // 设置Image转场动画
              type: TransitionType.Delete,    // 设置Image的退场动画
              opacity: 0,                     // 设置Image退场结束时的不透明度
              translate: {                    // 设置Image的位移参数
                x: '-100%'                    // 设置Image从父容器左侧退场
              }
            })
        }
      }
      .size({width: "100%", height: 80})

      Button(this.show ? "删除图片" : "添加图片")
        .size({width: 200, height: 50})
        .onClick(() => {
          animateTo({                         // 点击按钮,控制组件转场动画
            duration: 1300,                   // 设置动画的执行时长
            curve: Curve.Linear               // 设置动画曲线
          }, () => {
            this.show = !this.show;           // 控制显示和隐藏
          })
        })
    }
    .padding(10)
    .size({width: "100%", height: '100%'})
  }
}

运行结果如下图所示:

图片

组件内的转场动画时长,动画曲线等动画参数以 animationTo 方法设置的为基准。

共享元素转场动画

共享元素转场动画指的是组件支持页面间的转场,简单来说就是在页面切换时有一个共享的组件在新旧两个页面之间切换,因为共享的组件在新旧页面上的位置、尺寸等有所差异,所以在页面切换时它会从旧页面逐渐过渡到新页面中的指定位置,这样就会产生了转场动画。

共享元素转场动画定义介绍

bash

declare class CommonTransition<T> {
  sharedTransition(id: string, options?: sharedTransitionOptions): T;
}

id

设置共享元素转场动画的唯一标识,两个页面的组件配置为同一个 id,则转场过程中会进行共享元素转场,配置为空字符串时不会有共享元素转场效果。

options:

设置共享元素转场动画的配置参数, sharedTransitionOptions 参数说明如下:

  • duration:设置动画的执行时长,单位为毫秒,默认为 1000 毫秒。

  • curve:设置动画曲线,默认值为 Linear。

  • delay:设置动画的延迟执行时长,单位为毫秒,默认为 0 不延迟。

  • motionPath:自定义动画路径。

  • type:设置动画类型。

简单样例如下所示:

bash

// 第一个页面
@Entry @Component struct Index {

  build() {
    Column({space: 10}) {
      Image("pages/star.png")
        .size({width: 50, height: 50})
        .sharedTransition("img_star", {
          duration: 800,
          curve: Curve.Friction,
          delay: 200
        })
      Button('共享动画')
        .onClick(() => {
          router.push({
            url: "pages/second"
          })
        })
    }
    .padding(10)
    .size({width: "100%", height: '100%'})
  }
}

// 第二个页面
@Entry @Component struct Second {

  build() {
    Stack() {
      Navigator({target: "", type: NavigationType.Back}) {
        Image("pages/star.png")
          .size({width: 120, height: 120})
          .sharedTransition("img_star")
      }
    }
    .size({width: '100%', height: '100%'})
  }
}

运行结果如下图所示:

图片