> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/creator/content-creator-zh/chang-jing-sdk7/2d-ui/ui-positioning.md).

# UI 定位

设置 UI 实体的位置、缩放、内边距和其他属性。

对于各种 UI 内容，请使用 `uiTransform` 组件来设置大小、位置以及与实体对齐相关的其他属性。

该 `uiTransform` 组件在屏幕的二维空间中工作，很像 `Transform` 组件在场景的三维空间中工作。

***ui.tsx 文件：***

```ts
import { UiEntity, ReactEcs } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export const uiMenu = () => (
	<UiEntity
		uiTransform={{
			width: '200px',
			height: '100px',
			justifyContent: 'center',
			alignItems: 'center',
		}}
		uiBackground={{ color: Color4.Green() }}
	/>
)
```

***index.ts 文件：***

```ts
import { ReactEcsRenderer } from '@dcl/sdk/react-ecs'
import { uiMenu } from './ui'

export function main() {
    ReactEcsRenderer.setUiRenderer(uiMenu)
}
```

{% hint style="warning" %}
**📔 注意**：本页中的以下所有代码片段均假定你有一个 `.ts` 与上文类似，并运行 `ReactEcsRenderer.setUiRenderer()` 函数。
{% endhint %}

## 定位属性

UI 实体的对齐基于 Flexbox 对齐模型。这是一个非常强大的模型，适合在大小可能变化的模态框中动态组织嵌套实体。

{% hint style="info" %}
**💡 提示**: Decentraland 的 UI 实现基于 [Yoga](https://yogalayout.com/docs/). 参阅 [本文](https://www.joshwcomeau.com/css/interactive-guide-to-flexbox/) 以获得对 Flexbox 可用属性非常易懂且深入的介绍。
{% endhint %}

### 实体大小

使用 `width` 和 `height` 来设置实体大小。支持以下几类值：

* `auto`: 大小会自动适配以容纳内部内容。这对长度可能变化的文本非常方便。将值写为 "auto"。
* **百分比**: 作为父级测量值的百分比。将值写为以 "%" 结尾的字符串，例如 `10 %`.
* **像素**: 将值写为数字。
* **屏幕宽度或高度**: 可使用 vw（视口宽度）和 vh（视口高度）来表示正在运行 Decentraland 的窗口完整尺寸的一部分。例如 `10vw` 表示窗口宽度的 10%， `25vh` 表示窗口高度的 25%。

请注意，这些属性会影响 **default** 该项的大小，也就是在进行任何 flex grow 和 flex shrink 计算之前的大小。最终大小可能会根据父实体大小以及所设置的 Flexbox 属性而有不同的解释。

{% hint style="warning" %}
**📔 注意**: 在同时支持数字和字符串的属性中，要以像素设置值，请写数字。要将这些字段设置为父级测量值的百分比，请写以 "%" 结尾的字符串，例如 `10 %`. 你也可以将像素值写成字符串，只需让字符串以 `px`结尾，例如 `200px`.

* 当值以百分比表示时，它们始终相对于父容器。UI 的顶层实体也有父级：渲染器会把它放入由 [`screenInset`](/creator/content-creator-zh/chang-jing-sdk7/2d-ui/onscreen-ui.md#screen-inset-area)选择的区域中，默认是设备安全区域。因此，根级 `100%` 占该区域的 100%——在比屏幕更窄的手机上如此，在桌面上二者则重合。若需要以完整屏幕为基准计算百分比，请传入 `screenInset: 'none'` 如果你需要按整个屏幕来计算百分比。
* 如果值以像素表示，它们不会受到父级缩放的影响，但也不是 **不是** 原始屏幕像素：它们会乘以由 [虚拟屏幕](/creator/content-creator-zh/chang-jing-sdk7/2d-ui/onscreen-ui.md#screen-virtual-scale)派生出的 UI 缩放系数，而该系数默认启用。一个 `width: 200` 表示“在虚拟屏幕尺寸的屏幕上为 200 px”，并且会在任何其他尺寸上按比例放大或缩小。
* 如果值表示为 `vh` 或 `vw`，则它们是相对于整个窗口的百分比，不受父级缩放、虚拟屏幕或 `screenInset`.

在 `auto` 要让 width/height 生效，需遵循以下规则：

* 将 width/height 设为 “auto” 的 UiTransform 应该具有 `alignSelf`: `“center”`/`“flex-start”`/`“flex-end”` 或 `positionType: “absolute”`
* 如果子项的 UiTransform 使用 `positionType: “absolute”`，父级不会适应其大小/位置
* 如果子项的 UiTransform 使用任何位置覆盖，父级都不会适应其大小/位置
  {% endhint %}

还可以使用以下其他属性以更高级的方式调整大小：

* `maxWidth` 和 `maxHeight`: *数字* 或字符串（如 height 和 width）。实体可能具有的最大尺寸。
* `minWidth` 和 `minHeight`: *数字* 或字符串（如 height 和 width）。实体可能具有的最小尺寸。如果父级太小而无法容纳实体的最小尺寸，它们会溢出父级。
* `flexBasis`：这是一种与轴无关的方式，用来沿主轴提供项目的默认大小。为子项设置 flex basis，类似于在其父级为 flex direction: row 的容器时设置该子项的宽度，或者在其父级为 flex direction: column 的容器时设置该子项的高度。

```ts
import { UiEntity, ReactEcs } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export const uiMenu = () => (
	<UiEntity
		uiTransform={{
			alignSelf: 'center',
			alignContent: 'center',
			width: '80%',
			height: '30%',
			minWidth: 300,
			maxWidth: 2500,
			margin: { left: '10%', right: '10%' },
		}}
		uiBackground={{ color: Color4.Green() }}
	/>
)
```

### 排列子实体

默认情况下，子实体相对于其父实体的左上角定位。你可以使用诸如 `justifyContent` 和 `alignItems` 之类的属性来改变这一行为。

{% hint style="info" %}
**💡 提示**任何提及 *内容* 的属性都指主轴上的实体（由 `flexDirection`决定）。任何提及
{% endhint %}

* `flexDirection`：Flex direction 控制节点子项的布局方向。这也称为主轴。主轴是子项的布局方向。交叉轴是与主轴垂直的轴，或者是换行线布局所在的轴。它的值来自 `FlexDirectionType` 类型。可用选项如下：
  * `row` （默认）
  * `row-reverse`
  * `column`
  * `column-reverse`
* `justifyContent`：该属性描述如何在容器的主轴内对齐子项。例如，你可以使用此属性在设置为 row 的容器中水平居中子项，或在设置为 column 的容器中垂直居中子项。该属性的值必须来自 `flexDirection` 设置为 row 的容器中，或者在 `flexDirection` 设置为 column 的容器中。该属性的值必须来自 `JustifyType` 类型。可能的值有：
  * `flex-start` （默认）：将容器的子项对齐到容器主轴的起始位置。
  * `flex-end`：将容器的子项对齐到容器主轴的末尾。
  * `中心`：将容器的子项居中对齐到容器主轴。
  * `space-between`：沿容器主轴均匀分布子项，并在子项之间分配剩余空间。
  * `space-around`：沿容器主轴均匀分布子项，并在子项周围分配剩余空间。与 space-between 相比，使用 space-around 会将空间分配到第一个子项的开头和最后一个子项的结尾。
  * `space-evenly`：沿容器主轴均匀分布子项，使子项之间以及子项与容器边缘之间的间距都相等。
* `alignItems`：描述如何沿容器的交叉轴对齐子项。Align items 与 justify content 非常相似，但不同的是 align items 作用于交叉轴而不是主轴。该属性需要来自 `AlignType` 类型。可用选项如下：
  * `拉伸`： （默认）拉伸容器子项，使其与容器交叉轴的高度一致。
  * `flex-start`：将容器的子项对齐到容器交叉轴的起始位置。
  * `flex-end`：将容器的子项对齐到容器交叉轴的末尾。
  * `中心`：将容器的子项居中对齐到容器交叉轴。
  * `baseline`：将容器的子项沿共同基线对齐。单个子项可以被设置为其父级的参考基线。
* `alignSelf`：align self 与 `alignItems` 具有相同的选项和效果，但它不是影响容器内的子项，而是可以将此属性应用于单个子项，以改变其在父级中的对齐方式。align self 会覆盖父级通过 align items 设置的任何选项。它的值来自 `AlignType`，见 `alignItems` ，有关这些选项的详情请见上文。
* `alignContent`：Align content 定义沿交叉轴的各行分布方式。只有在使用 `flexWrap`将项目换行为多行时才会生效。它的值来自 `AlignType` 类型。可用选项如下：
  * `flex-start`： （默认）将换行对齐到容器交叉轴的起始位置。
  * `flex-end`：将换行对齐到容器交叉轴的末尾。
  * `拉伸`：拉伸换行，使其与容器交叉轴的高度一致。
  * `中心`：将换行居中对齐到容器交叉轴。
  * `space-between`：沿容器主轴均匀分布换行，并在各行之间分配剩余空间。
  * `space-around`：沿容器主轴均匀分布换行，并在各行周围分配剩余空间。与 space-between 相比，使用 space-around 会将空间分配到第一行的开头和最后一行的结尾。
* `flexGrow`：这描述了容器内的任何剩余空间应如何沿主轴在子项之间分配。布局完成后，容器会根据子项指定的 flex grow 值分配任何剩余空间。Flex grow 接受任何大于等于 0 的浮点值，默认值为 0。容器会按子项的 flex grow 值加权，将剩余空间分配给子项。
* `flexShrink`：描述在子项总大小在主轴上超过容器大小时，如何沿主轴收缩子项。flex shrink 与 flex grow 非常相似；如果将任何溢出的大小视为负的剩余空间，也可以按相同方式理解。这两个属性也能很好地协同工作，允许子项按需要增长和收缩。Flex shrink 接受任何大于等于 0 的浮点值，默认值为 1。容器会按子项的 flex shrink 值加权收缩子项。
* `overflow`：决定当实体子项的大小超出其父级时会发生什么。它使用来自 `OverflowType` 类型。
  * `hidden`：超出的实体会变为不可见。
  * `visible`：超出的实体会突破父级的边距范围。
  * `scroll`：该区域会变为可滚动，允许玩家滚动查看溢出的内容。参见 [可滚动容器](#scrollable-containers) 了解详情。
* `flexWrap`：flex wrap 属性设置在容器上，控制当子项沿主轴超出容器大小时会发生什么。默认情况下，子项在需要时会沿主轴换成多行。如果使用 `nowrap`禁用换行，子项会被强制放入单行（这可能会压缩实体）。wrap reverse 的行为与 wrap 相同，但行的顺序会反转。该属性的值来自 `FlexWrapType` 类型。
  * `wrap`
  * `nowrap`
  * `wrap-reverse`

### 边距和内边距

* `margin`：该属性影响节点外部周围的间距。带有 margin 的节点会相对于父级边界发生偏移，同时也会影响任何兄弟节点的位置。若父级为自动尺寸，节点的 margin 会计入其父级总大小。设置实体与父级边距之间的空间。期望值是一个对象，其中包含属性 `顶部`, `left`, `底部`，以及 `right`.
* `padding`：该属性影响其所应用节点的大小。在 Yoga 中，Padding 的作用类似于设置了 box-sizing: border-box;。也就是说，如果实体已经显式设置了尺寸，padding 不会增加其总大小。对于自动尺寸节点，padding 会增加节点大小并偏移任何子项的位置。期望值是一个对象，其中包含属性 `顶部`, `left`, `底部`，以及 `right`.

### 微调位置

在 Flexbox 中，实体位置主要由其父子关系以及父级和子级设置了哪些排列属性来决定。很多时候你根本不需要设置 `位置` 属性。但如果你确实想调整它，或者完全覆盖 Flexbox 的正常流并设置绝对位置，相关属性如下：

* `positionType`：定义实体如何定位。它使用来自 `PositionType` 枚举中的值。
  * `relative`： （默认）默认情况下，实体采用相对定位。这意味着实体会根据布局的正常流进行定位，然后再根据 `顶部`, `right`, `底部`，以及 `left`的值相对于该位置偏移。该偏移不会影响任何兄弟或父级实体的位置。
  * `absolute`：绝对定位时，实体不参与正常布局流，而是独立于其兄弟节点进行布局。位置根据 `顶部`, `right`, `底部`，以及 `left` 取值列表。
* `位置`：位置值 `顶部`, `right`, `底部`，以及 `left` 的行为会因 `positionType`而异。对于相对实体，它们会按指定方向偏移实体的位置；而对于绝对实体，这些属性表示实体某一边相对于父级同一边的偏移。期望值是一个对象，其中包含属性 `顶部`, `left`, `底部`，以及 `right`.

{% hint style="warning" %}
**📔 注意** ： `顶部` 或 `left` 的正值表示从父级同一边向内测量的距离。示例：要将组件定位为与父级在顶部和左侧都留出 20 像素的边距，请设置 `位置` 为 `{ top: 20, left: 20 }`.
{% endhint %}

### 可见性

* `显示`：确定实体是否可见。要让实体不可见，请设置 `显示` 为 `无`.

### Z 索引

该 `zIndex` 属性决定 `UiEntity` 实体的渲染顺序。具有更高 `zIndex` 的实体会渲染在具有较低 `zIndex`的实体之上。默认 `zIndex` 值为 0。

```ts
import { UiEntity, ReactEcs } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export const uiMenu = () => (
	<UiEntity
		uiTransform={{
			zIndex: 4
		}}
		uiBackground={{ color: Color4.Green() }}
	/>
)
```

{% hint style="warning" %}
**📔 注意** ： `zIndex` 属性只会在直接兄弟元素之间排序，不能用于将实体渲染到布局树其他部分之上。用 html/CSS 的术语来说，每个 DCL UI 元素都会创建一个新的 [堆叠上下文](https://web.dev/learn/css/z-index#stacking_context).

默认的 Decentraland UI，包括地图、聊天等，始终渲染在所有其他 UI 元素之上。
{% endhint %}

## 可滚动容器

当 UI 实体的内容超出其分配的大小时，你可以通过设置 `overflow` 为 `scroll` 实体的 `uiTransform`来让该区域可滚动。玩家随后可以通过拖动或使用鼠标滚轮来滚动内容。

要创建可滚动容器，父实体必须具有固定大小（使用 `width` 和 `height`），且子项必须超出该大小。

```ts
import { UiEntity, Label, ReactEcs } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export const scrollableMenu = () => (
	<UiEntity
		uiTransform={{
			width: 300,
			height: 400,
			overflow: 'scroll',
			flexDirection: 'column',
		}}
		uiBackground={{ color: Color4.fromHexString('#1a1a1a') }}
	>
		{/* 这些子项超过了父级 400px 的高度，使该区域可滚动 */}
		<Label value="项目 1" fontSize={18} uiTransform={{ width: '100%', height: 80 }} />
		<Label value="项目 2" fontSize={18} uiTransform={{ width: '100%', height: 80 }} />
		<Label value="项目 3" fontSize={18} uiTransform={{ width: '100%', height: 80 }} />
		<Label value="项目 4" fontSize={18} uiTransform={{ width: '100%', height: 80 }} />
		<Label value="项目 5" fontSize={18} uiTransform={{ width: '100%', height: 80 }} />
		<Label value="项目 6" fontSize={18} uiTransform={{ width: '100%', height: 80 }} />
		<Label value="项目 7" fontSize={18} uiTransform={{ width: '100%', height: 80 }} />
	</UiEntity>
)
```

这对于构建长列表、库存、聊天记录、排行榜，或任何内容可能超出屏幕显示范围的面板都很有用。

你也可以将可滚动容器嵌套在其他 UI 布局中。例如，一个带固定头部和可滚动正文的对话框模态：

```ts
import { UiEntity, Label, ReactEcs } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export const dialogWithScroll = () => (
	<UiEntity
		uiTransform={{
			width: 400,
			height: 500,
			flexDirection: 'column',
		}}
		uiBackground={{ color: Color4.fromHexString('#2a2a2a') }}
	>
		{/* 固定头部 */}
		<Label
			value="排行榜"
			fontSize={22}
			uiTransform={{ width: '100%', height: 60 }}
		/>

		{/* 可滚动正文 */}
		<UiEntity
			uiTransform={{
				width: '100%',
				flexGrow: 1,
				overflow: 'scroll',
				flexDirection: 'column',
			}}
		>
			<Label value="1. Alice - 9500" fontSize={16} uiTransform={{ width: '100%', height: 50 }} />
			<Label value="2. Bob - 8200" fontSize={16} uiTransform={{ width: '100%', height: 50 }} />
			<Label value="3. Charlie - 7800" fontSize={16} uiTransform={{ width: '100%', height: 50 }} />
			<Label value="4. Diana - 6100" fontSize={16} uiTransform={{ width: '100%', height: 50 }} />
			<Label value="5. Eve - 5500" fontSize={16} uiTransform={{ width: '100%', height: 50 }} />
			<Label value="6. Frank - 4900" fontSize={16} uiTransform={{ width: '100%', height: 50 }} />
			<Label value="7. Grace - 4200" fontSize={16} uiTransform={{ width: '100%', height: 50 }} />
			<Label value="8. Hank - 3800" fontSize={16} uiTransform={{ width: '100%', height: 50 }} />
			<Label value="9. Ivy - 3100" fontSize={16} uiTransform={{ width: '100%', height: 50 }} />
			<Label value="10. Jack - 2700" fontSize={16} uiTransform={{ width: '100%', height: 50 }} />
		</UiEntity>
	</UiEntity>
)
```

{% hint style="info" %}
**💡 提示**：使用 `flexGrow: 1` 在可滚动实体上设置，以使其填充父级剩余空间，这样当其他兄弟节点（例如头部或页脚）改变大小时，它也能自适应。
{% endhint %}

## 响应式 UI 大小

不同屏幕尺寸的玩家可能会看到不同的 UI 布局。像素值会按 [虚拟屏幕](/creator/content-creator-zh/chang-jing-sdk7/2d-ui/onscreen-ui.md#screen-virtual-scale) 为你进行缩放，因此同一套 UI 能在不同分辨率下保持比例—— **你无需自己计算缩放因子**，否则会导致缩放被应用两次。

{% hint style="warning" %}
**📔 注意**: `devicePixelRatio` 不参与 UI 布局。它只是一个显示密度提示——用于在纹理的 1x、2x 或 3x 版本之间做选择——仅此而已。如果你的场景尺寸是按早期 SDK 版本设置的，那么在高密度（retina 和移动）屏幕上，按像素尺寸的 UI 可能会渲染得大 2 到 3 倍，并请重新检查任何手动微调过的内容。
{% endhint %}

{% hint style="danger" %}
**📔 移除你自己的缩放因子。** 如果你的场景把尺寸乘以一个从 `UiCanvasInformation` ——通常是 `Math.min(width / 1920, height / 1080)` ——请移除那个乘数。它与 SDK 现在默认应用的因子相同，因此两者都保留会让你的 UI 随屏幕尺寸呈二次增长。如果你更想只保留你自己的因子，请使用 `setUiRenderer(ui, { virtualWidth: 0, virtualHeight: 0 })`.
{% endhint %}

`UiCanvasInformation`，默认添加到场景根实体上的它，仍然是处理缩放无法表达的布局决策的正确工具——例如在窄屏上采用不同的对话框布局，或从 `devicePixelRatio`中选择纹理分辨率，或者自行读取内边距区域。它不是用于尺寸设定的合适工具。

该 `UiCanvasInformation` 组件包含以下信息：

* `height`：画布高度（像素）
* `width`：画布宽度（像素）
* `devicePixelRatio`：设备物理像素分辨率与画布像素之间的比率。可作为显示密度提示使用，例如在纹理的 1x、2x 或 3x 版本之间进行选择。
* `interactableArea`: 一个 `BorderRect` 对象，详细说明为场景 UI 元素指定的区域。该对象包含以下值： `顶部`, `底部`, `left` 和 `right`，其中每一项都是屏幕该边缘上被浏览器 UI 占用的像素数。
* `screenInsetArea`: 一个 `BorderRect` 对象，详细说明由设备或平台 UI 保留的屏幕内嵌区域（安全边距），例如移动设备上的刘海、状态栏、主页指示条或圆角。该对象包含以下值： `顶部`, `底部`, `left` 和 `right`，其中每一项都是屏幕该边缘预留的像素数。在桌面端，这通常是 `0` 四周都为 0。

{% hint style="warning" %}
**📔 注意** 不同的 Decentraland 浏览器对这些值会有不同的结果，因为平台的全局 UI 可能不同，而且当用户展开或隐藏不同的全局 UI 菜单时，这些值也可能动态变化。
{% endhint %}

```ts
import { engine, UiCanvasInformation } from "@dcl/sdk/ecs"

export function Main() {
  const canvas = UiCanvasInformation.getOrNull(engine.RootEntity)
  if (!canvas) return
  console.log("CANVAS DIMENSIONS: ", canvas.width, canvas.height)
}
```

关于 UI 尺寸的其他一些最佳实践：

* 如果任何 UI 元素的宽度或高度是动态的，最好也使用 `maxWidth`, `minWidth`, `maxHeight`，以及 `minHeight` 参数，以确保它们保持在合理范围内。
* 数字形式的字体大小是一个虚拟像素值，会像其他值一样被缩放。如果你想要一个相对于画布测量的大小，使其不受虚拟屏幕影响，请传入一个 `vw`/`vh` 字符串——参见 [响应式文本大小](/creator/content-creator-zh/chang-jing-sdk7/2d-ui/ui_text.md#responsive-text-size)

{% hint style="info" %}
**💡 提示**：有关 UI 尺寸的可运行示例，请参见 [`81,-2-ui-screen-inset-area`](https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/81,-2-ui-screen-inset-area) 测试场景，它会运行全部三种 `screenInset` 模式（`。具体覆盖范围由各浏览器决定——请在你目标的平台上验证。`, `'device'`, `'interactable'`）作为三个并存的渲染器，并打印实时的 `screenInsetArea` 和 `interactableArea` 值，以及 [`76,-10-UiCanvasInformation`](https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/76,-10-UiCanvasInformation)，它会读取 `UiCanvasInformation` 每一帧来以响应式方式调整 UI 尺寸。
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.decentraland.org/creator/content-creator-zh/chang-jing-sdk7/2d-ui/ui-positioning.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
