请添加图片描述

前言:UI 升维,从“平面铺陈”到“空间折叠”

在移动应用开发的漫长历史中,我们习惯了将 UI 元素平铺在 X 轴和 Y 轴构成的二维平面上。然而,随着 HarmonyOS NEXT 以及折叠屏、AR/VR 等新硬件形态的爆发,“空间化设计 (Spatial Design)” 已经成为顶尖 App 拉开体验差距的核心分水岭。

在空间化 UI 中,元素不再仅仅是屏幕上的色块,它们拥有了真实的物理厚度、悬浮层级(Elevation)、光影投射以及 3D 视差。引入 Z 轴后,我们可以通过视觉纵深来引导用户的注意力,打造极其高级的“微拟物”交互体验。

本文将基于一份涵盖了 6 大核心空间布局场景的 ArkUI 3.0 源码,带您彻底吃透 ArkUI 中的 Z轴层叠景深 (Perspective)3D旋转浮动层级空间联动动效。建议先收藏,再慢慢品读!


🏗️ 一、 Z 轴层叠布局 (ZStackLayout):用阴影与偏移雕刻空间高度

在现实世界中,叠在一起的纸牌之所以能被看出先后顺序,是因为光线产生的阴影以及视角的错位

1.1 核心源码拆解

Stack() {
  // Z=3:最底层(最先声明)
  Column() { Text('Z=3') }
  .translate({ x: 12, y: 20, z: 0 }) // 向右下方偏移最多
  .shadow({ radius: 24, color: '#EF535040', offsetX: 0, offsetY: 12 })

  // Z=2:中间层
  Column() { Text('Z=2') }
  .translate({ x: 6, y: 10, z: 0 })
  .shadow({ radius: 16, color: '#AB47BC40', offsetX: 0, offsetY: 8 })

  // Z=1:最顶层(最后声明)
  Column() { Text('Z=1(顶层)') }
  .translate({ x: 0, y: 0, z: 0 }) // 无偏移,定海神针
  .shadow({ radius: 8, color: '#5C6BC040', offsetX: 0, offsetY: 4 })
}

1.2 空间构建哲学与 API 解析

  • 渲染顺序即 Z 轴高度:在 ArkUI 的 Stack 容器中,代码的声明顺序决定了层级。先声明的在底层,后声明的覆盖在其上。
  • translate 的视觉欺骗:顶层卡片保持在原点 (0,0),而底层卡片故意向右下方(x: 12, y: 20)偏移。这利用了透视错位,让大脑脑补出卡片是“斜向堆叠”的。
  • 物理真实的 shadow 映射
  • 顶层卡片:离底面最近,因此阴影最紧实(radius: 8),偏移量最小(offsetY: 4)。
  • 底层卡片:虽然在 DOM 树底,但通过偏移漏出来的部分,在视觉隐喻中距离光源最远,因此它的阴影必须最大、最弥散(radius: 24offsetY: 12)。

在这里插入图片描述

🌀 二、 3D 视角卡片网格 (PerspectiveGrid):打破屏幕的物理边界

在游戏商城、电影海报展示墙中,卡片以统一的角度向内侧倾斜,能营造出极强的画廊沉浸感。

2.1 核心源码拆解

Row() {
  // 左侧卡片:向内侧旋转
  Column() { Text('3D') }
  .rotate({ x: 0, y: 1, z: 0, angle: 20, perspective: 800 })
  .shadow({ radius: 10, color: '#5C6BC060', offsetX: 4, offsetY: 6 })

  // 中间卡片:原始平面(作为对比锚点)
  // ...

  // 右侧卡片:向内侧反向旋转
  Column() { Text('3D') }
  .rotate({ x: 0, y: 1, z: 0, angle: -20, perspective: 800 })
  .shadow({ radius: 10, color: '#7E57C260', offsetX: -4, offsetY: 6 })
}

2.2 技术深度解析

  • 旋转轴的选择x: 0, y: 1, z: 0 表示卡片完全绕着垂直中心轴(Y 轴)旋转,就像推开一扇门。
  • **内向透视与 perspective**:左侧卡片正向旋转 20°(右边缘向内陷入屏幕),右侧卡片反向旋转 -20°(左边缘陷入屏幕)。最关键的是 perspective: 800 参数,它定义了“虚拟相机”距离屏幕的像素距离。没有这个参数,旋转看起来只是变窄的 2D 形变;加上它,就能产生近大远小的 3D 空间畸变。
  • 光影的相对性:左侧卡片的阴影向右偏(offsetX: 4),右侧阴影向左偏(offsetX: -4)。这种对全局统一中心光源的模拟,是高级 UI 不可或缺的质感来源。

在这里插入图片描述

☁️ 三、 浮动面板 (FloatingPanel):Elevation 与弹出层级设计

在复杂的表单或地图应用中,往往有多个浮动面板相互覆盖。如何让用户一眼看出谁在上谁在下?

3.1 核心源码拆解

Stack() {
  // 底层:内容页
  Column() { /* 主内容 */ }.backgroundColor('#E8EAF6')

  // 中层面板 (Elevation 2)
  Column() { Text('中层面板') }
  .translate({ x: 0, y: -20, z: 0 })
  .shadow({ radius: 12, color: '#FF704040', offsetX: 0, offsetY: 6 })

  // 顶层面板 (Elevation 4)
  Column() { Text('顶层面板') }
  .translate({ x: -50, y: -50, z: 0 })
  .shadow({ radius: 20, color: '#5C6BC050', offsetX: 0, offsetY: 10 })
}

3.2 鸿蒙 UI 层级规范指南

这部分代码极其精准地复刻了 Material Design 与 HarmonyOS 的 Elevation(海拔)系统
组件的 Z 轴高度不仅由 Stack 决定,更由 shadow 决定。面板悬浮得越高,意味着它对用户的优先级越高(如警告弹窗、浮动操作按钮 FAB)。

  • 中层面板:阴影半径 12,投射距离 6
  • 顶层面板:阴影半径激增到 20,投射距离达 10
    通过这种极其规范的光影递增法则,大脑会瞬间建立起清晰的界面优先级 Z 轴树。

在这里插入图片描述

👆 四、 空间标签栏 (SpatialTab):状态驱动的 3D 微交互

传统的 Tab 切换只是文字变色,而空间化的 Tab 切换,按键会像真实的物理键盘一样发生形态偏转。

4.1 核心源码拆解

@State active: number = 0 // 维护当前选中的索引

ForEach(['首页', '发现', '我的'], (label: string, i: number) => {
  Column() { Text(label) }
  .shadow({
    radius: i === this.active ? 12 : 0,           // 选中时产生厚重的阴影
    offsetY: i === this.active ? 6 : 0
  })
  .rotate({
    x: 0, y: i === this.active ? 1 : 0, z: 0, 
    angle: i === this.active ? 5 : 0,             // 选中时微微侧转 5度
    perspective: 500
  })
  .animation({ duration: 300, curve: Curve.FastOutSlowIn }) // 动画缓动
  .onClick(() => { this.active = i })
})

4.2 动效设计逻辑

这就是 “状态驱动 UI” 理念的巅峰体现。
我们只需要维护一个 active 状态。当用户点击时:

  1. shadow 瞬间从无变成有,仿佛组件从屏幕中“脱壳而出”。
  2. rotate 角度从 0 变为 5 度,产生微弱的 3D 翘起效果。
  3. 配合 .animation(Curve.FastOutSlowIn),底层的 ArkUI 引擎会自动计算属性差值,并在一帧帧中插入平滑的过渡,完美迎合用户手指按压的物理反馈。

在这里插入图片描述

🎠 五、 立体卡片画廊 (CardGallery):多属性构建曲面阵列

当需要展示一系列精美的特权卡或角色立绘时,平面滑动太单调。画廊模式(Gallery)通过缩放和角度的配合,创造了一个虚拟的圆柱形展台。

5.1 核心源码拆解

@Component
struct GalleryCard {
  private angle: number = 0
  private scale_: number = 1
  // ...
  build() {
    Column() { Text(this.label) }
    // 1. 根据传入角度产生 3D 旋转
    .rotate({ x: 0, y: 1, z: 0, angle: this.angle, perspective: 600 })
    // 2. 两侧卡片缩小,中间卡片保持 1.0
    .scale({ x: this.scale_, y: this.scale_ })
    // 3. 动态阴影计算魔法
    .shadow({
      radius: 8 + Math.abs(this.angle) * 0.5, // 角度越大,阴影越散
      offsetX: this.angle > 0 ? 4 : -4,       // 根据偏转方向决定阴影偏向
      offsetY: 8
    })
  }
}

5.2 组合魔法揭秘

CardGallery 父组件中,调用了三张卡片,角度分别是 -15015,缩放分别是 0.8510.85

  • scalerotate 的共舞:中间卡片最大且平正,视觉焦点最集中。两侧卡片变小且向内旋转,模拟了它们“处于更远的空间位置”。
  • 动态阴影的精妙算法:源码中巧妙使用了 Math.abs(this.angle) * 0.5。卡片偏转角度越大,意味着离背景墙越远,计算出的阴影半径就越大。这种通过数学公式强绑定的属性联动,是高级组件开发的必修课。

在这里插入图片描述

📚 六、 景深列表 (DepthList):突破绝对定位的 3D 排版

普通的 List 组件是从上往下排列的。而在某些特定的设计中(如 iOS 的通知堆叠、卡包),列表项是从远到近、从上到下像阶梯一样铺开的。

6.1 核心源码拆解

// 数据源定义了 Z 轴、阴影和 Y 轴偏移的递增关系
ForEach([
  { label: '第四项(最深)', color: '#EF5350', shadowR: 20, yOff: 30 },
  { label: '第三项',       color: '#FF7043', shadowR: 16, yOff: 20 },
  // ...
], (item: DepthListItemInterface, index: number) => {
  Column() { Text(item.label) }
  // 1. 位置计算:X 轴产生错位,Y 轴由配置决定
  .translate({ x: index * 4, y: item.yOff, z: 0 })
  // 2. 阴影计算:越深的项阴影越大
  .shadow({ radius: item.shadowR, offsetX: 0, offsetY: item.shadowR * 0.5 })
  // 3. 绝对定位:脱离常规文档流,使所有卡片重叠在一处
  .position({ x: 10, y: 0 })
})

6.2 布局解析与应用

  • 脱离文档流:使用 .position() 使得所有卡片在起始状态下重叠在左上角,这是构建层叠效果的前提。
  • 多维数据驱动:通过 index * 4 让 X 轴产生微小的右移,配合 yOff 的垂直错位,在二维屏幕上模拟出了三维空间中的“Z 轴景深”。最下方的卡片反而看起来离用户最远,非常具有视觉冲击力。

在这里插入图片描述

🎯 总结与避坑指南 (Best Practices)

掌握了上述 6 大场景,你几乎已经通关了 ArkUI 的空间化布局体系。为了便于实际业务开发,特总结以下性能优化原则:

  1. 避免重绘风暴 (Overdraw):如果一个 Stack 内有数十张卡片堆叠,且都带有复杂的 .shadow()。不要让底层完全不可见的卡片继续渲染,请动态给它们加上 visibility(Visibility.Hidden)
  2. 巧用离屏渲染加速动画:当复杂的 3D 旋转卡片(如场景五)内嵌了大量图片和文本时,务必在卡片最外层容器挂载 .renderGroup(true)。系统会将其预渲染为一张纹理,极大地降低 GPU 每一帧的计算负担,保证 60/120 FPS 的丝滑体验。
  3. 慎重设置 Perspective:过小的透视值(如 < 300)会导致严重的画面畸变甚至 Z 轴穿模闪烁。常规 UI 动效中,perspective 保持在 500 ~ 1200 之间是最符合人类肉眼直觉的。

空间化设计是下一代操作系统的核心语境。学会用 rotatetranslateshadow 在代码中折叠空间,你就能打造出超越时代的惊艳应用。

如果这篇万字硬核实战拆解对您有帮助,恳请点赞、收藏

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐