1use bevy::{color::palettes::css::*, prelude::*};
4
5use core::time::Duration;
6
7const SCALE_TIME: u64 = 400;
8
9fn main() {
10 App::new()
11 .add_plugins(DefaultPlugins)
12 .insert_resource(TargetScale {
13 start_scale: 1.0,
14 target_scale: 1.0,
15 target_time: Timer::new(Duration::from_millis(SCALE_TIME), TimerMode::Once),
16 })
17 .add_systems(Startup, setup)
18 .add_systems(
19 Update,
20 (change_scaling, apply_scaling.after(change_scaling)),
21 )
22 .run();
23}
24
25fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
26 commands.spawn(Camera2d);
27
28 let text_font = TextFont {
29 font_size: FontSize::Px(13.),
30 ..default()
31 };
32
33 commands
34 .spawn((
35 Node {
36 width: percent(50),
37 height: percent(50),
38 position_type: PositionType::Absolute,
39 left: percent(25),
40 top: percent(25),
41 justify_content: JustifyContent::SpaceAround,
42 align_items: AlignItems::Center,
43 ..default()
44 },
45 BackgroundColor(ANTIQUE_WHITE.into()),
46 ))
47 .with_children(|parent| {
48 parent
49 .spawn((
50 Node {
51 width: px(40),
52 height: px(40),
53 ..default()
54 },
55 BackgroundColor(RED.into()),
56 ))
57 .with_children(|parent| {
58 parent.spawn((Text::new("Size!"), text_font, TextColor::BLACK));
59 });
60 parent.spawn((
61 Node {
62 width: percent(15),
63 height: percent(15),
64 ..default()
65 },
66 BackgroundColor(BLUE.into()),
67 ));
68 parent.spawn((
69 ImageNode::new(asset_server.load("branding/icon.png")),
70 Node {
71 width: px(30),
72 height: px(30),
73 ..default()
74 },
75 ));
76 });
77}
78
79fn change_scaling(input: Res<ButtonInput<KeyCode>>, mut ui_scale: ResMut<TargetScale>) {
81 if input.just_pressed(KeyCode::ArrowUp) {
82 let scale = (ui_scale.target_scale * 2.0).min(8.);
83 ui_scale.set_scale(scale);
84 info!("Scaling up! Scale: {}", ui_scale.target_scale);
85 }
86 if input.just_pressed(KeyCode::ArrowDown) {
87 let scale = (ui_scale.target_scale / 2.0).max(1. / 8.);
88 ui_scale.set_scale(scale);
89 info!("Scaling down! Scale: {}", ui_scale.target_scale);
90 }
91}
92
93#[derive(Resource)]
94struct TargetScale {
95 start_scale: f32,
96 target_scale: f32,
97 target_time: Timer,
98}
99
100impl TargetScale {
101 fn set_scale(&mut self, scale: f32) {
102 self.start_scale = self.current_scale();
103 self.target_scale = scale;
104 self.target_time.reset();
105 }
106
107 fn current_scale(&self) -> f32 {
108 let completion = self.target_time.fraction();
109 let t = ease_in_expo(completion);
110 self.start_scale.lerp(self.target_scale, t)
111 }
112
113 fn tick(&mut self, delta: Duration) -> &Self {
114 self.target_time.tick(delta);
115 self
116 }
117
118 fn already_completed(&self) -> bool {
119 self.target_time.is_finished() && !self.target_time.just_finished()
120 }
121}
122
123fn apply_scaling(
124 time: Res<Time>,
125 mut target_scale: ResMut<TargetScale>,
126 mut ui_scale: ResMut<UiScale>,
127) {
128 if target_scale.tick(time.delta()).already_completed() {
129 return;
130 }
131
132 ui_scale.0 = target_scale.current_scale();
133}
134
135fn ease_in_expo(x: f32) -> f32 {
136 if x == 0. {
137 0.
138 } else {
139 ops::powf(2.0f32, 5. * x - 5.)
140 }
141}