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