HarmonyOS 7从 2D 到 3D:空间计算带来的到底是什么范式变革
HarmonyOS 7(API 26)里,空间计算第一次被单独拎出来当作一个板块,而不是塞在图形或媒体能力里顺手提一句。很多人第一反应是"UI 能转起来了"“能做 3D 卡片了”,但如果只把它当成视觉增强,就会漏掉真正难的部分:我们过去十年熟练的这套 2D 设计语言,在 Z 轴出现以后,有一半的默认假设都不成立了。
这篇文章想聊清楚两件事:系统级能力到底把哪些门槛抹平了,以及为什么"认知门槛"和"设计门槛"还在原地等着你。
系统把技术门槛按了下去,但没按掉认知门槛
先看系统这次到底给了什么。以前要在 App 里做真实的 3D 效果,你得自己引入渲染引擎、自己算透视矩阵、自己处理光照模型。ArkUI 现在把这些能力做进了声明式组件里:
- 图形变换:
rotate(带x/y/z旋转轴)、translate(支持z)、scale、perspective(视距)在一套属性链里就能组合出三维效果; - 真正的三维变换:从 API 20 起提供
transform3D,可以直接喂一个 4x4 矩阵,处理带透视的变换; - 场景级 3D:ArkGraphics 3D 通过
Component3D渲染 glTF 模型,相机、光源、材质都在 ArkTS 侧管理; - 端侧重建:Spatial Recon Kit 支持加载 3DGS 模型(MP4 / PLY / GLB);
- 空间音频:AudioKit 的
AudioSpatializationManager可以查询和订阅设备的空间音频渲染状态。
矩阵推导、投影、着色这些过去劝退设计师和大部分应用开发者的东西,现在被封装成了属性。这是"技术门槛下降"的真实含义。
但门槛下降的是"实现",不是"判断"。系统不会告诉你一个按钮该浮在前景还是沉到背景,不会告诉你把导航放到用户左侧 30 度意味着什么,也不会告诉你某个动效会不会让人头晕。能不能跑是工程问题,该不该这么跑是设计问题——后者才是空间计算真正的分水岭。
从 X/Y 到 X/Y/Z,改的不是多一个轴
把 2D 界面往 Z 轴一推,很多人以为只是"多了个维度"。实际变动的是整套坐标语义。
在 2D 里,屏幕的 X/Y 是位置,深度没有概念。控件要么在,要么不在;层叠靠zIndex做遮挡排序,那只是绘制顺序,用户感知不到"远近"。
到了空间里,Z 轴同时承担了四种含义:
- 遮挡关系——近的挡住远的,这是最表层的物理直觉;
- 距离感——元素离观察者多远,直接对应"重要程度"和"可交互程度";
- 信息层级——前景是主任务,中景是上下文,背景是环境;
- 注意力资源——人眼在空间中一次只能聚焦一个焦平面,深度天然就是注意力分配器。
也就是说,Z 轴不再是渲染参数,而是一种信息组织手段。这是范式变革里最需要先扭转的一点:平面设计里的"上/下、左/右"对应的是阅读顺序,空间设计里的"近/远"对应的是认知优先级。
同一张卡片,2D 和空间两种写法
下面用一个商品卡片做对照。2D 版本是常见的平铺卡片;空间版本把同一张卡片放进一个有纵深的 Stack 里,用perspective和z位移拉开层次。
import{curves}from'@kit.ArkUI';@Entry@Componentstruct CardParadigmDemo{@Stateprivatespatial:boolean=false;@BuilderProductCard(title:string,price:string){Column({space:6}){// 占位图,实际项目替换为 ImageRow().width('100%').aspectRatio(1).backgroundColor('#E8EDF5').borderRadius(12)Text(title).fontSize(15).fontWeight(FontWeight.Medium).maxLines(1)Text(price).fontSize(13).fontColor('#E85D3D')}.padding(10).width(150).backgroundColor(Color.White).borderRadius(16).shadow({radius:12,color:'#14000000',offsetY:4})}build(){Column(){// Stack 是空间感的起点:子组件在这里共享同一块画布Stack({alignContent:Alignment.Center}){this.ProductCard('旧旅行箱','¥429').zIndex(0).translate({x:this.spatial?-70:-50,y:0,z:this.spatial?-60:0}).rotate({x:0,y:1,angle:this.spatial?-18:0,perspective:900}).opacity(this.spatial?0.75:1)this.ProductCard('主推商品','¥199').zIndex(2).translate({x:0,y:this.spatial?6:0,z:this.spatial?20:0}).rotate({x:0,y:1,angle:this.spatial?-4:0,perspective:900}).scale({x:this.spatial?1.12:1,y:this.spatial?1.12:1})this.ProductCard('新款上市','¥359').zIndex(1).translate({x:this.spatial?70:50,y:0,z:this.spatial?-60:0}).rotate({x:0,y:1,angle:this.spatial?16:0,perspective:900}).opacity(this.spatial?0.75:1)}.width('100%').height(320)Button(this.spatial?'回到平面':'进入空间').margin({top:24}).onClick(()=>{// 一次性把三张卡片的位移/旋转/缩放补间到目标状态this.getUIContext()?.animateTo({duration:400,curve:curves.springMotion(0.8,20)},()=>{this.spatial=!this.spatial;});})}.width('100%').height('100%').justifyContent(FlexAlign.Center)}}这段代码里,spatial这个布尔量切换的不是"有没有动画",而是三张卡片在 Z 轴上的相对关系:主推商品被拉到z: 20并放大,两侧商品退到z: -60并降低不透明度。perspective: 900提供视距——值越小透视越夸张,值越大越接近正交投影。想深入的话,perspective和centerZ组合还能做出"绕自己所在平面翻转"的效果。
这个例子里,真正决定信息层级的不是颜色也不是字号,而是 z 位置的相对排序。这就是空间 UI 和 2D UI 最本质的区别。
2D UI 与空间 UI 的设计要素对照
把设计要素拆开看,差异更直观:
| 设计要素 | 2D 平面 UI | 空间 UI | 变化实质 |
|---|---|---|---|
| 信息呈现 | 靠网格、留白、分区组织,信息量受单屏面积限制 | 靠距离、遮挡、景深组织,信息可分层堆叠在纵深上 | 从"平面排布"变成"纵深编排" |
| 导航 | 固定坐标系,页面跳转即视角切换 | 视角本身可移动,导航可以是"走过去"或"拉近看" | 从"切换页面"变成"移动观察点" |
| 反馈 | 颜色变化、缩放、位移,幅度小且以像素为单位 | 可叠加深度弹出、材质高光、空间音效、轻微透视偏移 | 反馈通道从 1 个扩到 3 个以上 |
| 状态 | 选中/禁用/加载用样式区分 | 除样式外,还需区分"在视野内/外"“可触及/过远” | 状态维度与用户姿态相关 |
| 层级 | zIndex只决定绘制顺序 | Z 位置决定真实远近,参与遮挡与透视 | 层级从"视觉排序"变成"空间事实" |
| 交互输入 | 点击、滑动、长按(触点即目标) | 视线、手势、头部姿态协同,目标需要"选中"过程 | 输入从"直接点"变成"先瞄准再动作" |
这张表里最容易被低估的是最后两列。层级和交互输入这两栏,直接决定了 2D 那套"所见即所点"的直觉不能照搬。
案例:设置中心从列表变成分层空间
拿最常见的设置中心举例。2D 版本是一列设置项,用户滚动、点进去、返回。空间化之后,可以让"一级入口"作为前景层平铺,"二级详情"沉到中景,环境/预览作为背景层。
两条路径的差别不只是动效。2D 路径里,返回是"撤销一次操作";空间路径里,返回是"退后一步"。后者更接近人在物理空间里的直觉,代价是用户必须始终知道自己在哪一层——这正是设计门槛,系统替不了你。
再往前一步,如果这个设置中心接入 3DGS 场景(比如相机类 App 让用户在实景空间里调整参数),设置项就不再是列表,而是悬浮在环境里的锚点。Spatial Recon Kit 加载 3DGS 模型后,配合 ArkGraphics 3D 的相机和光源,锚点会随视角变化产生合理的透视关系。这类场景里,设置项的空间位置本身就是语义:贴近实物的参数(比如滤镜强度)就该靠近那件实物。
几条小经验
- 先问"Z 轴在这个页面承担什么职责",再动手写代码。如果答案只是"好看",大概率不该上 3D。
- 把深度当层级用,别当装饰用。让用户在纵深里找到方向,比让元素飘起来更有价值。
- 2D 到 3D 的第一步通常是"分层的 Stack",不是"加载一个 3D 模型"。绝大多数页面用层叠 + 透视就够了。
- 透视值(
perspective)要统一。同屏元素用不同视距,会让人眼判断不了它们的远近关系。 - 动效时间控制在 300~500ms。空间位移过大又太慢,比平面动画更容易引发不适。
注意一下下哦
- 把
zIndex当成 Z 轴。zIndex只影响绘制顺序,不产生透视和遮挡的物理感。要真实纵深,得用translate的z配合perspective。 rotate和scale的锚点打架。两个属性都设centerX/centerY时,以属性链里后设置的为准。写链式调用时别想当然。- 透视中心默认在组件自身。做整屏空间感时,往往需要把旋转锚点对齐到屏幕中心或观察者位置,否则所有元素各转各的。
- 忽略可触及范围。平面里屏幕边缘还能点,空间里"远处"的控件既难点中也不该高频使用。
- 只做视觉不做状态。元素进出视野、被遮挡、过远这些状态如果没设计,用户会以为自己操作失败了。
空间计算的门槛,一半在"怎么写",另一半在"为什么这么写"。系统把前一半按低了,后一半还得靠自己。
#鸿蒙 #HarmonyOS 7(API 26)