【HarmonyOS】动画—转场动效

文章目录

  • 一、转场动效
  • 二、转场动画
    • 1、出现/消失转场
      • [1.1 示例一](#1.1 示例一)
      • [1.2 示例二](#1.2 示例二)
    • 2、模态转场
      • [2.1 半模态组件(bindSheet)](#2.1 半模态组件(bindSheet))
        • [2.1.1 示例一](#2.1.1 示例一)
      • [2.2 菜单控制(bindMenu)](#2.2 菜单控制(bindMenu))
        • [2.2.1 示例一](#2.2.1 示例一)
      • [2.3 弹框(bindPopup)](#2.3 弹框(bindPopup))
        • [2.3.1 示例一](#2.3.1 示例一)
    • 3、共享元素转场 (一镜到底)
      • [3.1 不新建容器并直接变化原容器](#3.1 不新建容器并直接变化原容器)
      • [3.2 新建容器并跨容器迁移组件](#3.2 新建容器并跨容器迁移组件)
        • [3.2.1 结合Stack使用](#3.2.1 结合Stack使用)
        • [3.2.2 结合BindSheet使用](#3.2.2 结合BindSheet使用)
      • [3.3 使用geometryTransition共享元素转场](#3.3 使用geometryTransition共享元素转场)
    • 4、旋转屏动画
      • [4.1 布局切换的旋转屏动画](#4.1 布局切换的旋转屏动画)
      • [4.2 透明度变化的旋转屏动画](#4.2 透明度变化的旋转屏动画)

一、转场动效

衔接页面与元素间的过渡动画被称为转场动画,帮助用户理解界面及元素的逻辑关系。

1、层级关系

层级关系确定了转场动效的交互目的。

不同的转场设计会传递不同的交互层级隐喻,不合理的动效编排会让用户误解,从而导致用户主观感受下降,流畅体验感较差。

在设计转场动效时,首先要理解应用之间和页面元素之间的架构关系,再运用合适的设计手法完成动效设计表达。

层级关系又大致分为以下三类

同层级:转场后的页面或元素与当前的在同一层级上。(编辑,页签,横竖屏转场)

上下层级:转场后的页面或元素与当前的存在上下层级关系。(上下级页面,新建,解锁,搜索转场)

跨层级:适用于应用之间的跳转。(从一个应用转到其他应用)

2、运动编排

在界面变化中,将各个部分元素分类,协调相互运动。

场景解构:转场是由交互行为引起的界面变化,分析界面元素在过程中的意义,定义其在转场中所在的类型,并将它们进行分类。

  • 进场元素:转场中新出现的元素,一般是结果界面上的构成元素。
  • 出场元素:转场中消失的元素,一般是上一界面中的构成元素。
  • 持续元素:转场中持续存在的元素,整个过程无中断。
  • 静止元素:转场中无任何变化的元素。

3、一镜到底

通过共享元素的一种转场编排方式,有助于提升用户操作任务的效率,增强视觉的流畅感。

3.1 共享元素

共享元素一般是转场前后持续存在的界面元素,即上文提到的持续元素,是在转场发生后希望用户关注到的焦点元素,它增强了转场的连续感。

3.2 共享容器

当一组元素在过渡时包含明确的边界,可使用容器来让转换过程有连续感。通过共用容器将转场前后不同界面成组,一致的容器动画让过渡有连续感。

实现思路:

  1. 共用容器设计 :在转场前后的两个页面中,使用相同的容器组件(如ColumnRow或自定义Component),并保持容器的基础属性(如id、初始大小、圆角等)一致,作为 "共享容器" 的载体
  2. 容器属性补间动画 :通过animateToTransition组件,对容器的widthheightradiusbackgroundColor等属性设置过渡动画,实现大小、形状的平滑变化。
  3. 内部元素过渡 :容器内的元素可通过opacity(淡入淡出)或translate(位置平移)实现过渡,若有 "共享元素"(如图片、文本),可通过状态管理保持元素属性联动。
  4. 页面转场触发 :通过路由跳转(如router.pushUrl)或自定义组件切换时,同步触发容器和内部元素的动画,确保过渡连贯。

3.3 共享动势

无中间属性,无法通过补间变化(常规属性补间(如大小、颜色渐变)实现过渡)来实现柔和过渡,需要提取出可用的共享转换属性,来实现前后的平滑过渡。常用的共享运动属性有位移、缩放、旋转等。

实现思路:

  1. 提取共享运动属性 :确定转场前后元素的关键运动参数(如起始 / 结束位置 position、缩放比例 scale、旋转角度 rotate),这些参数将作为 "共享动势" 的衔接点。
  2. 运动轨迹关联:通过动画让元素从起始属性平滑过渡到结束属性,即使元素的其他属性(如内容、样式)完全不同,也能通过一致的运动轨迹让用户感知到 "连续性"。
  3. 状态同步与触发 :利用路由钩子(如页面切换时)或状态管理(如 AppStorage)传递起始属性,在目标页面加载时触发动画,确保运动轨迹无缝衔接。

4、淡入淡出

通过透明度变化来实现过渡转场,适用于无中间属性的组件或元素之间进行过渡变化。

4.1 单向淡入淡出

在前后层叠的场景中,只有前景元素进行淡入或淡出动作,下方元素(如背景)无变化。(如弹窗弹出 / 关闭、菜单展开 / 收起)。

4.2 交叉淡入淡出

存在出场元素与进场元素的场景下,根据元素图形特点进行淡入淡出效果处理。一般进出场元素样式一致或近似,可考虑使用同时进行透明度变化的方式;视觉样式上不同类型的进出场元素,为避免交叠状态带来视觉上的混乱,可适当错开出场元素淡出和进场元素淡入的时机。

  1. 同时交叉淡入淡出(适用于样式近似元素)

当进出场元素样式一致(如同一区域的图片切换),可同时执行淡出和淡入,形成 "无缝替换" 效果。

  1. 错时交叉淡入淡出(适用于样式差异大的元素)

当进出场元素样式差异大(如列表→详情页),可先让出场元素淡出,再让进场元素淡入,避免叠层时的视觉混乱。

二、转场动画

转场动画是指对将要出现或消失的组件做动画,对始终出现的组件做动画应使用属性动画。转场动画主要为了让开发者从繁重的消失节点管理中解放出来,如果用属性动画做组件转场,开发者需要在动画结束回调中删除组件节点。同时,由于动画结束前已经删除的组件节点可能会重新出现,还需要在结束回调中增加对节点状态的判断。

转场动画如下几类(本文):

  • 出现/消失转场:对新增、消失的控件实现动画效果,是通用的基础转场效果。
  • 导航转场:页面的路由转场方式,对应一个界面消失,另外一个界面出现的动画效果,如设置应用一级菜单切换到二级界面。
  • 模态转场:新的界面覆盖在旧的界面之上的动画,旧的界面不消失,新的界面出现,如弹框就是典型的模态转场动画。
  • 旋转屏动画:旋转屏动画主要分为两类:布局切换的旋转屏动画和透明度变化的旋转屏动画,旨在实现屏幕显示方向变化时的自然过渡。

1、出现/消失转场

transition是基础的组件转场接口,用于实现一个组件出现或者消失时的动画效果。可以通过TransitionEffect对象的组合使用,定义出各式效果。

部分接口示例:

转场效果 说明 动画
IDENTITY 禁用转场效果。 无。
OPACITY 默认的转场效果,透明度转场。 出现时透明度从0到1,消失时透明度从1到0。
SLIDE 滑动转场效果。 出现时从窗口左侧滑入,消失时从窗口右侧滑出。
translate 通过设置组件平移创建转场效果。 出现时为translate接口设置的值到默认值0,消失时为默认值0到translate接口设置的值。
rotate 通过设置组件旋转创建转场效果。 出现时为rotate接口设置的值到默认值0,消失时为默认值0到rotate接口设置的值。
opacity 通过设置透明度参数创建转场效果。 出现时为opacity设置的值到默认透明度1,消失时为默认透明度1到opacity设置的值。
combine 组合其他TransitionEffect。 组合其他TransitionEffect,一起生效。
animation 定义转场效果的动画参数:- 如果不定义会跟随animateTo的动画参数。- 不支持通过控件的animation接口配置动画参数。- TransitionEffect中animation的onFinish不生效。 调用顺序时从上往下,上面TransitionEffect的animation也会作用到下面TransitionEffect。

1.1 示例一

  1. 创建TransitionEffect。
typescript 复制代码
// 出现时会是所有转场效果的出现效果叠加,消失时会是所有消失转场效果的叠加
// 说明各个effect跟随的动画参数
private effect: object =
  TransitionEffect.OPACITY // 创建了透明度转场效果,这里没有调用animation接口,会跟随animateTo的动画参数
    // 通过combine方法,添加缩放转场效果,并指定了springMotion(0.6, 1.2)曲线
    .combine(TransitionEffect.scale({ x: 0, y: 0 }).animation({ curve: curves.springMotion(0.6, 1.2) }))
    // 添加旋转转场效果,这里的动画参数会跟随上面的TransitionEffect,也就是springMotion(0.6, 1.2)
    .combine(TransitionEffect.rotate({ angle: 90 }))
    // 添加平移转场效果,动画参数会跟随其之上带animation的TransitionEffect,也就是springMotion(0.6, 1.2)
    .combine(TransitionEffect.translate({ x: 150, y: 150 }))
    // 添加move转场效果,并指定了springMotion曲线
    .combine(TransitionEffect.move(TransitionEdge.END)).animation({curve: curves.springMotion()})
    // 添加非对称的转场效果,由于这里没有设置animation,会跟随上面的TransitionEffect的animation效果,也就是springMotion
    .combine(TransitionEffect.asymmetric(TransitionEffect.scale({ x: 0, y: 0 }), TransitionEffect.rotate({ angle: 90 })));
  1. 将转场效果通过transition接口设置到组件。
typescript 复制代码
Text('test')
  .transition(this.effect)
  1. 新增或者删除组件触发转场。
typescript 复制代码
@State isPresent: boolean = true;
// ...
if (this.isPresent) {
  Text('test')
    .transition(this.effect)
}
// ...
// 控制新增或者删除组件
// 方式一:将控制变量放到animateTo闭包内,未通过animation接口定义动画参数的TransitionEffect将跟随animateTo的动画参数
this.getUIContext()?.animateTo({ curve: curves.springMotion() }, () => {
  this.isPresent = false;
})

// 方式二:直接控制删除或者新增组件,动画参数由TransitionEffect的animation接口配置
this.isPresent = false;

所成动画如图所示。

1.2 示例二

对多个组件添加转场效果时,可以在animation动画参数中配置不同的delay值,实现组件渐次出现消失的效果:

typescript 复制代码
const ITEM_COUNTS = 9;
const ITEM_COLOR = '#ED6F21';
const INTERVAL = 30;
const DURATION = 300;

@Entry
@Component
struct Index1 {
  @State isGridShow: boolean = false;
  private dataArray: number[] = new Array(ITEM_COUNTS);

  aboutToAppear(): void {
    for (let i = 0; i < ITEM_COUNTS; i++) {
      this.dataArray[i] = i;
    }
  }

  build() {
    Stack() {
      if (this.isGridShow) {
        Grid() {
          ForEach(this.dataArray, (item: number, index: number) => {
            GridItem() {
              Stack() {
                Text((item + 1).toString())
              }
              .size({ width: 50, height: 50 })
              .backgroundColor(ITEM_COLOR)
              .transition(TransitionEffect.OPACITY
                .combine(TransitionEffect.scale({ x: 0.5, y: 0.5 }))// 对每个方格的转场添加delay,实现组件的渐次出现消失效果
                .animation({ duration: DURATION, curve: Curve.Friction, delay: INTERVAL * index }))
              .borderRadius(10)
            }
            .transition(TransitionEffect.opacity(0.99))
          }, (item: number) => item.toString())
        }
        ...
      }
    }
    ...
  }
}

2、模态转场

模态转场是新的界面覆盖在旧的界面上,旧的界面不消失的一种转场方式。

模态转场接口(本文)

接口 说明 使用场景
bindSheet 弹出半模态组件。 用于半模态展示界面,如分享框。
bindMenu 弹出菜单,点击组件后弹出。 需要Menu菜单的场景,如一般应用的"+"号键。
bindPopup 弹出Popup弹框。 Popup弹框场景,如点击后对某个组件进行临时说明。

2.1 半模态组件(bindSheet)

typescript 复制代码
bindSheet(isShow: boolean, builder: CustomBuilder, options?: SheetOptions): T
参数名 类型 必填 说明
isShow boolean 是否显示半模态页面。true:显示半模态页面。false:隐藏半模态页面。从API version 10开始,该参数支持$$双向绑定变量。从API version 18开始,该参数支持!!双向绑定变量。
builder CustomBuilder 配置半模态页面内容。
options SheetOptions 配置半模态页面的可选属性。

注意:

  1. 在非双向绑定情况下,以拖拽方式关闭半模态页面不会改变isShow参数的值。
  2. 为了使isShow参数值与半模态界面的状态同步,建议使用$$双向绑定isShow参数。
  3. 半模态页面的离场动效不支持打断,动效执行期间无法响应其他手势动作。目前离场动效使用弹簧曲线,该动画曲线存在视觉上并不明显的拖尾动画。因此,在半模态退出时,视觉上半模态页面已经消失,但此时动效可能还未结束,若想再次点击拉起半模态页面则不会响应。需要等动效完全结束后,才可以再次拉起。
2.1.1 示例一
typescript 复制代码
@Entry
@Component
struct SheetTransitionExample {
  @State isShow: boolean = false;

  @Builder
  myBuilder() {
    ...
  }

  build() {
    Column() {
      Button("transition modal 1")
        .onClick(() => {
          this.isShow = true;
        })
        .fontSize(20)
        .margin(10)
        .bindSheet($$this.isShow, this.myBuilder(), {
          detents: [SheetSize.MEDIUM, SheetSize.LARGE, 200],
          blurStyle: BlurStyle.Thick,
          showClose: true,
          title: { title: "title", subtitle: "subtitle" },
        })
    }
    ...
  }
}

2.2 菜单控制(bindMenu)

为组件绑定弹出式菜单,支持长按、点击或鼠标右键来触发菜单的弹出,菜单项以垂直列表形式显示。

参数名 类型 必填 说明
content Array<MenuElement> | CustomBuilder 配置菜单项图标和文本的数组,或者自定义组件。
options MenuOptions 配置弹出菜单的参数。

注意:

  1. 弹出菜单的文本内容不支持长按选中。
  2. 当窗口大小发生变化以及点击菜单内容区时,菜单自动隐藏。
  3. 菜单最大宽度受设备所占栅格限制,即使设置宽度100%,也不会占满屏幕。
  4. 菜单绑定的组件对象销毁时,菜单消失。
2.2.1 示例一
typescript 复制代码
@Entry
@Component
struct MenuExample {
  build() {
    Column() {
      Text('click for Menu')
        .bindMenu([
          {
            value: 'Menu1',
            action: () => {
              console.info('handle Menu1 select');
            }
          },
          {
            value: 'Menu2',
            action: () => {
              console.info('handle Menu2 select');
            }
          },
        ])
    }
    .width('100%')
    .margin({ top: 5 })
  }
}

2.3 弹框(bindPopup)

为组件绑定Popup气泡,并设置气泡内容、交互逻辑和显示状态。

参数名 类型 必填 说明
show boolean 气泡显示状态。Popup气泡必须等待页面全部构建完成才能展示,因此show不能在页面构建中设置为true,否则会导致Popup气泡显示位置及形状错误。该参数从API version 18开始支持!!语法双向绑定变量。true:弹出气泡;false:关闭气泡。默认值:false
popup PopupOptions | CustomPopupOptions 配置弹出气泡的参数。

注意:

  1. Popup气泡的显示状态在onStateChange事件回调中反馈,其显隐与组件的创建或销毁无强对应关系。
  2. Popup气泡的高度为当前窗口高度 - 上下安全区域高度(状态栏、导航条)- 80vp。
  3. 多个气泡同时弹出时,子窗内显示的气泡比主窗内显示的气泡层级高,所处窗口相同时,后面弹出的气泡层级比先弹出的气泡层级高。
  4. 2in1设备默认有双描边,其他设备默认无双描边。
  5. 子窗弹窗里不能再弹出子窗弹窗,例如bindPopup设置了showInSubWindowtrue时,则不能再弹出另一个设置了showInSubWindowtrue的弹窗。
2.3.1 示例一
typescript 复制代码
// xxx.ets

@Entry
@Component
struct PopupExample {
  @State customPopup: boolean = false;
  @State handlePopup: boolean = false;

  build() {
    Column({ space: 100 }) {
      Button("popup")
        .margin({ top: 50 })
        .onClick(() => {
          this.customPopup = !this.customPopup;
        })
        .bindPopup(this.customPopup, {
          message: "this is a popup",
          arrowHeight: 20, // 设置气泡箭头高度
          arrowWidth: 20, // 设置气泡箭头宽度
          radius: 20, // 设置气泡的圆角
          shadow: ShadowStyle.OUTER_DEFAULT_XS, // 设置气泡的阴影
        })

      Button('PopupOptions')
        .onClick(() => {
          this.handlePopup = !this.handlePopup;
        })
        .bindPopup(this.handlePopup, {
          width: 300,
          message: 'This is a popup with PopupOptions',
          arrowPointPosition: ArrowPointPosition.START, // 设置箭头的位置
          backgroundBlurStyle: BlurStyle.NONE, // 关闭气泡的模糊背景
          popupColor: Color.Red, // 设置气泡的背景色
          autoCancel: true,
        })
    }
    .width('100%')
  }
}

3、共享元素转场 (一镜到底)

共享元素转场是一种界面切换时对相同或者相似的两个元素做的一种位置和大小匹配的过渡动画效果,也称一镜到底动效。

在点击图片后,该图片消失,同时在另一个位置出现新的图片,二者之间内容相同,可以对它们添加一镜到底动效。上图为不添加一镜到底动效的效果,下图为添加一镜到底动效的效果,一镜到底的效果能够让二者的出现消失产生联动,使得内容切换过程显得灵动自然而不生硬。

一镜到底的动效的实现方式:

一镜到底实现方式 特点 适用场景
不新建容器直接变化原容器 不发生路由跳转,需要在一个组件中实现展开及关闭两种状态的布局,展开后组件层级不变。 适用于转场开销小的简单场景,如点开页面无需加载大量数据及组件。
新建容器并跨容器迁移组件 通过使用NodeController,将组件从一个容器迁移到另一个容器,在开始迁移时,需要根据前后两个布局的位置大小等信息对组件添加位移及缩放,确保迁移开始时组件能够对齐初始布局,避免出现视觉上的跳变现象。之后再添加动画将位移及缩放等属性复位,实现组件从初始布局到目标布局的一镜到底过渡效果。 适用于新建对象开销大的场景,如视频直播组件点击转为全屏等。
使用geometryTransition共享元素转场 利用系统能力,转场前后两个组件调用geometryTransition接口绑定同一id,同时将转场逻辑置于animateTo动画闭包内,这样系统侧会自动为二者添加一镜到底的过渡效果。 系统将调整绑定的两个组件的宽高及位置至相同值,并切换二者的透明度,以实现一镜到底过渡效果。因此,为了实现流畅的动画效果,需要确保对绑定geometryTransition的节点添加宽高动画不会有跳变。此方式适用于创建新节点开销小的场景。

3.1 不新建容器并直接变化原容器

该方法不新建容器,通过在已有容器上增删组件触发transition,搭配组件属性动画实现一镜到底效果。

对于同一个容器展开,容器内兄弟组件消失或者出现的场景,可通过对同一个容器展开前后进行宽高位置变化并配置属性动画,对兄弟组件配置出现消失转场动画实现一镜到底效果。基本步骤为:

  1. 构建需要展开的页面,并通过状态变量构建好普通状态和展开状态的界面。
  2. 将需要展开的页面展开,通过状态变量控制兄弟组件消失或出现,并通过绑定出现消失转场实现兄弟组件转场效果。

3.2 新建容器并跨容器迁移组件

通过NodeContainer自定义占位节点,利用NodeController实现组件的跨节点迁移,配合属性动画给组件的迁移过程赋予一镜到底效果。这种一镜到底的实现方式可以结合多种转场方式使用,如导航转场(Navigation)、半模态转场(bindSheet)等。

3.2.1 结合Stack使用

可以利用Stack内后定义组件在最上方的特性控制组件在跨节点迁移后位z序最高,以展开收起卡片的场景为例,实现步骤为:

  • 展开卡片时,获取节点A的位置信息,将其中的组件迁移到与节点A位置一致的节点B处,节点B的层级高于节点A。
  • 对节点B添加属性动画,使之展开并运动到展开后的位置,完成一镜到底的动画效果。
  • 收起卡片时,对节点B添加属性动画,使之收起并运动到收起时的位置,即节点A的位置,实现一镜到底的动画效果。
  • 在动画结束时利用回调将节点B中的组件迁移回节点A处。
3.2.2 结合BindSheet使用

想实现半模态转场(bindSheet)的同时,组件从初始界面做一镜到底动画到半模态页面的效果,可以使用这样的设计思路。将SheetOptions中的mode设置为SheetMode.EMBEDDED,该模式下新起的页面可以覆盖在半模态弹窗上,页面返回后该半模态依旧存在,半模态面板内容不丢失。在半模态转场的同时设置一全模态转场(bindContentCover)页面无转场出现,该页面仅有需要做共享元素转场的组件,通过属性动画,展示组件从初始界面至半模态页面的一镜到底动效,并在动画结束时关闭页面,并将该组件迁移至半模态页面。

以点击图片展开半模态页的场景为例,实现步骤为:

  • 在初始界面挂载半模态转场和全模态转场两个页面,半模态页按需布局,全模态页面仅放置一镜到底动效需要的组件,抓取布局信息,使其初始位置为初始界面图片的位置。点击初始界面图片时,同时触发半模态和全模态页面出现,因设置为SheetMode.EMBEDDED模式,此时全模态页面层级最高。
  • 设置不可见的占位图片置于半模态页上,作为一镜到底动效结束时图片的终止位置。利用布局回调监听该占位图片布局完成的时候,此时执行回调抓取占位图片的位置信息,随后全模态页面上的图片利用属性动画开始进行共享元素转场。
  • 全模态页面的动画结束时触发结束回调,关闭全模态页面,将共享元素图片的节点迁移至半模态页面,替换占位图片。
  • 需注意,半模态页面的弹起高度不同,其页面起始位置也有所不同,而全模态则是全屏显示,两者存在一高度差,做一镜到底动画时,需要计算差值并进行修正,具体可见demo。
  • 还可以配合一镜到底动画,给初始界面图片也增加一个从透明到出现的动画,使得动效更为流畅。

3.3 使用geometryTransition共享元素转场

geometryTransition用于组件内隐式共享元素转场,在视图状态切换过程中提供丝滑的上下文继承过渡体验。

geometryTransition的使用方式为对需要添加一镜到底动效的两个组件使用geometryTransition接口绑定同一id,这样在其中一个组件消失同时另一个组件创建出现的时候,系统会对二者添加一镜到底动效。

geometryTransition绑定两个对象的实现方式使得geometryTransition区别于其他方法,最适合用于两个不同对象之间完成一镜到底。

4、旋转屏动画

旋转屏动画主要分为两类:布局切换的旋转屏动画和透明度变化的旋转屏动画,旨在实现屏幕显示方向变化时的自然过渡。布局切换的旋转屏动画实现较为简便,例如在module.json5中配置自动旋转(或设置窗口显示方向)即可实现。而透明度变化的旋转屏动画则需在module.json5配置的基础上,预备两套视图,在屏幕旋转时,通过视图切换,使消失的视图呈现渐隐效果,新出现的视图则渐显,从而营造流畅的视觉体验。

4.1 布局切换的旋转屏动画

布局切换时的旋转屏动画,是在屏幕显示方向改变时,为窗口与应用视图同步旋转而设计的大小和位置过渡动画。这种布局切换的旋转屏动画是系统默认的,便于开发者实现。当屏幕显示方向变化时,系统会生成窗口旋转动画,并自动调整窗口大小以匹配旋转后的尺寸。在此过程中,窗口会通知对应的应用,要求其根据新的窗口大小重新布局,产生与窗口旋转动画参数相同的布局动画。

切换屏幕方向即可实现布局切换的旋转屏动画效果。

typescript 复制代码
@Entry
@Component
struct rotation {

  build() {
    Stack() {
      Image($r('app.media.tree'))
        .position({ x: 0, y: 0 })
        .size({ width: 100, height: 100 })
        .id('image1')
    }
    .backgroundColor(Color.White)
    .size({ width: '100%', height: '100%' })
  }
}

需要在项目的module.json5文件中的abilities列表里添加"orientation",指定为"auto_rotation"。

json 复制代码
"orientation": "auto_rotation",

4.2 透明度变化的旋转屏动画

透明度变化的旋转屏动画在屏幕显示方向变化时启用,当窗口进行旋转动画时,为旋转过程中新增或删除的组件添加默认透明度转场,以实现组件的优雅出现和消失。此功能通过监听窗口旋转事件,在事件中切换组件的视图效果,如果消失视图的根节点和新出现视图的根节点未设置转场效果,会为其自动添加默认透明度转场(即TransitionEffect.OPACITY),展现出透明度的渐隐和渐显效果。

typescript 复制代码
// xx.ets
import { display } from '@kit.ArkUI';

@Entry
@Component
struct rotation {

  // 获取通过监听窗口的windowsSizeChange事件得到的屏幕显示方向
  @StorageLink('orientation') myOrientation: display.Orientation = display.Orientation.PORTRAIT;

  build() {
    Stack() {

      // 当屏幕显示方向变化时,切换组件的视图效果
      if (this.myOrientation == display.Orientation.PORTRAIT || this.myOrientation == display.Orientation.PORTRAIT_INVERTED) {
        Image($r('app.media.sky'))
          .size({ width: 100, height: 100 })
          .id('image1')

          // 开发者也可以通过自行设置transition的TransitionEffect.OPACITY转场效果来实现旋转屏动画的透明度变化
          // .transition(TransitionEffect.OPACITY)
      } else {
        Image($r('app.media.tree'))
          .position({ x: 0, y: 0 })
          .size({ width: 200, height: 200 })
          .id('image2')
          
          // 开发者也可以通过自行设置transition的TransitionEffect.OPACITY来实现旋转屏动画的透明度变化
          // .transition(TransitionEffect.OPACITY)
      }
    }
    .backgroundColor(Color.White)
    .size({ width: '100%', height: '100%' })
  }
}

监听窗口旋转的同步事件windowsSizeChange来实现视图的切换。例如可在EntryAbility.ets文件的onWindowStageCreate方法中添加处理逻辑以获取屏幕的显示方向。

typescript 复制代码
onWindowStageCreate(windowStage: window.WindowStage): void {

    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    let mainWindow: window.Window;
    try {
      mainWindow = windowStage.getMainWindowSync();
      let displayClass: display.Display = display.getDefaultDisplaySync();
      AppStorage.setOrCreate('orientation', displayClass.orientation);
      // 监听窗口的windowsSizeChange事件,旋转屏时会触发该事件
      mainWindow.on('windowSizeChange', (data) => {
        console.info('Succeeded in enabling the listener for window size changes. Data: ' + JSON.stringify(data));
        let displayClass: display.Display | null = null;
        try {
          displayClass = display.getDefaultDisplaySync();
          console.info('display orientation is ' + JSON.stringify(displayClass.orientation));
          // 获取屏幕的显示方向
          AppStorage.set('orientation', displayClass.orientation);
        } catch {
          return;
        }
      })
    } catch {
      hilog.error(0x0000, 'testTag', '%{public}s', 'error');
      return;
    }

    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
    });
}

需要在项目的 module.json5 文件中的 abilities 列表里添加 "orientation",指定为 "auto_rotation"。

json 复制代码
"orientation": "auto_rotation",
相关推荐
cooldream20096 小时前
项目实战复盘:基于仓颉语言的鸿蒙智能导航助手(HarmonyNav)
华为·harmonyos·仓颉
爱笑的眼睛116 小时前
HarmonyOS ScrollBar深度定制:超越系统默认的滚动体验
华为·harmonyos
爱笑的眼睛116 小时前
HarmonyOS 文件管理Kit 的应用场景深度解析
华为·harmonyos
周倦岚6 小时前
【HarmonyOS】GC垃圾回收
harmonyos
HarmonyOS_SDK6 小时前
【FAQ】HarmonyOS SDK 闭源开放能力 — AppGallery Kit
harmonyos
SWUT胖虎6 小时前
ArkTS 自定义组件与 @Builder 区别总结
harmonyos·arkts·鸿蒙
SWUT胖虎6 小时前
ArkTS 中 @State 底层原理详解
java·list·harmonyos·鸿蒙
北风江畔(LuckyClover)7 小时前
鸿蒙应用开发(第一章:快速体验)
华为·harmonyos
大雷神8 小时前
HarmonyOS Canvas开发指南
harmonyos