Skip to main content

overflow_transform/
overflow_transform.rs

1//! Displays a rotating node with text and images, clipped by multiple rotating ancestor nodes.
2
3use bevy::color::palettes::css::LIGHT_BLUE;
4use bevy::color::palettes::css::NAVY;
5use bevy::math::ops::sin;
6use bevy::prelude::*;
7
8fn main() {
9    App::new()
10        .add_plugins(DefaultPlugins)
11        .add_systems(Startup, setup)
12        .add_systems(Update, (rotate_nodes, scale_inner))
13        .run();
14}
15
16#[derive(Component)]
17struct RotatingClipLayer {
18    base_rotation: f32,
19    speed: f32,
20}
21
22fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
23    commands.spawn(Camera2d);
24
25    commands
26        .spawn(Node {
27            width: percent(100),
28            height: percent(100),
29            align_items: AlignItems::Center,
30            justify_content: JustifyContent::Center,
31            ..default()
32        })
33        .with_children(|parent| {
34            parent
35                .spawn(rotating_node(
36                    400.,
37                    0.35,
38                    0.18,
39                    Color::srgb(0.12, 0.17, 0.22),
40                ))
41                .with_children(|parent| {
42                    parent
43                        .spawn(rotating_node(
44                            350.,
45                            -0.5,
46                            -0.4,
47                            Color::srgb(0.24, 0.18, 0.32),
48                        ))
49                        .with_children(|parent| {
50                            parent
51                                .spawn(rotating_node(
52                                    300.,
53                                    0.65,
54                                    0.55,
55                                    Color::srgb(0.15, 0.30, 0.25),
56                                ))
57                                .with_children(|parent| {
58                                    parent.spawn((
59                                        Node {
60                                            flex_direction: FlexDirection::Column,
61                                            position_type: PositionType::Absolute,
62                                            margin: auto().all(),
63                                            border_radius: BorderRadius::all(percent(20.)),
64                                            align_items: AlignItems::Center,
65                                            justify_content: JustifyContent::SpaceAround,
66                                            border: px(5.).all(),
67                                            padding: px(5.).all(),
68                                            ..default()
69                                        },
70                                        BoxShadow::new(
71                                            Color::srgb(0.15, 0.30, 0.25).darker(0.05),
72                                            px(30),
73                                            px(30),
74                                            px(0),
75                                            px(4),
76                                        ),
77                                        InnerNode,
78                                        BackgroundColor(NAVY.into()),
79                                        BorderColor::all(LIGHT_BLUE),
80                                        UiTransform::from_rotation(Rot2::degrees(45.)),
81                                        children![
82                                            (
83                                                ImageNode::new(
84                                                    asset_server
85                                                        .load("branding/bevy_logo_dark_big.png"),
86                                                ),
87                                                Node {
88                                                    width: px(400),
89                                                    ..default()
90                                                },
91                                            ),
92                                            (
93                                                Text::new("transform + overflow"),
94                                                TextFont {
95                                                    font: asset_server
96                                                        .load("fonts/FiraSans-Bold.ttf")
97                                                        .into(),
98                                                    font_size: FontSize::Px(34.),
99                                                    ..default()
100                                                },
101                                                TextColor(Color::WHITE),
102                                            )
103                                        ],
104                                    ));
105                                });
106                        });
107                });
108        });
109}
110
111fn rotate_nodes(time: Res<Time>, mut query: Query<(&RotatingClipLayer, &mut UiTransform)>) {
112    for (layer, mut transform) in &mut query {
113        transform.rotation = Rot2::radians(layer.base_rotation + time.elapsed_secs() * layer.speed);
114    }
115}
116
117fn rotating_node(size: f32, rotation: f32, speed: f32, color: Color) -> impl Bundle {
118    (
119        Node {
120            width: px(size),
121            height: px(size),
122            overflow: Overflow::clip(),
123            align_items: AlignItems::Center,
124            justify_content: JustifyContent::Center,
125            border: UiRect::all(px(4)),
126            ..default()
127        },
128        BackgroundColor(color),
129        BorderColor::all(Color::WHITE.with_alpha(0.7)),
130        UiTransform::from_rotation(Rot2::radians(rotation)),
131        RotatingClipLayer {
132            base_rotation: rotation,
133            speed,
134        },
135    )
136}
137
138#[derive(Component)]
139struct InnerNode;
140
141fn scale_inner(time: Res<Time>, mut query: Query<&mut UiTransform, With<InnerNode>>) {
142    for mut transform in query.iter_mut() {
143        transform.scale = Vec2::splat(1. + 0.75 * sin(0.4 * time.elapsed_secs()));
144    }
145}