Skip to main content

gradients/
gradients.rs

1//! Simple example demonstrating linear gradients.
2
3use bevy::color::palettes::css::BLUE;
4use bevy::color::palettes::css::GREEN;
5use bevy::color::palettes::css::INDIGO;
6use bevy::color::palettes::css::LIME;
7use bevy::color::palettes::css::ORANGE;
8use bevy::color::palettes::css::RED;
9use bevy::color::palettes::css::VIOLET;
10use bevy::color::palettes::css::YELLOW;
11use bevy::prelude::*;
12
13use bevy::ui::ColorStop;
14use std::f32::consts::TAU;
15
16#[derive(Component)]
17struct CurrentColorSpaceLabel;
18
19fn main() {
20    App::new()
21        .add_plugins(DefaultPlugins)
22        .add_systems(Startup, setup)
23        .add_systems(Update, update)
24        .run();
25}
26
27fn setup(mut commands: Commands) {
28    commands.spawn(Camera2d);
29
30    commands
31        .spawn(Node {
32            flex_direction: FlexDirection::Column,
33            row_gap: px(20),
34            margin: UiRect::all(px(20)),
35            ..Default::default()
36        })
37        .with_children(|commands| {
38            for (b, stops) in [
39                (
40                    4.,
41                    vec![
42                        ColorStop::new(Color::WHITE, percent(15)),
43                        ColorStop::new(Color::BLACK, percent(85)),
44                    ],
45                ),
46                (4., vec![RED.into(), BLUE.into(), LIME.into()]),
47                (
48                    0.,
49                    vec![
50                        RED.into(),
51                        ColorStop::new(RED, percent(100. / 7.)),
52                        ColorStop::new(ORANGE, percent(100. / 7.)),
53                        ColorStop::new(ORANGE, percent(200. / 7.)),
54                        ColorStop::new(YELLOW, percent(200. / 7.)),
55                        ColorStop::new(YELLOW, percent(300. / 7.)),
56                        ColorStop::new(GREEN, percent(300. / 7.)),
57                        ColorStop::new(GREEN, percent(400. / 7.)),
58                        ColorStop::new(BLUE, percent(400. / 7.)),
59                        ColorStop::new(BLUE, percent(500. / 7.)),
60                        ColorStop::new(INDIGO, percent(500. / 7.)),
61                        ColorStop::new(INDIGO, percent(600. / 7.)),
62                        ColorStop::new(VIOLET, percent(600. / 7.)),
63                        VIOLET.into(),
64                    ],
65                ),
66            ] {
67                commands.spawn(Node::default()).with_children(|commands| {
68                    commands
69                        .spawn(Node {
70                            flex_direction: FlexDirection::Column,
71                            row_gap: px(5),
72                            ..Default::default()
73                        })
74                        .with_children(|commands| {
75                            for (w, h) in [(70., 70.), (35., 70.), (70., 35.)] {
76                                commands
77                                    .spawn(Node {
78                                        column_gap: px(10),
79                                        ..Default::default()
80                                    })
81                                    .with_children(|commands| {
82                                        for angle in (0..8).map(|i| i as f32 * TAU / 8.) {
83                                            commands.spawn((
84                                                Node {
85                                                    width: px(w),
86                                                    height: px(h),
87                                                    border: UiRect::all(px(b)),
88                                                    border_radius: BorderRadius::all(px(20)),
89                                                    ..default()
90                                                },
91                                                BackgroundGradient::from(LinearGradient {
92                                                    angle,
93                                                    stops: stops.clone(),
94                                                    ..default()
95                                                }),
96                                                BorderGradient::from(LinearGradient {
97                                                    angle: 3. * TAU / 8.,
98                                                    stops: vec![
99                                                        YELLOW.into(),
100                                                        Color::WHITE.into(),
101                                                        ORANGE.into(),
102                                                    ],
103                                                    ..default()
104                                                }),
105                                            ));
106                                        }
107                                    });
108                            }
109                        });
110
111                    commands.spawn(Node::default()).with_children(|commands| {
112                        commands.spawn((
113                            Node {
114                                aspect_ratio: Some(1.),
115                                height: percent(100),
116                                border: UiRect::all(px(b)),
117                                margin: UiRect::left(px(20)),
118                                border_radius: BorderRadius::all(px(20)),
119                                ..default()
120                            },
121                            BackgroundGradient::from(LinearGradient {
122                                angle: 0.,
123                                stops: stops.clone(),
124                                ..default()
125                            }),
126                            BorderGradient::from(LinearGradient {
127                                angle: 3. * TAU / 8.,
128                                stops: vec![YELLOW.into(), Color::WHITE.into(), ORANGE.into()],
129                                ..default()
130                            }),
131                            AnimateMarker,
132                        ));
133
134                        commands.spawn((
135                            Node {
136                                aspect_ratio: Some(1.),
137                                height: percent(100),
138                                border: UiRect::all(px(b)),
139                                margin: UiRect::left(px(20)),
140                                border_radius: BorderRadius::all(px(20)),
141                                ..default()
142                            },
143                            BackgroundGradient::from(RadialGradient {
144                                stops: stops.clone(),
145                                shape: RadialGradientShape::ClosestSide,
146                                position: UiPosition::CENTER,
147                                ..default()
148                            }),
149                            BorderGradient::from(LinearGradient {
150                                angle: 3. * TAU / 8.,
151                                stops: vec![YELLOW.into(), Color::WHITE.into(), ORANGE.into()],
152                                ..default()
153                            }),
154                            AnimateMarker,
155                        ));
156                        commands.spawn((
157                            Node {
158                                aspect_ratio: Some(1.),
159                                height: percent(100),
160                                border: UiRect::all(px(b)),
161                                margin: UiRect::left(px(20)),
162                                border_radius: BorderRadius::all(px(20)),
163                                ..default()
164                            },
165                            BackgroundGradient::from(ConicGradient {
166                                start: 0.,
167                                stops: stops
168                                    .iter()
169                                    .map(|stop| AngularColorStop::auto(stop.color))
170                                    .collect(),
171                                position: UiPosition::CENTER,
172                                ..default()
173                            }),
174                            BorderGradient::from(LinearGradient {
175                                angle: 3. * TAU / 8.,
176                                stops: vec![YELLOW.into(), Color::WHITE.into(), ORANGE.into()],
177                                ..default()
178                            }),
179                            AnimateMarker,
180                        ));
181                    });
182                });
183            }
184
185            let button = commands.spawn((
186                        Button,
187                        Node {
188                            border: UiRect::all(px(2)),
189                            padding: UiRect::axes(px(8), px(4)),
190                            // horizontally center child text
191                            justify_content: JustifyContent::Center,
192                            // vertically center child text
193                            align_items: AlignItems::Center,
194                            border_radius: BorderRadius::MAX,
195                            ..default()
196                        },
197                        BorderColor::all(Color::WHITE),
198                        BackgroundColor(Color::BLACK),
199                        children![(
200                            Text::new("next color space"),
201                            TextColor(Color::srgb(0.9, 0.9, 0.9)),
202                            TextShadow::default(),
203                        )]
204                )).observe(
205                    |_event: On<Pointer<Over>>, mut border_query: Query<&mut BorderColor, With<Button>>| {
206                    *border_query.single_mut().unwrap() = BorderColor::all(RED);
207
208
209                })
210                .observe(
211                    |_event: On<Pointer<Out>>, mut border_query: Query<&mut BorderColor, With<Button>>| {
212                    *border_query.single_mut().unwrap() = BorderColor::all(Color::WHITE);
213                })
214                .observe(
215                        |_event: On<Pointer<Click>>,
216                            mut gradients_query: Query<&mut BackgroundGradient>,
217                            mut label_query: Query<
218                            &mut Text,
219                            With<CurrentColorSpaceLabel>,
220                        >| {
221                            let mut current_space = InterpolationColorSpace::default();
222                            for mut gradients in gradients_query.iter_mut() {
223                                for gradient in gradients.0.iter_mut() {
224                                    let space = match gradient {
225                                        Gradient::Linear(linear_gradient) => {
226                                            &mut linear_gradient.color_space
227                                        }
228                                        Gradient::Radial(radial_gradient) => {
229                                            &mut radial_gradient.color_space
230                                        }
231                                        Gradient::Conic(conic_gradient) => {
232                                            &mut conic_gradient.color_space
233                                        }
234                                    };
235                                    *space = match *space {
236                                        InterpolationColorSpace::Oklaba => {
237                                            InterpolationColorSpace::Oklcha
238                                        }
239                                        InterpolationColorSpace::Oklcha => {
240                                            InterpolationColorSpace::OklchaLong
241                                        }
242                                        InterpolationColorSpace::OklchaLong => {
243                                            InterpolationColorSpace::Srgba
244                                        }
245                                        InterpolationColorSpace::Srgba => {
246                                            InterpolationColorSpace::LinearRgba
247                                        }
248                                        InterpolationColorSpace::LinearRgba => {
249                                            InterpolationColorSpace::Hsla
250                                        }
251                                        InterpolationColorSpace::Hsla => {
252                                            InterpolationColorSpace::HslaLong
253                                        }
254                                        InterpolationColorSpace::HslaLong => {
255                                            InterpolationColorSpace::Hsva
256                                        }
257                                        InterpolationColorSpace::Hsva => {
258                                            InterpolationColorSpace::HsvaLong
259                                        }
260                                        InterpolationColorSpace::HsvaLong => {
261                                            InterpolationColorSpace::Oklaba
262                                        }
263                                    };
264                                    current_space = *space;
265                                }
266                            }
267                            for mut label in label_query.iter_mut() {
268                                label.0 = format!("{current_space:?}");
269                            }
270                        }
271                    ).id();
272
273            commands.spawn(
274                Node {
275                    flex_direction: FlexDirection::Column,
276                    row_gap: px(10),
277                    align_items: AlignItems::Center,
278                    ..Default::default()
279                }
280            ).with_children(|commands| {
281                commands.spawn((Text::new(format!("{:?}", InterpolationColorSpace::default())), TextFont { font_size: FontSize::Px(25.), ..default() }, CurrentColorSpaceLabel));
282
283            })
284            .add_child(button);
285        });
286}
287
288#[derive(Component)]
289struct AnimateMarker;
290
291fn update(time: Res<Time>, mut query: Query<&mut BackgroundGradient, With<AnimateMarker>>) {
292    for mut gradients in query.iter_mut() {
293        for gradient in gradients.0.iter_mut() {
294            if let Gradient::Linear(LinearGradient { angle, .. }) = gradient {
295                *angle += 0.5 * time.delta_secs();
296            }
297        }
298    }
299}