动画是UI界面的重要元素之一,ArkUI开发框架为开发者提供了丰富的动画能力,如属性动画、显示动画、转场动画和路径动画等。

ArkUI动画_fish

一、属性动画

1.1.什么是属性动画?

组件的某些通用属性变化时,可以通过属性动画实现渐变过渡效果,提升用户体验。支持的属性包括width、height、backgroundColor、opacity、scale、rotate、translate等。

1.2.使用场景

属性动画,是最基础的动画,其功能强大、使用场景众多。常用于如下场景中:

  • 页面布局发生变化。例如添加、删除部分组件元素。
  • 页面元素的可见性和位置发生变化。例如显示或者隐藏部分元素,或者将部分元素从一端移动到另外一端。
  • 页面中图形图片元素动起来。例如使页面中的静态图片动起来。

属性动画是组件的通用属性发生改变时而产生的属性渐变效果。如下图所示,其原理是当组件的通用属性发生改变时,组件状态由初始状态逐渐变为结束状态的过程中,会创建多个连续的中间状态,逐帧播放后,就会形成属性渐变效果,从而形成动画。

ArkUI动画_属性动画_02

属性动画的使用方式也非常简单,只需要给组件(包括基础组件和容器组件)添加animation属性,设置好参数即可,如下代码所示:

Image($r('app.media.Sns'))   
   .animation({   
      duration: 1000,    
      tempo: 1.0,    
      delay: 0,    
      curve: Curve.Linear,    
      playMode: PlayMode.Normal,    
      iterations: 1  
   })

如果之间执行上面的代码,会发现并没有动画,这是因为:

  • animation属性作用域。animation自身也是组件的一个属性,其作用域为animation之前。即产生属性动画的属性须在animation之前声明,其后声明的将不会产生属性动画。例如,我们期望产生动画的属性为Image组件的width属性,故该属性width需在animation属性之前声明。如果将该属性width在animation之后声明,则不会产生动画效果。
  • 产生属性动画的属性变化时需触发UI状态更新。在上面的示例中,因为缺少产生动画的属性,所以并不会有动画
  • 产生属性动画的属性本身需满足一定的要求,并非任何属性都可以产生属性动画。目前支持的属性包括width、height、position、opacity、backgroundColor、scale、rotate、translate等

1.3. 属性动画的定义

属性动画是通过设置组件的animation属性来给组件添加动画,当组件的width, height, opacity, backgroundColor, scale, rotate, translate等属性变更时,可以实现渐变过渡效果。

解读:给组件添加animation属性,arkUI会监控组件样式的变化,当修改了组件的样式时,aukUI发现组件样式发生了变更,会填充组件起始样式和结束样式之间的每一帧画面,从而实现样式变化的动画效果。

接口如下:

declare class CommonMethod<T> {
  animation(value: AnimateParam): T;
}

animate 常用属性:

参数名称

参数类型

必填

描述

duration

number


设置动画时长

默认值:1000,单位:毫秒

tempo

number


动画播放速度,数值越大,速度越快

默认值:1

curve

string | Curve


设置动画曲线

默认值:Curve.EaseInOut,平滑开始和结束

delay

number


设置动画延迟执行的时长

默认值:0,单位:毫秒

iterations

number


设置播放次数

默认值:1,取值范围:[-1, +∞]

说明:-1代表无限次播放

playMode

PlayMode


设置动画播放模式,默认播放完成后重头开始播放

默认是:PlayMode.Normal

onFinish

() => void


状态回调,动画播放完成时触发

其中变化曲线curve枚举值为:

名称

描述

Linear

表示动画从头到尾的速度都是相同的。

Ease

表示动画以低速开始,然后加快,在结束前变慢,CubicBezier(0.25, 0.1, 0.25, 1.0)。

EaseIn

表示动画以低速开始,CubicBezier(0.42, 0.0, 1.0, 1.0)。

EaseOut

表示动画以低速结束,CubicBezier(0.0, 0.0, 0.58, 1.0)。

EaseInOut

表示动画以低速开始和结束,CubicBezier(0.42, 0.0, 0.58, 1.0)。

FastOutSlowIn

标准曲线,cubic-bezier(0.4, 0.0, 0.2, 1.0)。

LinearOutSlowIn

减速曲线,cubic-bezier(0.0, 0.0, 0.2, 1.0)。

FastOutLinearIn

加速曲线,cubic-bezier(0.4, 0.0, 1.0, 1.0)。

ExtremeDeceleration

急缓曲线,cubic-bezier(0.0, 0.0, 0.0, 1.0)。

Sharp

锐利曲线,cubic-bezier(0.33, 0.0, 0.67, 1.0)。

Rhythm

节奏曲线,cubic-bezier(0.7, 0.0, 0.2, 1.0)。

Smooth

平滑曲线,cubic-bezier(0.4, 0.0, 0.4, 1.0)。

Friction

阻尼曲线,CubicBezier(0.2, 0.0, 0.2, 1.0)。

上面的设置的效果是:相同时间,结果相同,不同的过程

播放模式playMode枚举值为:

名称

描述

Normal

动画按正常播放。

Reverse

动画反向播放。

Alternate

动画在奇数次(1、3、5…)正向播放,在偶数次(2、4、6…)反向播放。

AlternateReverse

动画在奇数次(1、3、5…)反向播放,在偶数次(2、4、6…)正向播放。

实例如下:

Text('^_^')
  .position({ //组件的位置
    x: 10, // x轴坐标
    y: 0 // y轴坐标
  })
  .rotate({
    angle: 0, // 旋转角度
    centerX: '50%', // 旋转中心横坐标
    centerY: '50%' // 旋转中心纵坐标
  })
  .animation({
    duration: 1000,
    curve: Curve.EaseInOut
  })

1.4.案例代码

案例一:

@Entry
@Component
struct AnimationPage {
  @State btnWidth:number = 300;
  @State btnHeight:number = 90;

  build() {
    Column(){
      Button({type:ButtonType.Normal}){
        Text("Animation").fontSize(25)
      }
      .width(this.btnWidth)
      .height(this.btnHeight)
      .borderRadius(8)
      .animation({
        duration:2000,           //设置动画指定时长
        curve:Curve.Friction,    //设置动画曲线样式, Curve.Friction阻尼曲线
        iterations:2,            //设置动画执行两次
        playMode:PlayMode.Normal //设置动画播放模式  PlayMode.Normal动画按正常播放。
      })
      .onClick(()=>{
        this.btnWidth = 150
        this.btnHeight = 50
      })

    }.width("100%").height("100%").padding(20)
  }
}

预览效果如下:

ArkUI动画_属性动画_03

案例二:

准备主页Index.ets,点击开始游戏,可以跳转到游戏界面:

import router from '@ohos.router'
@Entry
@Component
struct Index {

  build() {
    Column(){
      Stack(){
        Image($r("app.media.home"))

        Button(){
          Image($r("app.media.button1")).width(260).width(150)
        }.onClick(()=>{
          router.pushUrl({url:"pages/AnimationPage02"},
            router.RouterMode.Single,
            err => {   // 异常响应回调函数
              if(err) {
                console.log(`路由失败, errCode: ${err.code}, errMsg: ${err.message}`)
              }
            })
        })

        Button(){
          Image($r("app.media.button2")).width(260).width(150)
        }.margin({top:150})

        Button(){
          Image($r("app.media.button3")).width(260).width(150)
        }.margin({top:300})

      }
    }
    .width("100%")
    .height("100%")
  }
}

创建 AnimationPage02.ets 实现页面内容如下:

import router from '@ohos.router'

@Entry
@Component
struct AnimationPage02 {
  //小鱼坐标
  @State fishX:number = 200
  @State fishY:number = 180
  //小鱼的角度
  @State angle:number = 0

  //小鱼图片
  @State src:Resource = $r("app.media.fish_right")

  @State isBegin:boolean = false



  build() {
    Row(){
      Stack(){
        //背景图片
        Image($r("app.media.fish_home"))

        //返回按钮
        Button(){
          Text("返回").height(25).width(70).textAlign(TextAlign.Center).fontColor(Color.White)
        }
          .type(ButtonType.Capsule)
          .position({x:0,y:0})
          .position({x:10, y:10})
          .onClick(()=>{
            //返回上一页
            router.back()
          })

        //判断
        if(!this.isBegin){
          //开始游戏
          Button("点击即可开始游戏")
            .height(40)
            .onClick(()=>{
              //控制状态, 点击后显示鱼
              this.isBegin = true
            })
        }else {
          //小鱼图片
          Image(this.src)
            .position({x: this.fishX - 20,y:this.fishY-20})
            .rotate({angle:this.angle,centerX:"50%",centerY:"50%"})
            .width(40)
            .height(40)
            .animation({duration:500})


          Row(){
            Button("←").backgroundColor("#0DB7D8")
              .onClick(()=>{
                this.fishX -= 30
                //点击向左。图片替换
                this.src = $r("app.media.fish_left")
              })
            Column({space:40}){
              Button("↑").backgroundColor("#0DB7D8")
                .onClick(()=>{
                  this.fishY -= 30
                })
              Button("↓").backgroundColor("#0DB7D8")
                .onClick(()=>{
                  this.fishY += 30
                })
            }
            Button("→").backgroundColor("#0DB7D8")
              .onClick(()=>{
                this.fishX += 30
                //点击向右。图片替换
                this.src = $r("app.media.fish_right")
              })
          }.height(240).width(240).position({x: 50,y:150})
        }
      }
    }.width("100%").height("100%")
  }
}

预览效果如下:

ArkUI动画_fish_04

二、显式动画

显式动画是通过全局animateTo函数来修改组件属性,实现属性变化时的渐变过渡效果。显式动画使用起来比较灵活,也是实际开发中使用较多的一种方式。

2.1.显示动画定义

接口如下:

animateTo(value: AnimateParam, event: () => void): void

从API version 9开始,该接口支持在ArkTS卡片中使用。上面的参数说明

参数

类型

是否必填

描述

value

AnimateParam

设置动画效果相关参数。

event

() => void

指定显示动效的闭包函数,在闭包函数中导致的状态变化系统会自动插入过渡动画。

AnimateParam和上面的animate 常用属性一致,这里就不错赘述了。使用如下:

private x: number = 10
private y: number = 0

Text('^_^')
  .position({
    x: this.x, // x轴坐标
    y: this.y // y轴坐标
  })
  .rotate({
    angle: 0, // 旋转角度
    centerX: '50%', // 旋转中心横坐标
    centerY: '50%' // 旋转中心纵坐标
  })
  .onClick(() => {
    // 显式调用animateTo函数触发动画
    .animateTo(
      { duration: 1000}, // 动画参数
      () => {
        // 修改组件属性关联的状态变量
        this.x -= 20
        this.y += 50
      }
    )
  })

2.2.案例

案例一:

修改 AnimationPage.ets 代码如下:

import prompt from '@system.prompt';

@Entry
@Component
struct AnimationPage {
  @State btnWidth:number = 300;
  @State btnHeight:number = 90;
  @State btnAnim:boolean = true

  build() {
    Column(){
      Button({type:ButtonType.Normal}){
        Text("Animation").fontSize(25)
      }
      .width(this.btnWidth)
      .height(this.btnHeight)
      .borderRadius(8)
      .onClick(()=>{
        if (this.btnAnim) {
          animateTo({
            duration:2000,           //设置动画指定时长
            tempo: 1,                //动画播放速度。值越大,动画播放速度越快,值越小,动画播放速度越慢。值0表示没有动画
            curve:Curve.Friction,    //设置动画曲线样式, Curve.Friction阻尼曲线
            iterations:2,            //设置动画执行两次
            playMode:PlayMode.Normal, //设置动画播放模式  PlayMode.Normal动画按正常播放。
            onFinish:()=>{prompt.showToast({message:"if play finish"})}
          },()=>{
            //修改宽和高
            this.btnWidth = 150
            this.btnHeight = 50
          })
        }else {
          animateTo({
            duration: 500,              //设置动画指定时长
            tempo: 1,                   //动画播放速度。值越大,动画播放速度越快,值越小,动画播放速度越慢。值0表示没有动画
            curve: Curve.Linear,        //设置动画曲线样式,
            delay: 200,                 //设置动画的延迟执行时间,单位为毫秒,默认值为 0 不延迟。
            iterations: 1,              //设置动画执行两次
            playMode: PlayMode.Normal,  //设置动画播放模式
            onFinish: ()=>{
              prompt.showToast({message:"else play finish"})
            }
          },()=>{
            this.btnWidth = 200
            this.btnHeight = 60
          })
        }
        this.btnAnim = !this.btnAnim
      })

    }.width("100%").height("100%").padding(20)
  }
}

预览效果如下:

ArkUI动画_属性动画_05

案例二:

修改 AnimationPage02.ets 代码,使用显示动画实现下面的之前的小鱼游戏,如下:

import router from '@ohos.router'

@Entry
@Component
struct AnimationPage02 {
  //小鱼坐标
  @State fishX:number = 200
  @State fishY:number = 180
  //小鱼的角度
  @State angle:number = 0

  //小鱼图片
  @State src:Resource = $r("app.media.fish_right")

  @State isBegin:boolean = false



  build() {
    Row(){
      Stack(){
        //背景图片
        Image($r("app.media.fish_home"))

        //返回按钮
        Button(){
          Text("返回").height(25).width(70).textAlign(TextAlign.Center).fontColor(Color.White)
        }
          .type(ButtonType.Capsule)
          .position({x:0,y:0})
          .position({x:10, y:10})
          .onClick(()=>{
            //返回上一页
            router.back()
          })

        //判断
        if(!this.isBegin){
          //开始游戏
          Button("点击即可开始游戏")
            .height(40)
            .onClick(()=>{
              //控制状态, 点击后显示鱼
              this.isBegin = true
            })
        }else {
          //小鱼图片
          Image(this.src)
            .position({x: this.fishX - 20,y:this.fishY-20})
            .rotate({angle:this.angle,centerX:"50%",centerY:"50%"})
            .width(40)
            .height(40)
            //.animation({duration:500}) //属性动画


          
          Row(){
            Button("←").backgroundColor("#0DB7D8")
              .onClick(()=>{
                //采用显示动画
                animateTo({
                  duration:500
                },()=>{
                  this.fishX -= 30
                  //点击向左。图片替换
                  this.src = $r("app.media.fish_left")
                })
              })
            Column({space:40}){
              Button("↑").backgroundColor("#0DB7D8")
                .onClick(()=>{
                  //
                  animateTo({
                    duration:500
                  },()=>{
                    this.fishY -= 30
                  })
                })
              Button("↓").backgroundColor("#0DB7D8")
                .onClick(()=>{
                  //
                  animateTo({
                    duration:500
                  },()=>{
                    this.fishY += 30
                  })
                })
            }
            Button("→").backgroundColor("#0DB7D8")
              .onClick(()=>{
                //
                animateTo({
                  duration:500
                },()=>{
                  this.fishX += 30
                  //点击向右。图片替换
                  this.src = $r("app.media.fish_right")
                })
              })
          }.height(240).width(240).position({x: 50,y:150})
        }
      }
    }.width("100%").height("100%")
  }
}

预览效果如下:

ArkUI动画_fish_06

三、转场动画

ArkUI开发框架提供了转场动画,可以划分为页面转场动画、组件转场动画和共享元素转场动画。

3.1.页面转场动画

页面转场动画的设置在全局pageTransition方法内配置页面入场和页面退场时的自定义转场动效。

3.1.1.页面入场和退场定义

接口如下:

// 入场动画接口
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 说明如下:
  • None:没有样式
  • Push:PageA 跳转到 PageB 时,PageA 为 Exit + Push,PageB 为 Enter + Push。
  • Pop:PageB 返回至 PageA 时,PageA 为 Enter + Pop, PageB 为 Exit + Pop。
  • duration:设置动画执行时间,单位毫秒,默认为 0 。
  • curve:动画曲线,默认值为 Linear 。
  • onEnter:页面入场时的事件回调,其中 progress 取值范围为:[0 ~ 1]。
  • onExit:页面入场时的事件回调,其中 progress 取值范围为:[0 ~ 1]。

事件

事件

功能描述

onEnter(event: (type?: RouteType, progress?: number) => void)

回调入参为当前入场动画的归一化进度[0 - 1]。

- type:跳转方法。

- progress:当前进度。

触发该事件的条件:逐帧回调,直到入场动画结束,progress从0变化到1。

onExit(event: (type?: RouteType, progress?: number) => void)

回调入参为当前退场动画的归一化进度[0 - 1]。

- type:跳转方法。

- progress:当前进度。

触发该事件的条件:逐帧回调,直到退场动画结束,progress从0变化到1。

3.1.2.页面间转场动画属性介绍

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

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 说明如下:
  • Left:设置到入场时表示从左边滑入,出场时表示滑出到左边。
  • Right:设置到入场时表示从右边滑入,出场时表示滑出到右边。
  • Top:设置到入场时表示从上边滑入,出场时表示滑出到上边。
  • Bottom:设置到入场时表示从下边滑入,出场时表示滑出到下边。
  • translate:设置页面转场时的平移效果,为入场时起点和退场时终点的值,和 slide 同时设置时默认生效 slide
  • scale:设置页面转场时的缩放效果,为入场时起点和退场时终点的值。
  • opacity: 设置入场的起点透明度值或者退场的终点透明度值。

3.1.3.案例

  • 案例一:配置了当前页面的入场动画为淡入,退场动画为缩小。

准备页面一TransitionAnimationPage01.ets,内容如下:

import router from '@ohos.router'
@Entry
@Component
struct TransitionAnimationPage01 {
  @State proportion: number = 1    //默认缩放比例
  @State transparency: number = 1  //默认不透明度

  build() {
    Stack(){
      Image($r("app.media.transitions1"))
        .size({width:"100%",height:"100%"})
        .onClick(()=>{
          router.pushUrl({url:"pages/TransitionAnimationPage02"})
        })

      Text("第一个页面").fontSize(25).fontColor(Color.Red).fontWeight(FontWeight.Bold)
    }.size({width:"100%",height:"100%"})
    .scale({
      x:this.proportion,  //设置当前页面X轴方向的缩放比例
      //y:this.proportion   //设置当前页面Y轴方向的缩放比例
    })
    .opacity(this.transparency)  //设置当前页面不透明度

  }

  //设置页面转场动画
  pageTransition(){          //添加全局pageTransition方法
    PageTransitionEnter({    //设置当前页面入场动画的配置信息
      duration: 1200,        //动画执行时间
      curve: Curve.Linear    //设置动画曲线
    }).onEnter((type: RouteType, progress: number) => {//设置每一帧的动画事件回调
       //设置缩放比例
      this.proportion = 1
      this.transparency = progress
      console.log("页面一进入动画帧:"+progress);
    })// 进场过程中会逐帧触发onEnter回调,入参为动效的归一化进度(0% -- 100%)

    PageTransitionExit({     //设置当前页面退场动画的配置信息
      duration: 1500,        //动画执行时间
      curve: Curve.Ease      //设置动画曲线
    }).onExit((type: RouteType, progress: number) => {//设置每一帧的动画事件回调
      console.log("页面一退出动画帧:"+progress);
      //设置缩放比例
      this.proportion = 1 - progress
      //设置透明度
      this.transparency = 1
      console.log("PageTransitionExit: "+type)
    })// 退场过程中会逐帧触发onExit回调,入参为动效的归一化进度(0% -- 100%)
  }
}

准备页面二:TransitionAnimationPage02.ets内容如下:

@Entry
@Component
struct TransitionAnimationPage02 {
  @State proportion2: number = 1    //默认缩放比例
  @State transparency2: number = 1  //默认不透明度

  build() {
    Stack(){
      //路由容器组件,提供路由跳转能力。
      Navigator({target:"", type:NavigationType.Back}){
        Image($r("app.media.transitions2"))
          .size({width:"100%",height:"100%"})
      }

      Text("第二个页面").fontSize(25).fontColor(Color.Red).fontWeight(FontWeight.Bold)
    }.size({width:"100%",height:"100%"})
    .scale({
      x:this.proportion2,  //设置当前页面X轴方向的缩放比例
      //y:this.proportion2
    })
    .opacity(this.transparency2)  //设置当前页面不透明度

  }

  //设置页面转场动画
  pageTransition(){          //添加全局pageTransition方法
    PageTransitionEnter({    //设置当前页面入场动画的配置信息
      duration: 3000,        //动画执行时间
      curve: Curve.Linear    //设置动画曲线
    }).onEnter((type: RouteType, progress: number) => {//设置每一帧的动画事件回调
      console.log("页面二退出动画帧:"+progress);
      //设置缩放比例
      this.proportion2 = 1
      this.transparency2 = progress
    })

    PageTransitionExit({     //设置当前页面退场动画的配置信息
      duration: 1500,        //动画执行时间
      curve: Curve.Ease      //设置动画曲线
    }).onExit((type: RouteType, progress: number) => {//设置每一帧的动画事件回调
      console.log("页面二退出动画帧:"+progress);
      //设置缩放比例
      this.proportion2 = 1 - progress
      //设置透明度
      this.transparency2 = 1
    })
  }
}

预览效果如下:

ArkUI动画_fish_07

注意:页面转场动画的配置必须是在全局方法 pageTransition 中,否则不生效。

  • 案例二:配置了当前页面的入场动画为从左侧滑入,退场为缩小加透明度变化。

修改页面一TransitionAnimationPage01.ets,内容如下:

import router from '@ohos.router'
@Entry
@Component
struct TransitionAnimationPage01 {
  @State proportion: number = 1    //默认缩放比例
  @State transparency: number = 1  //默认不透明度

  build() {
    Stack(){
      Image($r("app.media.transitions1"))
        .size({width:"100%",height:"100%"})
        .onClick(()=>{
          router.pushUrl({url:"pages/TransitionAnimationPage02"})
        })

      Text("第一个页面").fontSize(25).fontColor(Color.Red).fontWeight(FontWeight.Bold)
    }.size({width:"100%",height:"100%"})
    .scale({
      x:this.proportion,  //设置当前页面X轴方向的缩放比例
      //y:this.proportion   //设置当前页面Y轴方向的缩放比例
    })
    .opacity(this.transparency)  //设置当前页面不透明度

  }

  //设置页面转场动画,使用系统提供的多种默认效果(平移、缩放、透明度等)
  pageTransition(){          //添加全局pageTransition方法
    PageTransitionEnter({    //设置当前页面入场动画的配置信息
      duration: 1200,        //动画执行时间
    }).slide(SlideEffect.Left)


    PageTransitionExit({     //设置当前页面退场动画的配置信息
      delay: 100,//动画延迟时长,单位为毫秒,默认为0,不延迟播放。
}) .translate({x:100.0,y:100.0}) .opacity(0) } }

修改页面二:TransitionAnimationPage02.ets内容如下:

@Entry
@Component
struct TransitionAnimationPage02 {
  @State proportion2: number = 1    //默认缩放比例
  @State transparency2: number = 1  //默认不透明度

  build() {
    Stack(){
      //路由容器组件,提供路由跳转能力。
      Navigator({target:"", type:NavigationType.Back}){
        Image($r("app.media.transitions2"))
          .size({width:"100%",height:"100%"})
      }

      Text("第二个页面").fontSize(25).fontColor(Color.Red).fontWeight(FontWeight.Bold)
    }.size({width:"100%",height:"100%"})
    .scale({
      x:this.proportion2,  //设置当前页面X轴方向的缩放比例
      //y:this.proportion2
    })
    .opacity(this.transparency2)  //设置当前页面不透明度

  }

  //设置页面转场动画,使用系统提供的多种默认效果(平移、缩放、透明度等)
  pageTransition(){          //添加全局pageTransition方法
    PageTransitionEnter({    //设置当前页面入场动画的配置信息
      duration: 3000,        //动画执行时间
    }).slide(SlideEffect.Left)

    PageTransitionExit({     //设置当前页面退场动画的配置信息
      delay: 100,        //动画延迟时长,单位为毫秒,默认为0,不延迟播放。
    })
      .translate({x:100.0, y:100.0})
      .opacity(0)
  }
}

预览效果如下:

ArkUI动画_fish_08

3.2.组件内转场

组件内转场主要通过transition属性配置转场参数,在组件插入和删除时显示过渡动效,主要用于容器组件中的子组件插入和删除时,提升用户体验(需要配合animateTo才能生效,动效时长、曲线、延时跟随animateTo中的配置)

3.2.1.组件内转场定义

属性说明如下:

名称

参数类型

参数描述

transition

TransitionOptions

设置组件插入显示和删除隐藏的过渡效果。

默认值:不设置任何过渡效果时,默认有透明度从0到1的过渡效果。若设置了其他过渡效果,以设置的过渡效果为准。

说明:

所有参数均为可选参数,详细描述见TransitionOptions参数说明。

TransitionOptions参数说明

参数名称

参数类型

必填

参数描述

type

TransitionType

默认包括组件新增和删除。

默认值:TransitionType.All

说明:

不指定Type时说明插入删除使用同一种效果。

opacity

number

设置组件转场时的透明度效果,为插入时起点和删除时终点的值。

默认值:1

取值范围: [0, 1]

说明:

设置小于0或大于1的非法值时,按1处理。

translate

{

x? : number | string,

y? : number | string,

z? : number | string

}

设置组件转场时的平移效果,为插入时起点和删除时终点的值。

-x:横向的平移距离。

-y:纵向的平移距离。

-z:竖向的平移距离。

scale

{

x? : number,

y? : number,

z? : number,

centerX? : number | string,

centerY? : number | string

}

设置组件转场时的缩放效果,为插入时起点和删除时终点的值。

-x:横向放大倍数(或缩小比例)。

-y:纵向放大倍数(或缩小比例)。

-z:当前为二维显示,该参数无效。

- centerX、centerY指缩放中心点,centerX和centerY默认值是"50%"。

- 中心点为0时,默认的是组件的左上角。

rotate

{

x?: number,

y?: number,

z?: number,

angle: number | string,

centerX?: number | string,

centerY?: number | string

}

设置组件转场时的旋转效果,为插入时起点和删除时终点的值。

-x:横向的旋转向量。

-y:纵向的旋转向量。

-z:竖向的旋转向量。

- centerX,centerY指旋转中心点,centerX和centerY默认值是"50%"。

- 中心点为(0,0)时,默认的是组件的左上角。

TransitionType枚举值说明如下:

名称

描述

All

指定当前的Transition动效生效在组件的所有变化场景。

Insert

指定当前的Transition动效生效在组件的插入显示场景。

Delete

指定当前的Transition动效生效在组件的删除隐藏场景。

3.2.2.案例一

创建 TransitionAnimationWithinComponents01.ets 如下:

@Entry
@Component
struct TransitionAnimationWithinComponents01 {
  @State isShow:boolean = false;     //控制组件内的添加和删除
  build() {
    Stack(){
      if (this.isShow){
        Image($r("app.media.transitions1"))
          .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从父容器左侧退出
            }
          })
      }

      Button(this.isShow?"删除图片":"添加图片")
        .size({width:160,height:40})
        .position({x:100,y:400})
        .onClick(()=>{
          animateTo( //点击按钮,控制组件转场动画
            {
              duration:1500, //设置动画的执行时长
              curve:Curve.Linear //设置动画曲线
            }
            ,()=>{
            this.isShow = !this.isShow //控制图片的移除和显示
          })
        })
    }.size({width:"100%",height:"100%"})
  }
}

预览效果如下:

ArkUI动画_属性动画_09

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

3.2.3.案例二

对于之前的小鱼游戏,实现小鱼入场的时候动画效果,修改 AnimationPage02.ets 内容如下:

import router from '@ohos.router'

@Entry
@Component
struct AnimationPage02 {
  //小鱼坐标
  @State fishX:number = 200
  @State fishY:number = 180
  //小鱼的角度
  @State angle:number = 0

  //小鱼图片
  @State src:Resource = $r("app.media.fish_right")

  @State isBegin:boolean = false



  build() {
    Row(){
      Stack(){
        //背景图片
        Image($r("app.media.fish_home"))

        //返回按钮
        Button(){
          Text("返回").height(25).width(70).textAlign(TextAlign.Center).fontColor(Color.White)
        }
          .type(ButtonType.Capsule)
          .position({x:0,y:0})
          .position({x:10, y:10})
          .onClick(()=>{
            //返回上一页
            router.back()
          })

        //判断
        if(!this.isBegin){
          //开始游戏
          Button("点击即可开始游戏")
            .height(40)
            .onClick(()=>{
              //1.组件内转场动画状态控制
              animateTo(
                {duration:1500},
                ()=>{
                  //控制状态, 点击后显示鱼
                  this.isBegin = true
              })
            })
        }else {
          //小鱼图片
          Image(this.src)
            .position({x: this.fishX - 20,y:this.fishY-20})
            .rotate({angle:this.angle,centerX:"50%",centerY:"50%"})
            .width(40)
            .height(40)
            .transition({ //1.组件内转场动画
              type:TransitionType.Insert, //新增时动画
              opacity:0,//设置组件转场时的透明度效果,为插入时起点和删除时终点的值。默认值:1
              translate: {x:-300} //让从左侧移动进入,所以写负数
            })
        }

        Row(){
          Button("←").backgroundColor("#0DB7D8")
            .onClick(()=>{
              //采用显示动画
              animateTo({
                duration:500
              },()=>{
                this.fishX -= 30
                //点击向左。图片替换
                this.src = $r("app.media.fish_left")
              })
            })
          Column({space:40}){
            Button("↑").backgroundColor("#0DB7D8")
              .onClick(()=>{
                //
                animateTo({
                  duration:500
                },()=>{
                  this.fishY -= 30
                })
              })
            Button("↓").backgroundColor("#0DB7D8")
              .onClick(()=>{
                //
                animateTo({
                  duration:500
                },()=>{
                  this.fishY += 30
                })
              })
          }
          Button("→").backgroundColor("#0DB7D8")
            .onClick(()=>{
              //
              animateTo({
                duration:500
              },()=>{
                this.fishX += 30
                //点击向右。图片替换
                this.src = $r("app.media.fish_right")
              })
            })
        }.height(240).width(240).position({x: 50,y:150})
      }
    }.width("100%").height("100%")
  }
}

预览效果如下:

ArkUI动画_属性动画_10

3.3.共享元素转场

当路由进行切换时,可以通过设置组件的 sharedTransition 属性将该元素标记为共享元素并设置对应的共享元素转场动效。换言之就是在页面切换时有一个共享的组件在新旧两个页面之间切换,因为共享的组件在新旧页面上的位置、尺寸等有所差异,所以在页面切换时它会从旧页面逐渐过渡到新页面中的指定位置,这样就会产生了转场动画。

3.3.1.共享元素转场动画定义

属性

名称

参数

参数描述

sharedTransition

id: string,

{

duration?: number,

curve?: Curve | string,

delay?: number,

motionPath?:

{

path: string,

form?: number,

to?: number,

rotatable?: boolean

},

zIndex?: number,

type?: SharedTransitionEffectType

}

两个页面中id值相同且不为空字符串的组件即为共享元素,在页面转场时可显示共享元素转场动效。

- id:设置组件的id。

- duration:描述共享元素转场动效播放时长。

默认值:1000

单位:毫秒

取值范围:[0, +∞)

- curve:默认曲线为Linear,有效值参见Curve说明。

- delay:单位为毫秒,默认不延时播放。

默认值:0

单位:毫秒

取值范围:[0, +∞)

设置小于0的值时,按值为0处理。

- motionPath:运动路径信息,详细说明请参考路径动画

- path:设置路径。

- from:设置起始值。

- to:设置终止值。

- rotatable:是否旋转。

默认值:false

- zIndex:设置Z轴。

默认值:0

- type:动画类型。

默认值:SharedTransitionEffectType.Exchange

 

注意:type为SharedTransitionEffectType.Exchange时motionPath才会生效。

SharedTransitionEffectType枚举如下:

名称

描述

Static

目标页面元素的位置保持不变,可以配置透明度动画。目前,只有为重定向到目标页面而配置的静态效果才会生效。

Exchange

将源页面元素移动到目标页面元素位置并适当缩放。

3.3.2.案例

创建 SharedTransitionExample.ets,代码内容如下:

import router from '@ohos.router'
@Entry
@Component
struct SharedTransitionExample {

  build() {
    Stack(){
      Image($r("app.media.transitions1"))//加载背景图片

      Image($r("app.media.icon_star"))//共享元素
        .position({x:90,y:50})
        .width(50)
        .height(50)
        .sharedTransition(
          "sharedImage",
          {
            duration:1500, //描述共享元素转场动效播放时长。
            curve:Curve.Linear,//默认曲线为Linear
            delay:200 // 单位为毫秒,默认不延时播放。默认值:0
          }
        )

      Button("共享动画")
        .onClick(()=>{
          //路由跳转
          router.pushUrl({url:"pages/PageBExample"})
        })

    }.width("100%").height("100%")
  }
}

在创建 PageBExample.ets,作为跳转后的页面,代码如下:

@Entry
@Component
struct PageBExample {
  build() {
    Stack(){
      Image($r("app.media.transitions2"))//背景图片
      Image($r("app.media.icon_star"))//共享元素
        .width(100)
        .height(100)
        .sharedTransition("sharedImage",{
          duration:1500, //描述共享元素转场动效播放时长。
          curve:Curve.Linear,//默认曲线为Linear
          delay:200 // 单位为毫秒,默认不延时播放。默认值:0
        })
    }.width("100%").height("100%")
  }
}

预览效果如下:

ArkUI动画_属性动画_11

四、路径动画

设置组件进行位移动画时的运动路径。

4.1.路径动画定义

属性

名称

参数

描述

motionPath

{

path: string,

from?: number,

to?: number,

rotatable?: boolean

}

设置组件的运动路径。

- path:位移动画的运动路径,使用svg路径字符串。path中支持使用start和end进行起点和终点的替代,如:'Mstart.x start.y L50 50 Lend.x end.y Z'。

- from:运动路径的起点。取值范围:[0, 1]

- to:运动路径的终点。取值范围:[0, 1]

- rotatable:是否跟随路径进行旋转。

4.2.案例

创建 MotionPathExample.ets 代码内容如下:

@Entry
@Component
struct MotionPathExample  {
  @State toggle:boolean = true

  build() {
    Column(){
      Button("MotionPath")
        .margin(50)
        .motionPath({
          // 执行动画:从起点移动到(300,200)、(300 500)、(500,500)再到终点(200,500)
          path:"Mstart.x start.y L300 200 L300 500  L500 500 L200 500 Lend.x end.y",
          from: 0.0,//运动路径的起点。
          to:1.0,//运动路径的终点
          rotatable:true //跟随路径进行旋转。
        })
        .onClick(()=>{
          animateTo({duration:5000,curve:Curve.Linear},()=>{
            this.toggle = !this.toggle //通过this.toggle变化组件的位置
          })
        })

    }.width("100%").height("100%").alignItems(this.toggle?HorizontalAlign.Start:HorizontalAlign.End).justifyContent(FlexAlign.Center)
  }
}

预览效果如下:

ArkUI动画_Image_12