Skip to content

Flexbox 布局

单个矩形无趣——UI 的核心是多个元素的排列方式。Bevy 的默认布局算法是 Flexbox(和 CSS 的 Flexbox 同源),由 Nodeflex_direction 字句控制主轴方向。

行布局

flex_direction 设为 FlexDirection::Row,子节点就从左到右排列:

rust
use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .run();
}

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d);
    commands
        .spawn((
            Node {
                width: Val::Percent(100.0),
                height: Val::Percent(100.0),
                flex_direction: FlexDirection::Row,
                align_items: AlignItems::Center,
                column_gap: Val::Px(12.0),
                padding: UiRect::all(Val::Px(20.0)),
                ..default()
            },
            BackgroundColor(Color::srgb(0.1, 0.1, 0.1)),
        ))
        .with_children(|parent| {
            parent.spawn((
                Node {
                    width: Val::Px(80.0),
                    height: Val::Px(80.0),
                    ..default()
                },
                BackgroundColor(Color::srgb(0.9, 0.2, 0.2)),
            ));
            parent.spawn((
                Node {
                    width: Val::Px(80.0),
                    height: Val::Px(80.0),
                    ..default()
                },
                BackgroundColor(Color::srgb(0.2, 0.9, 0.2)),
            ));
            parent.spawn((
                Node {
                    width: Val::Px(80.0),
                    height: Val::Px(80.0),
                    ..default()
                },
                BackgroundColor(Color::srgb(0.2, 0.2, 0.9)),
            ));
        });
}

Listing 28-2:FlexDirection::Row——三个色块水平排列(examples/listing-28-02.rs)

这段代码用 with_children 闭包在父节点下添加三个子节点。column_gap: Val::Px(12.0) 在子节点之间插入 12 像素的间距,align_items: AlignItems::Center 让子节点在交叉轴(垂直方向)居中。

列布局

flex_direction 改为 FlexDirection::Column,子节点就从上到下排列:

rust
use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .run();
}

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d);
    commands
        .spawn((
            Node {
                width: Val::Percent(100.0),
                height: Val::Percent(100.0),
                flex_direction: FlexDirection::Column,
                justify_content: JustifyContent::Center,
                align_items: AlignItems::Center,
                row_gap: Val::Px(10.0),
                ..default()
            },
            BackgroundColor(Color::srgb(0.1, 0.1, 0.1)),
        ))
        .with_children(|parent| {
            parent.spawn((
                Node {
                    width: Val::Px(200.0),
                    height: Val::Px(50.0),
                    ..default()
                },
                BackgroundColor(Color::srgb(0.9, 0.3, 0.3)),
            ));
            parent.spawn((
                Node {
                    width: Val::Px(200.0),
                    height: Val::Px(50.0),
                    ..default()
                },
                BackgroundColor(Color::srgb(0.3, 0.9, 0.3)),
            ));
            parent.spawn((
                Node {
                    width: Val::Px(200.0),
                    height: Val::Px(50.0),
                    ..default()
                },
                BackgroundColor(Color::srgb(0.3, 0.3, 0.9)),
            ));
        });
}

Listing 28-3:FlexDirection::Column——三个色块垂直居中(examples/listing-28-03.rs)

justify_content: JustifyContent::Center 控制主轴(现在是垂直方向)的对齐——子节点被挤到正中间。row_gap 在列布局中控制行间距。

JustifyContent:主轴上的分布

justify_content 决定子节点在主轴上如何分布。常用值:

效果
FlexStart从主轴起点开始排列(默认)
FlexEnd从主轴终点开始排列
Center居中
SpaceBetween首尾贴边,中间均匀分
SpaceEvenly所有间距相等(包括首尾)
SpaceAround每个子节点两侧等距(首尾间距是中间的一半)

AlignItems:交叉轴上的对齐

align_items 控制子节点在交叉轴(与主轴垂直的方向)上的对齐方式:

效果
FlexStart贴交叉轴起点
FlexEnd贴交叉轴终点
Center居中
Stretch拉伸填满(默认)
Baseline按文字基线对齐

组合 justify_contentalign_items 就能表达绝大多数一维布局需求:

rust
use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .run();
}

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d);
    commands
        .spawn(Node {
            width: Val::Percent(100.0),
            height: Val::Percent(100.0),
            flex_direction: FlexDirection::Column,
            row_gap: Val::Px(16.0),
            padding: UiRect::all(Val::Px(24.0)),
            ..default()
        })
        .with_children(|parent| {
            spawn_row(parent, JustifyContent::FlexStart);
            spawn_row(parent, JustifyContent::Center);
            spawn_row(parent, JustifyContent::FlexEnd);
            spawn_row(parent, JustifyContent::SpaceBetween);
            spawn_row(parent, JustifyContent::SpaceEvenly);
        });
}

fn spawn_row(parent: &mut ChildSpawnerCommands, justify: JustifyContent) {
    parent
        .spawn((
            Node {
                width: Val::Percent(100.0),
                height: Val::Px(100.0),
                flex_direction: FlexDirection::Row,
                justify_content: justify,
                align_items: AlignItems::Center,
                padding: UiRect::all(Val::Px(8.0)),
                column_gap: Val::Px(8.0),
                ..default()
            },
            BackgroundColor(Color::srgb(0.2, 0.2, 0.2)),
        ))
        .with_children(|parent| {
            spawn_box(parent, Color::srgb(0.9, 0.3, 0.3));
            spawn_box(parent, Color::srgb(0.3, 0.9, 0.3));
            spawn_box(parent, Color::srgb(0.3, 0.3, 0.9));
        });
}

fn spawn_box(parent: &mut ChildSpawnerCommands, color: Color) {
    parent.spawn((
        Node {
            width: Val::Px(50.0),
            height: Val::Px(50.0),
            ..default()
        },
        BackgroundColor(color),
    ));
}

Listing 28-4:JustifyContent 的五种取值对比(examples/listing-28-04.rs)

子节点的 flex_grow

flex_grow(默认 0)控制子节点是否"吃掉"剩余空间。设为 1 就意味着"在所有 flex_grow 为 1 的兄弟之间按比例分配剩余空间"。这在"侧边栏固定宽度、主内容区填满"的布局中极为常用——Listing 28-5 会演示。

Reverse:反向排列

FlexDirection::RowReverseFlexDirection::ColumnReverse 让子节点从终点向起点排列。用途不多,但在某些 RTL(从右到左)布局或动画中偶尔需要。

小结:flex_direction 决定主轴方向,justify_content 控制主轴分布,align_items 控制交叉轴对齐,flex_grow 让子节点分配剩余空间。这四个字段覆盖了一维布局的绝大多数场景。