ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

【共创稿事节】HarmonyOS 7从“铺大饼“到“叠千层“:布局思维的转变

【共创稿事节】HarmonyOS 7从“铺大饼“到“叠千层“:布局思维的转变 做了多年 2D 应用的人第一次做空间布局最容易犯的错是把手机上的排版原样搬进来——所有元素摊在一个平面上靠间距和分割线分出条理。这在手机上没毛病因为屏幕就巴掌大、视点固定。到了空间里这个前提没了摊开的大饼会跟着用户转头、走近、后退一起变形。这篇聊的就是布局思路怎么从平面切分换成前后分层。铺大饼是怎么形成的2D 界面的核心约束是一屏之内。屏幕有固定边界内容必须一次性装进这个矩形所以布局的活儿基本就是在二维平面上分配面积谁宽谁窄、谁上谁下、谁压谁。行、列、栅格、瀑布流本质都是二维切分。为什么大家习惯这么干因为屏幕的物理属性替你兜了底它一直假设三件事成立画布尺寸固定宽高就是设计稿上的数字视点固定眼睛正对屏幕中心距离基本不变内容一次性全可见滚动只是换一批。在手机上这三条几乎永远成立。你写一个ColumnScroll编辑器里的布局和用户看到的布局一模一样。平铺之所以够用是因为它和屏幕的物理属性对上了。换到空间里这三个前提全断了空间计算里屏幕边界不再等于内容边界画布不再固定。用户可以在 2m 外扫一眼整体也可以凑到 0.3m 看某张图的细节。同一个元素在不同距离上占的视野比例完全不同。视点会动。头部转动、身体位移用户的屏幕中心一直在变。原来放在正中央的东西转头之后就跑到视野边缘了。内容不必全可见。前后遮挡、进出视野在空间里是常态不是 bug。平铺布局在这三条上全都不成立。更麻烦的是平铺要求用户一次看全而空间里视野是扇形的、注意力是有焦点的硬塞满的结果是近了看不过来远了看不清转个头还找不到北。这就是铺大饼在空间里失效的根因——它用面积分配代替了注意力分配。“叠千层”把信息拆成前后层分层布局的思路反过来不追求把东西摊在一个平面上比大小而是给信息分配深度让用户像看一叠卡片那样一层层地看。一个稳妥的起点是三层模型每层职责不同层深度位置承载内容允许的交互环境层背景最远氛围背景、空间锚点、品牌场景无交互只提供上下文内容层中景中间列表、卡片、图文主体滚动、选择、浏览焦点层前景最近当前主任务、详情、弹层、输入点按、输入、确认分层的判断依据其实就一句话用户当下要不要操作它。要操作的往前提提供上下文的后退。有了这个判断一个页面的元素该放哪层基本就定了。这里要区分两个容易混的概念渲染层级谁盖住谁靠zIndex或声明顺序决定是贴纸叠贴纸。空间深度谁离用户近、谁离用户远靠translate的 z 分量或transform3D决定是真的一前一后。前者影响遮挡后者影响透视和视差。叠千层要的是后者zIndex只是配套保证遮挡关系正确。用 Stack 分层属性组织千层Stack是堆叠容器子组件按声明顺序依次入栈后声明的盖在先声明的上面可以用alignContent控制整体对齐方式。下面把同一个商品页写成两版。先看铺大饼的写法——所有东西塞进一条Column// FlatPage.ets —— 2D 思维的平铺版EntryComponentstruct FlatProductPage{build(){Column(){// 导航、主图、标题、价格、规格、评价、推荐全在一条纵轴里Scroll(){Column({space:12}){Image($r(app.media.product_cover)).width(100%).height(280).objectFit(ImageFit.Cover)Text(便携蓝牙音箱).fontSize(22).fontWeight(FontWeight.Bold)Text(¥ 399).fontSize(28).fontColor(#E5484D)// 规格、评价、推荐位……继续往下堆this.specBlock()this.reviewBlock()this.recommendBlock()}.width(100%).padding(16)}.layoutWeight(1)}.width(100%).height(100%)}}这套代码在手机上没问题因为它默认了用户正对屏幕从头看到尾。到了空间里用户的视锥是有限的把七个区块从头排到尾等于逼着他不停转头。再看叠千层的写法——同样的信息按职责拆到三层用Stack叠起来用 z 分量拉开深度// LayeredPage.ets —— 叠千层的分层版EntryComponentstruct LayeredProductPage{// 焦点层当前深度0 表示收起0 表示前移到用户面前StatefocusZ:number0// 是否进入详情聚焦态Statefocused:booleanfalse// 统一的透视距离ArkUI 里 z 轴平移要配合 rotate 的 perspective// 才会产生近大远小的透视感否则只是纯粹的像素位移privatereadonlyperspective:number900build(){Stack({alignContent:Alignment.Center}){// ---------- 环境层最远只给氛围不接交互 ----------Column().width(140%).height(140%).linearGradient({angle:180,colors:[[#0B1020,0.0],[#1B2440,1.0]]}).translate({z:-220}).rotate({perspective:this.perspective})// 开启透视.blur(this.focused?16:0)// 聚焦时背景虚化// ---------- 内容层中景浏览与选择 ----------Column({space:12}){Text(便携蓝牙音箱).fontSize(20).fontColor(Color.White)Text(¥ 399).fontSize(24).fontColor(#FF8A8A)Image($r(app.media.product_cover)).width(240).height(160).borderRadius(12)}.translate({z:-60}).rotate({perspective:this.perspective})// ---------- 焦点层最近主操作与详情 ----------if(this.focused){Column({space:16}){Text(规格与配送).fontSize(18).fontColor(Color.White)Button(加入购物车).onClick((){/* 主操作 */})}.padding(24).backgroundColor(#22FFFFFF).borderRadius(20).translate({z:this.focusZ})// 前移到用户面前.rotate({perspective:this.perspective}).zIndex(10)// 保证盖住中景层}}.width(100%).height(100%).onClick((){// 点一下焦点层从远处飘到面前this.getUIContext()?.animateTo({duration:360,curve:Curve.Friction},(){this.focused!this.focusedthis.focusZthis.focused?120:0})})}}两版的信息量一样区别在于组织方式平铺版用横向面积排序分层版用纵向深度排序。用户转头时分层版的层次关系还在平铺版则彻底散了。一个实操提示translate({ z })单独用只会产生位移想看到近大远小的效果得配合rotate的perspectiveAPI 10 起支持或者transform3D。透视参数越小纵深压缩越强烈一般 600~1200 之间比较自然太小会有鱼眼感。平铺与分层对照维度铺大饼平铺叠千层分层组织方式二维面积切分前后深度分配依赖前提画布/视点/可见性固定无固定假设随视点自适应层级表达位置、分割线、字号深度、尺度、模糊、亮度用户注意力要求一次看全焦点优先按需展开适用载体平面屏幕空间、多视点、可变视距主要风险空间里散架层数太多导致眩晕案例设置中心从平铺到分层设置页是最典型的铺大饼重灾区。2D 里它是一长串分组列表用户得滑很久。空间化改造后可以这么分环境层一块柔和的底纹左上角用极低亮度放设置字样作为空间锚点用户转头回来能定位。内容层把分组账号、通用、隐私、关于做成四张横排的卡片站在中景一次可见靠左右转头切换而不是上下滑动。焦点层点开某个分组后具体开关项前移到用户面前 0.5m 处成为唯一可操作对象其余卡片后退并降亮。这样改的效果是用户不用记我滑到第几屏了因为分组是空间里固定的几块位置转头就能找到。记忆负担从滚动位置变成了空间方位而空间记忆恰恰是人比较擅长的那一类。转头返回锚点可定位用户视点焦点层当前分组详情内容层四张分组卡片环境层底纹与空间锚点总结一下下先问要不要操作再问放哪一层。分层的依据是交互意图不是视觉好看。层数控制在三层。超过三层用户分不清哪层是当前任务眩晕感也上来了。深度差要够大才看得出来。z 相差 20vp 基本等于没有一般至少 60vp 起步环境层可以到 -200vp 以上。zIndex管遮挡z 管透视两件事分开调。想让某个层浮起来两个都得动。环境层不接交互。给它挂手势用户会误触到背景破坏方向感。小注意哦只调 zIndex 不调 z以为就是分层了。zIndex只改谁盖谁元素还是贴在同一个平面上看起来只是浮层没有空间纵深。开了透视但没配perspective。直接写translate({ z: 100 })视觉上几乎没有变化会误以为 API 不生效。焦点层没有回退入口。前移之后如果背景层不可点、又没有关闭手势用户会卡在焦点态。中景层用layoutWeight撑满全屏。空间布局更需要固定的可视尺寸靠弹性撑满会让透视关系变形。忽略性能。每层都开blur和透视变换叠加起来 GPU 压力不小环境层的模糊最好做静态处理或降低分辨率。
返回列表