Yoga Flexbox 布局计算 UTS 插件,支持在独立线程中执行布局计算,避免阻塞 UI 线程。
介绍
tui-yoga-layout 是 uni-app x 环境下的 Yoga Flexbox 布局计算引擎插件。调用方传入 JSON 格式的布局配置字符串,插件在平台内部线程 / Worker 中执行 Yoga 布局计算,再以 JSON 字符串形式返回每个节点的测量框(x / y / width / height)与文本逐行数据,供 Canvas 或自研渲染引擎排版绘制。
输入输出均以 JSON 字符串为边界,便于跨线程 / 进程传递与后续扩展。
支持 box / text / image 三种节点:普通容器按 Flexbox 规则计算,文本节点支持原生折行测量并输出逐行数据,图片节点支持本地 / base64 / 网络图解码并按比例参与布局。
支持平台
| 平台 | 引擎 | 计算位置 | 状态 |
|---|---|---|---|
| Android | Yoga Android SDK 2.0.1(.aar) | 后台线程 | ✅ 全能力 |
| iOS | Yoga iOS SDK 2.0.1(pod) | 后台 | ✅ box 全能力(text/image 测量待接入) |
| HarmonyOS | @ohos/yoga | Worker 线程 | ✅ 全能力 |
| Web | yoga-wasm-web(WASM 内联) | Worker / 主线程 | ✅ 全能力(仅异步接口) |
| 微信小程序 | yoga-wasm-web(asm.js) | 小程序逻辑层 | ✅ 全能力(仅异步接口) |
说明:
text折行 /image解码测量为各平台渐进接入能力。Android、HarmonyOS、Web、小程序端均已支持;iOS 当前第一迭代仅支持box容器布局,text/image叶子会按普通 box 或显式尺寸处理,lines/loaded输出暂缺。iOS 同步接口出错时降级返回空字符串(不抛异常),请自行判空。
基础用法
在 uvue / uts 页面中通过 @/uni_modules/tui-yoga-layout 导入:
import { calculateLayoutAsync, calculateLayoutSync, calculateLayout } from '@/uni_modules/tui-yoga-layout'
// 方式一:异步接口(全平台推荐)
const configJson = JSON.stringify({
width: 320,
flexDirection: 'row',
alignItems: 'center',
children: [
{ id: 'a', width: 100, height: 100 },
{ id: 'b', flex: 1, height: 50 }
]
})
const resultJson: string = await calculateLayoutAsync(configJson)
const result = JSON.parse(resultJson)
console.log(result.root.width, result.children.length)
// 方式二:同步接口(小程序 / Web 端不可用,将抛 9010003)
const resultJsonSync: string = calculateLayoutSync(configJson)
// 方式三:options 回调风格(结构化返回)
calculateLayout({
configJson: configJson,
success: (res) => {
console.log(res.root.x, res.root.width)
},
fail: (err) => {
console.error(err.errCode, err.errMsg)
}
})页面中使用
<template>
<view>
<t-button type="primary" @click="run">计算布局</t-button>
<text>{{ outJson }}</text>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { calculateLayoutAsync } from '@/uni_modules/tui-yoga-layout'
const outJson = ref('')
async function run() {
const configJson = JSON.stringify({
width: 280,
flexDirection: 'column',
children: [
{ id: 't1', type: 'text', value: '中英文混排自动折行,行高逐行累加', fontSize: 15, lineHeight: 22 },
{ id: 't2', type: 'text', value: '加粗标题行', fontSize: 16, fontWeight: 'bold', margin: { top: 8 } },
{ id: 'img1', type: 'image', src: '/static/image/1.png', maxWidth: 200 }
]
})
try {
const resultJson = await calculateLayoutAsync(configJson)
const out = JSON.parse(resultJson)
outJson.value = JSON.stringify(out, null, 2)
} catch (err) {
console.error('code=' + err.errCode + ':' + err.errMsg)
}
}
</script>更多完整示例见 demo/yoga/yoga.uvue(文本折行、双栏图文、标签换行等运行示例,含 Canvas 排版预览与友好报告)。
API
函数列表
| 函数 | 签名 | 说明 |
|---|---|---|
calculateLayoutAsync | (configJson: string) => Promise<string> | 异步计算,返回 JSON 布局结果字符串。全平台推荐,App 端自动丢后台线程,小程序 / Web 端内部走 Worker |
calculateLayoutSync | (configJson: string) => string | 同步计算,返回 JSON 布局结果字符串。App / HarmonyOS 可用;小程序 / Web 端抛 9010003 |
calculateLayout | (options: YogaLayoutOptions) => void | options 回调风格,成功返回结构化 YogaLayoutResult |
calculateLayoutAsync
参数
| 参数 | 说明 | 类型 |
|---|---|---|
| configJson | YogaLayoutConfig 的 JSON 字符串 | string |
返回值
Promise<string>:YogaLayoutResult 的 JSON 字符串。
Android 端异步阶段会自动并行下载配置中所有 http(s) 图片并测量真实宽高,使网络图与本地 / base64 图一样按真实比例参与布局。
错误
reject 一个 YogaLayoutFail 错误对象(errCode / errMsg,错误码见下表)。
calculateLayoutSync
同步计算接口。仅 App(Android / iOS)与 HarmonyOS 可用;小程序 / Web 端因 WASM 需异步初始化将抛出 9010003,请使用异步接口。
| 参数 | 说明 | 类型 |
|---|---|---|
| configJson | YogaLayoutConfig 的 JSON 字符串 | string |
注意:
- Android 端含网络图时内部会同步下载(阻塞等待),请勿在 UI 主线程调用;
- HarmonyOS 无同步图片解码能力,命中进程内缓存才返回真实尺寸,否则图片按占位返回
loaded=false; - iOS 端同步出错时降级返回空字符串(不抛异常),请自行判空。
calculateLayout(options)
options 回调风格,内部走异步计算,返回结构化结果。
| 参数 | 说明 | 类型 |
|---|---|---|
| configJson | YogaLayoutConfig 的 JSON 字符串 | string |
| success | 计算成功回调,返回 YogaLayoutResult | (result) => void |
| fail | 计算失败回调,返回错误对象 | (err: YogaLayoutFail) => void |
| complete | 完成回调(成功时触发) | (result) => void |
数据结构
YogaLayoutConfig(计算入参)
| 字段 | 说明 | 类型 |
|---|---|---|
| width / height | 根容器宽高,可选,缺省为 auto(由子内容按 flex 规则撑开,结果 root.width/height 为实际尺寸) | number |
| direction | 文本方向:inherit / ltr / rtl | string |
| flexDirection | 主轴方向:row / column / row-reverse / column-reverse | string |
| flexWrap | 换行:no-wrap / wrap / wrap-reverse | string |
| justifyContent | flex-start / flex-end / center / space-between / space-around / space-evenly | string |
| alignItems / alignContent | flex-start / flex-end / center / baseline / stretch | string |
| padding | 根容器内边距 | YogaEdgeInsets |
| children | 子节点数组(可继续嵌套) | YogaLayoutNode[] |
YogaLayoutNode(节点描述)
| 字段 | 说明 | 类型 |
|---|---|---|
| type | 节点类型:box(缺省,可嵌套 children)/ text / image | string |
| id | 节点标识,用于在结果中定位对应布局框 | string |
| width / height | 宽高;number 绝对长度,"NN%" 相对父容器 | number | string |
| minWidth / minHeight / maxWidth / maxHeight | 尺寸约束 | number | string |
| flexGrow / flexShrink | 伸缩比例 | number |
| flexBasis | 'auto' / 数值 / 'NN%' | string | number |
| flex | 快捷属性,等价于 flexGrow=flex, flexShrink=1, flexBasis=0 | number |
| flexDirection / flexWrap / justifyContent / alignItems / alignContent / alignSelf | flex 布局属性 | string |
| position | relative / absolute | string |
| display | flex / none | string |
| direction | inherit / ltr / rtl | string |
| overflow | 溢出裁剪:visible / hidden / scroll | string |
| top / bottom / left / right | absolute 定位偏移(支持 number 或 "NN%") | number | string |
| gap | 主轴子节点间距(仅主轴生效) | number |
| margin / padding / borderWidth | 外边距 / 内边距 / 边框,支持 number 或 YogaEdgeInsets;borderWidth 不支持百分比 | YogaEdgeInsets | number |
| aspectRatio | 宽高比(宽/高),width / height 至少一个为空时生效 | number |
| value | type='text' 时的文本内容 | string |
| fontSize | 字号(逻辑像素),缺省 16 | number |
| fontWeight | 字重 'normal'(400) / 'bold'(700) 或数字 100~1000,缺省 normal | string | number |
| fontFamily | 字体族:'sans-serif'(缺省)/ 'serif' / 'monospace' 或系统字体名 | string |
| textAlign | 文本水平对齐:left / center / right,缺省 left | string |
| lineHeight | 行高,缺省按字体度量自动计算 | number |
| src | type='image' 时图片来源:本地绝对路径 / file:// / data:image/...;base64,xxxx / http(s) | string |
| children | 子节点(box 容器) | YogaLayoutNode[] |
注意:
- 尺寸 / 边距百分比(
"NN%")相对父节点对应维度(绝对定位相对父 padding 框);文本 / 图片叶子不支持百分比 width; gap仅作用于主轴子节点间距,flexWrap 换行产生的交叉轴“行间距”请用子节点margin处理;- 文本叶子建议不传 width / height,由原生按可用宽度折行后自动撑开高度;图片叶子建议给
maxWidth(等比缩小上限)或精确width,否则按解码原图宽高比与可用宽度自适应;解码失败按占位返回loaded=false。
YogaLayoutResult(计算结果)
| 字段 | 说明 | 类型 |
|---|---|---|
| root | 根容器布局框 | YogaLayoutBox |
| children | 全部子节点布局框(扁平数组,树前序遍历顺序),通过 id 与输入节点对应 | YogaLayoutBox[] |
YogaLayoutBox(节点布局框)
| 字段 | 说明 | 类型 |
|---|---|---|
| id | 对应输入节点 id | string |
| type | 节点类型 | string |
| x / y / width / height | 布局框(相对父节点坐标) | number |
| lines | type='text' 时折行后逐行数据 | YogaLayoutTextLine[] |
| fontSize / lineHeight | type='text' 时实际采用的字号 / 行高 | number |
| loaded | type='image' 时是否成功解码,false 表示按占位尺寸返回 | boolean |
| src | type='image' 时原始图片地址(成功 / 占位都会原样返回) | string |
| ratio | type='image' 时实际采用的宽高比(宽/高) | number |
YogaLayoutTextLine(文本行数据)
| 字段 | 说明 | 类型 |
|---|---|---|
| text | 该行渲染的文本 | string |
| x | 该行相对 box 左边界的 x | number |
| y | 该行文字 top 相对 box 上边界的 y(按 lineHeight 累加) | number |
| width | 该行实际文本测量宽度 | number |
| ascent | 字体基线偏移(负值),canvas 绘制 fillText(text, box.x + x, box.y + y - ascent) | number |
YogaEdgeInsets
| 字段 | 说明 | 类型 |
|---|---|---|
| top / bottom / left / right | 各边值 | number | string |
| horizontal / vertical | 快捷写法,优先级低于 top / bottom / left / right | number | string |
错误码
遵循 uni 错误码规范(90 开头),错误对象 errSubject 为 tui-yoga-layout。
| errCode | 含义 |
|---|---|
| 9010001 | 配置 JSON 解析失败 |
| 9010002 | Yoga 布局计算失败 |
| 9010003 | 当前平台不支持该接口(小程序 / Web 端请使用异步接口 calculateLayoutAsync) |
| 9010004 | 该平台实现尚未接入 |