UI (User Interface) / Overflow Transform

Back to examples View in GitHub

Displays a rotating node with text and images, clipped by multiple rotating ancestor nodes.

use bevy::color::palettes::css::LIGHT_BLUE;
use bevy::color::palettes::css::NAVY;
use bevy::math::ops::sin;
use bevy::prelude::*;

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

#[derive(Component)]
struct RotatingClipLayer {
    base_rotation: f32,
    speed: f32,
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2d);

    commands
        .spawn(Node {
            width: percent(100),
            height: percent(100),
            align_items: AlignItems::Center,
            justify_content: JustifyContent::Center,
            ..default()
        })
        .with_children(|parent| {
            parent
                .spawn(rotating_node(
                    400.,
                    0.35,
                    0.18,
                    Color::srgb(0.12, 0.17, 0.22),
                ))
                .with_children(|parent| {
                    parent
                        .spawn(rotating_node(
                            350.,
                            -0.5,
                            -0.4,
                            Color::srgb(0.24, 0.18, 0.32),
                        ))
                        .with_children(|parent| {
                            parent
                                .spawn(rotating_node(
                                    300.,
                                    0.65,
                                    0.55,
                                    Color::srgb(0.15, 0.30, 0.25),
                                ))
                                .with_children(|parent| {
                                    parent.spawn((
                                        Node {
                                            flex_direction: FlexDirection::Column,
                                            position_type: PositionType::Absolute,
                                            margin: auto().all(),
                                            border_radius: BorderRadius::all(percent(20.)),
                                            align_items: AlignItems::Center,
                                            justify_content: JustifyContent::SpaceAround,
                                            border: px(5.).all(),
                                            padding: px(5.).all(),
                                            ..default()
                                        },
                                        BoxShadow::new(
                                            Color::srgb(0.15, 0.30, 0.25).darker(0.05),
                                            px(30),
                                            px(30),
                                            px(0),
                                            px(4),
                                        ),
                                        InnerNode,
                                        BackgroundColor(NAVY.into()),
                                        BorderColor::all(LIGHT_BLUE),
                                        UiTransform::from_rotation(Rot2::degrees(45.)),
                                        children![
                                            (
                                                ImageNode::new(
                                                    asset_server
                                                        .load("branding/bevy_logo_dark_big.png"),
                                                ),
                                                Node {
                                                    width: px(400),
                                                    ..default()
                                                },
                                            ),
                                            (
                                                Text::new("transform + overflow"),
                                                TextFont {
                                                    font: asset_server
                                                        .load("fonts/FiraSans-Bold.ttf")
                                                        .into(),
                                                    font_size: FontSize::Px(34.),
                                                    ..default()
                                                },
                                                TextColor(Color::WHITE),
                                            )
                                        ],
                                    ));
                                });
                        });
                });
        });
}

fn rotate_nodes(time: Res<Time>, mut query: Query<(&RotatingClipLayer, &mut UiTransform)>) {
    for (layer, mut transform) in &mut query {
        transform.rotation = Rot2::radians(layer.base_rotation + time.elapsed_secs() * layer.speed);
    }
}

fn rotating_node(size: f32, rotation: f32, speed: f32, color: Color) -> impl Bundle {
    (
        Node {
            width: px(size),
            height: px(size),
            overflow: Overflow::clip(),
            align_items: AlignItems::Center,
            justify_content: JustifyContent::Center,
            border: UiRect::all(px(4)),
            ..default()
        },
        BackgroundColor(color),
        BorderColor::all(Color::WHITE.with_alpha(0.7)),
        UiTransform::from_rotation(Rot2::radians(rotation)),
        RotatingClipLayer {
            base_rotation: rotation,
            speed,
        },
    )
}

#[derive(Component)]
struct InnerNode;

fn scale_inner(time: Res<Time>, mut query: Query<&mut UiTransform, With<InnerNode>>) {
    for mut transform in query.iter_mut() {
        transform.scale = Vec2::splat(1. + 0.75 * sin(0.4 * time.elapsed_secs()));
    }
}