ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

【共创稿事节】HarmonyOS 7深度即层级:用 Z 轴组织信息的交互设计

2026/10/2 13:59:08 拓冰建站 浏览量
【共创稿事节】HarmonyOS 7深度即层级:用 Z 轴组织信息的交互设计 2D 界面表达层级靠的是位置和大小重要的放上面、放大、加粗。空间界面多了一个可用的维度——深度。用户天生会用远近理解重要程度前景的东西就是此刻该看的背景的东西是环境。把 Z 轴当成一条层级轴来用界面会立刻变得有主次而且不用靠加粗和描边硬撑。为什么深度能表达层级深度和层级之间的映射不是设计师拍脑袋定的跟人的知觉习惯有关。几个稳定的规律近大远小。距离越近物体在视野里占比越大、细节越清晰天然更吸引注意。这是透视在替你做视觉权重。前景清晰、背景虚化。相机的景深效果人从小看到大已经把清晰焦点、模糊背景写进了直觉。遮挡即优先。前面挡住后面的被挡的就自动退到次要位置这是遮挡关系最原始的信息编码。视差与位移。用户移动时近处的物体位移大、远处的位移小深度差会自动强化层次感。所以 Z 轴并不只是一个坐标它是一条可以被读的语义轴。近 当下焦点中 内容主体远 环境上下文。设计师要做的是把信息的重要性映射到这条轴上。Z 轴的三个语义带给空间界面定三档深度每档对应一个语义带比无级地随便挪要好管得多语义带z 参考值视觉特征承载内容交互前景 / 焦点80 ~ 160 vp大、亮、清晰、有阴影主任务、详情、弹层点按、输入中景 / 内容-40 ~ -80 vp正常尺度、正常亮度列表、卡片、图文滚动、选择背景 / 环境-160 ~ -260 vp小、暗、虚化、无阴影背景氛围、空间锚点无参考值的意义是让团队用同一套刻度说话而不是每次拍脑袋。数值本身可以调但档与档之间要拉开足够差距z 相差太小透视压缩后几乎看不出区别。一个常见误区以为把元素translate到更近的 z 就会变大。在 ArkUI 里translate的 z 分量只有在透视生效时才会改变视觉大小而透视由rotate的perspective参数控制API 10 起支持。没开透视z 平移就是纯粹的前后遮挡位移。zIndex 与 z 坐标别把它们混着用这两个都带个z但管的事完全不同是空间布局里最容易绕晕的一对概念。zIndex管渲染顺序谁盖住谁。它是同一容器内兄弟组件的堆叠顺序值越大显示层级越高。它不影响透视、不影响大小纯靠声明顺序和数值决定覆盖。跨容器的组件zIndex是无效的。z 坐标管空间位置谁离用户近。通过translate({ z })、rotate的centerZ、scale的 z或者transform3D矩阵来改。它决定透视下的尺度和视差但不管遮挡——一个 z 更近的元素如果zIndex更低照样可能被盖住。正确的配合方式是用 z 坐标定义层次关系用zIndex保证遮挡顺序跟着层次走。别指望只动一个。// 用常量把语义层级固化下来避免代码里散落魔数constenumDepthBand{Background-200,// 环境层Content-60,// 内容层Focus120// 焦点层}// zIndex 也按层级给保证遮挡与深度一致constenumLayerOrder{Background0,Content1,Focus10}前后层切换用状态驱动深度空间层级切换的骨架很简单一个状态表示当前哪层在前深度和虚化都由它推导出来。下面是一个可复用的实现。// DepthStage.ets —— 用 z 坐标 zIndex 组织三层并支持前后层切换Componentexportstruct DepthStage{// expanded 为 true 时焦点层前移其余层后退Stateexpanded:booleanfalseprivatereadonlyperspective:number900// 统一的深度推导避免各处硬编码privatebackgroundZ():number{returnthis.expanded?-280:-200}privatecontentZ():number{returnthis.expanded?-120:-60}privatefocusZ():number{returnthis.expanded?140:0}build(){Stack({alignContent:Alignment.Center}){// 环境层Column().width(150%).height(150%).backgroundColor(#0E1220).translate({z:this.backgroundZ()}).rotate({perspective:this.perspective}).blur(this.expanded?20:4)// 展开时背景更虚.zIndex(0)// 内容层Column(){this.contentCard()}.translate({z:this.contentZ()}).rotate({perspective:this.perspective}).opacity(this.expanded?0.55:1)// 后退时降透明度进一步让位.zIndex(1)// 焦点层默认收起在内容层附近展开时前移Column(){this.detailPanel()}.width(this.expanded?360:0).translate({z:this.focusZ()}).rotate({perspective:this.perspective}).zIndex(10)}.width(100%).height(100%).onClick((){// 一次动画同时改三个深度层次切换才连贯this.getUIContext()?.animateTo({duration:380,curve:Curve.Friction},(){this.expanded!this.expanded})})}BuildercontentCard(){Column({space:8}){Text(云雾乌龙).fontSize(18).fontColor(Color.White)Text(¥ 88 / 罐).fontSize(16).fontColor(#FF9AA2)}.padding(20).borderRadius(16).backgroundColor(#1A2238)}BuilderdetailPanel(){Column({space:12}){Text(产地与工艺).fontSize(18).fontColor(Color.White)Text(轻发酵炭焙提香适合 90℃ 水温。).fontSize(14).fontColor(#C7CEDB)}.padding(24).borderRadius(20).backgroundColor(#2A3350)}}这段代码里有三处安排是刻意的深度集中推导。三个xZ()函数把语义带和状态解耦以后调参数只改一处。虚化跟着状态走。背景层blur随展开加深配合前景前移景深关系才成立。一个animateTo改多个属性。层次切换是整体的分几段动画会显得各走各的破坏空间感。案例卡片详情展开的空间化把上面的骨架套到商品卡片的点开看详情。2D 里的做法是卡片放大、翻页或者弹出新页面用户会丢失我从哪来的这个上下文。空间化后处理方式变成卡片本体留在中景原位稍微后退并虚化它还在那儿告诉用户你从这张卡来详情面板从卡片位置前移到焦点层成为当前唯一可读对象关闭时详情退回中景、与卡片合并视觉上是收回去而不是换了个页面。// 关键状态与关闭手势StatedetailVisible:booleanfalse// 展开面板前移 原卡片后退虚化privateopenDetail(){this.getUIContext()?.animateTo({duration:360,curve:Curve.Friction},(){this.detailVisibletrue})}// 关闭面板退回卡片复位privatecloseDetail(){this.getUIContext()?.animateTo({duration:300,curve:Curve.Friction},(){this.detailVisiblefalse})}点按卡片面板前移 z140点按空白或返回面板退回 z0CollapsedExpandingFocusedCollapsing卡片后退 z-120 并 blur 18仅焦点层接收手势前后层切换的对照处理方式常用 API效果适用场景只改 zIndexzIndex元素平移前后遮挡无纵深浮层、Toast改 z 坐标translate({ z })perspective有透视缩放和视差层级切换、聚焦缩放模拟scale近大远小但无视差轻量聚焦矩阵变换transform3D完整的 3D 变换复杂 3D 排布总结一下下语义先定数值后调。先把三档语义带定下来再让设计稿去贴合比反向凑数值省事。焦点层同一时刻只留一个。两个东西同时最近用户不知道该看谁。虚化要配合后退。只虚化不后退看着像图层蒙了层雾只后退不虚化背景又会抢戏。给动画留时间。深度切换 300~400ms 比较舒服太快会晕太慢会拖沓。中景是家别让焦点层把中景遮死。留出可见的边缘用户才有返回的参照。容易出问题的地方把zIndex当深度用。结果是一堆浮层贴在同一个平面上越叠越乱没有空间感。跨容器用zIndex。不同 Stack 容器里的组件zIndex无法跨容器改变层级调了也没反应。动态改zIndex忽略稳定排序。在不增删兄弟节点时zIndex变化会在原层级顺序上稳定排序结果可能和值大者在上的直觉不一致。细节面板宽度直接从 0 动画到 360。只改宽度会触发反复重排改scale或opacity更顺滑。焦点层不限制命中区域。前移之后它的触摸区域也放大了容易误触到后面本该被挡住的控件建议焦点态下让背景层hitTestBehavior设为None。