HarmonyOS-ArkUI: 组件内转场(transition)

什么是组件内转场

组件内转场指的是组件在触发转场的时机所具备的动画效果。转场的时机指的是,组件元素发生变化的时候,具体为:

组件被添加
组件被删除
组件可见性发生变化-Visibility

这些场景有时候单纯的让其消失,出现,平移有时候视觉效果会比较突兀。我们可以利用组件内转场动效实现平滑的过度。

先看怎么用再分析API

图片消失组件转场案例


@Entry
@Component
//组件内转场,实现图片的出现与消失转场效果
struct TransitionEffectTest1 {
  // 我们利用条件渲染语句来实现组件的出现与消失
  @State isImageVisible:boolean = true
  @State buttonText:string = '展示'
  build() {
    Column(){
      Button(this.buttonText)
        .onClick(()=>{
          this.isImageVisible = !this.isImageVisible
          this.buttonText = this.isImageVisible? '展示' : '消失'
        })

      if (this.isImageVisible) { //此处是条件渲染语句,isImageVisible的值发生变化,组件便可以触发消失和出现
        Image($r('app.media.11073300'))
          .objectFit(ImageFit.ScaleDown)
          .width('100%')
          .margin({top: 50})
          //接下来重点,书写转场时的动效
          // 这个转场配置一写,此组件的出现和消失便具备了转场效果
          // 我们看到转场配置也是要出动画参数的,例如持续500毫秒,缓入缓出的效果。
          // 另外转场也是可以通过combine函数来自由搭配。
          .transition(
            TransitionEffect.OPACITY
              .combine(TransitionEffect.rotate({z: 1, angle: 180}))
              .animation({duration: 500, curve: Curve.Ease})
          )

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

  }
}

真机效果展示:

组件转场API

我们从上文中看出,组件转场用到了一个名字叫transition的函数,这个函数是CommonMethod类中声明的一个函数。这个CommonMethod类是UI组件某顶层类, 所以所有的UI组件都可以继承下来,并调用这个方法。所有的组件都具备组件转场能力。

Transition触发时机

触发时机有两个:

当组件插入或者删除的时候, 比如条件渲染语句涉及到的条件发生了变化,或者ForEach新增或删除组件,会递归触发所有新插入或者删除组件的transition效果
当组件visibility属性,在可见与不可见之间发生改变的时候,只触发该组件的transition效果。其他组件不会触发。


关于这个函数的API,涉及的类是比较多的。 包括我们之前属性动画animation, 显式动画animateTo都用到的AnimationParam类。此处我们也会画出来!

下文中的图,既表示出来了类的关系,也表示出来了其属性的作用。我就不用文字来进行解释了!看图即可:

transition函数的位置及参数含义

TransitionEffect API分析图

TransitionEffect是组件转场动画的核心类,用于描述这个场应该怎么来转。也就是动画要是什么动画。

API如下图所示:

ainimation函数参数详解

上图中没有标注animation方法的具体参数细节。原因是这块涉及到的内容比较多,我们单独开一个图来解释

从这些类图中我们也可以大致看出来, TransitionEffect的调用特点是链式调用,其中它本身内置了几个默认的只读变量,类型也是TransitionEffect。意思很明显:直接让大家拿来用即可。里面的信息应该已经内置好了。

另外它还准备了几个设置自定义变化的接口,返回值也是TransitionEffect类型,很明显就是方便链式调用用的。

最后,它还有一个combine方法, 这个函数很明显就是连接不同的TransitionEffect使用的,由此可见,TransitionEffect是支持任意组合的形式的,比如您可以在转场的时候同时设置,平移叠加透明度变化叠加缩放的效果!

之后我们可以在代码里感受一下。

animation函数如果不进行配置会发生什么

如果您写的转场配置中,没有进行animation进行函数调用,那么转场动效会因为没有这种配置而导致不展示。默认情况下动效不展示。
但是!这里面有例外!!!如果你没有配置animation,但是有关于触发组件转场的变量变了,而这个变量变化还被写到animateTo函数中了,也就是在animateTo函数的event参数中被改变的。那么!会沿用这个动画配置!奇怪吧。这个HarmonyOS的动画比较有意思,有时间真的要研究下其实现架构和原理。

为了证明这块的区别,我又画了个图,图中表示了,代码之间的对比,与展示之间的区别:

对于组件转产动效而言,动画参数的生效,甚至可以扩展至其本身的配置之外,具体是下方的规则

动画参数生效顺序为: 本TransitionEffect指定的animation参数 > 前面的transitionEffect指定的animation参数 > 触发该组件消失的animateTo中的动画参数。
如果您根本没有配置TransitionEffect中的animation参数,并且也没有使用animateTo触发转场动画,那么该组件就不会有转场动效,直接出现或者消失。

其余还有一些我们代码中没有展示出来的注意点也需要重视一下:

如果您在写TransitionEffect时,配置的转场效果,和正常展示的效果压根没区别,那么也不会展示动效。例如一个组件平常就是不透明度为1,您配置入场动效的时候,写个 TransitionEffect.opacity(1).animation(duration: 100), 白搭不奏效的。 因为实在是没什么差别可以展示的。
TransitionEffect可通过combine函数实现多个转场效果的组合,可以为每个效果分别指定animation参数且前一效果的animation的参数也可适用于后一效果。例如,TransitionEffect.OPACITY.animation({duration: 1000}).combine(TransitionEffect.translate({x: 100})),则时长为1000ms的动画参数对OPACITY和translate均生效。
如果在动画范围(animateTo、animation)内触发组件的上下树或可见性(Visibility)改变,而根组件没有配置transition,会给该组件加上默认透明度转场,即TransitionEffect.OPACITY,动画参数跟随所处动画环境的参数。如不需要可通过主动配置TransitionEffect.IDENTITY来禁用,使该组件直接出现或消失。

 

TransitionOptions API详解

如果仔细看图的话,transition其实是一个重载方法。 而TransitionOptions则是第一个重载方法的第一个参数可选类型。这个目前已经被标注为不再维护了。我们案例中也不会使用这个代码。

但是还是画了一张图,方便大家有个了解,以后万一碰到了它的使用,就比较容易理解了。

代码示例

使用不同的接口实现图片的出现消失

下图代码重点展示了

TransitionEffect.asymmetric函数的用法及展示效果。
TransitionEffect不配置animation情况下,用animateTo函数中的动画配置。

@Entry
@Component
struct TransitionEffectTest2 {
  // 我们利用条件渲染语句来实现组件的出现与消失
  @State isImageVisible: boolean = true
  @State buttonText: string = '展示'

  build() {
    Column() {
      Button(this.buttonText)
        .onClick(() => {
          //在animateTo中修改条件变量. 第二张图片的转场因为没有配置animation,而会采用此处的配置
          this.getUIContext().animateTo({ 
            duration: 500
          }, () => {
            this.isImageVisible = !this.isImageVisible
            this.buttonText = this.isImageVisible ? '展示' : '消失'
          })
        })

      if (this.isImageVisible) {
        Image($r('app.media.11073300'))
          .objectFit(ImageFit.ScaleDown)
          .width('100%')
          .margin({ top: 50 })//接下来重点,书写转场时的动效
          .transition(
            TransitionEffect.asymmetric(
              TransitionEffect.OPACITY.animation({ duration: 500 })
                .combine(TransitionEffect.rotate({ z: 1, angle: 180 })
                  .animation({ duration: 500 })), // 入场动效配置,不写animation,那就代表目前没有animation
              TransitionEffect.OPACITY.animation({ delay: 1000, duration: 500 })
                .combine(TransitionEffect.rotate({ z: 1, angle: 180 }).animation({ duration: 3000 })) //离场动效配置
            )
          )

        Image($r('app.media.11073300'))
          .objectFit(ImageFit.ScaleDown)
          .width('100%')
          .margin({ top: 50 })
          .transition(
            // 这里没有写animation函数,其实默认就是没动画.但是好在onClick中写了动画,
            // 会采用onclick中animateTo中的参数!!!!这点很重要!!!!一会儿专门讲讲这里面的门道.
            TransitionEffect.asymmetric(
              TransitionEffect.scale({ x: 0, y: 0 }),
              TransitionEffect.OPACITY
            )
          )
      }
    }
    .width('100%')
    .height('100%')
  }
}

效果展示:

父子组件同时设置Transition

下方代码为,父组件和子组件同时有转场动效的表现

@Entry
@Component
struct TransitionEffectTest3 {
  // 我们利用条件渲染语句来实现组件的出现与消失
  @State isImageVisible: boolean = true
  @State buttonText: string = '展示'

  build() {
    Column() {
      Button(this.buttonText)
        .onClick(() => {
          //在animateTo中修改条件变量. 第二张图片的转场因为没有配置animation,而会采用此处的配置
          this.isImageVisible = !this.isImageVisible
          this.buttonText = this.isImageVisible ? '展示' : '消失'
        })

      if (this.isImageVisible) {
        Column(){
          Image($r('app.media.11073300'))
            .objectFit(ImageFit.ScaleDown)
            .width('100%')
            .margin({ top: 50 })//接下来重点,书写转场时的动效
            .transition(
              TransitionEffect.asymmetric(
                TransitionEffect.OPACITY.animation({ duration: 500 })
                  .combine(TransitionEffect.rotate({ z: 1, angle: 180 })
                    .animation({ duration: 500 })), // 入场动效配置,不写animation,那就代表目前没有animation
                TransitionEffect.OPACITY.animation({ delay: 1000, duration: 500 })
                  .combine(TransitionEffect.rotate({ z: 1, angle: 180 }).animation({ duration: 3000 })) //离场动效配置
              )
            )

          Image($r('app.media.11073300'))
            .objectFit(ImageFit.ScaleDown)
            .width('100%')
            .margin({ top: 50 })
            .transition(
              // 这里没有写animation函数,其实默认就是没动画.但是好在onClick中写了动画,
              // 会采用onclick中animateTo中的参数!!!!这点很重要!!!!一会儿专门讲讲这里面的门道.
              TransitionEffect.asymmetric(
                TransitionEffect.scale({ x: 0, y: 0 }),
                TransitionEffect.OPACITY
              )
                .animation({duration: 1000})
            )

          Button('无组件转场的button')
            .width(200)
            .margin({top: 30})
        }
        .transition(TransitionEffect.opacity(0.5).animation({duration: 3000}), (b: boolean)=>{
          console.log((b? "展示" : "消失") + " 动效结束")
        })

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

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容