animated_ui/
animated_ui.rs1use bevy::{
4 animation::{
5 animated_field, AnimationEntityMut, AnimationEvaluationError, AnimationTarget,
6 AnimationTargetId,
7 },
8 prelude::*,
9};
10use std::any::TypeId;
11
12struct AnimationInfo {
14 target_name: Name,
16 target_id: AnimationTargetId,
18 graph: Handle<AnimationGraph>,
20 node_index: AnimationNodeIndex,
22}
23
24fn main() {
26 App::new()
27 .add_plugins(DefaultPlugins)
28 .add_systems(Startup, setup)
31 .run();
32}
33
34impl AnimationInfo {
35 fn create(
37 animation_graphs: &mut Assets<AnimationGraph>,
38 animation_clips: &mut Assets<AnimationClip>,
39 ) -> AnimationInfo {
40 let animation_target_name = Name::new("Text");
42 let animation_target_id = AnimationTargetId::from_name(&animation_target_name);
43
44 let mut animation_clip = AnimationClip::default();
46
47 animation_clip.add_curve_to_target(
49 animation_target_id,
50 AnimatableCurve::new(
51 animated_field!(TextFont::font_size),
52 AnimatableKeyframeCurve::new(
53 [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0]
54 .into_iter()
55 .zip([24.0, 80.0, 24.0, 80.0, 24.0, 80.0, 24.0]),
56 )
57 .expect(
58 "should be able to build translation curve because we pass in valid samples",
59 ),
60 ),
61 );
62
63 animation_clip.add_curve_to_target(
69 animation_target_id,
70 AnimatableCurve::new(
71 TextColorProperty,
72 AnimatableKeyframeCurve::new([0.0, 1.0, 2.0, 3.0].into_iter().zip([
73 Srgba::RED,
74 Srgba::GREEN,
75 Srgba::BLUE,
76 Srgba::RED,
77 ]))
78 .expect(
79 "should be able to build translation curve because we pass in valid samples",
80 ),
81 ),
82 );
83
84 let animation_clip_handle = animation_clips.add(animation_clip);
86
87 let (animation_graph, animation_node_index) =
89 AnimationGraph::from_clip(animation_clip_handle);
90 let animation_graph_handle = animation_graphs.add(animation_graph);
91
92 AnimationInfo {
93 target_name: animation_target_name,
94 target_id: animation_target_id,
95 graph: animation_graph_handle,
96 node_index: animation_node_index,
97 }
98 }
99}
100
101fn setup(
103 mut commands: Commands,
104 asset_server: Res<AssetServer>,
105 mut animation_graphs: ResMut<Assets<AnimationGraph>>,
106 mut animation_clips: ResMut<Assets<AnimationClip>>,
107) {
108 let AnimationInfo {
110 target_name: animation_target_name,
111 target_id: animation_target_id,
112 graph: animation_graph,
113 node_index: animation_node_index,
114 } = AnimationInfo::create(&mut animation_graphs, &mut animation_clips);
115
116 let mut animation_player = AnimationPlayer::default();
118 animation_player.play(animation_node_index).repeat();
119
120 commands.spawn(Camera2d);
122
123 commands
127 .spawn((
128 Node {
130 position_type: PositionType::Absolute,
131 top: Val::Px(0.0),
132 left: Val::Px(0.0),
133 right: Val::Px(0.0),
134 bottom: Val::Px(0.0),
135 justify_content: JustifyContent::Center,
136 align_items: AlignItems::Center,
137 ..default()
138 },
139 animation_player,
140 AnimationGraphHandle(animation_graph),
141 ))
142 .with_children(|builder| {
143 let player = builder.parent_entity();
145 builder
146 .spawn((
147 Text::new("Bevy"),
148 TextFont {
149 font: asset_server.load("fonts/FiraSans-Bold.ttf"),
150 font_size: 24.0,
151 ..default()
152 },
153 TextColor(Color::Srgba(Srgba::RED)),
154 TextLayout::new_with_justify(JustifyText::Center),
155 ))
156 .insert(AnimationTarget {
158 id: animation_target_id,
159 player,
160 })
161 .insert(animation_target_name);
162 });
163}
164
165#[derive(Clone)]
169struct TextColorProperty;
170
171impl AnimatableProperty for TextColorProperty {
172 type Property = Srgba;
173
174 fn evaluator_id(&self) -> EvaluatorId {
175 EvaluatorId::Type(TypeId::of::<Self>())
176 }
177
178 fn get_mut<'a>(
179 &self,
180 entity: &'a mut AnimationEntityMut,
181 ) -> Result<&'a mut Self::Property, AnimationEvaluationError> {
182 let text_color = entity
183 .get_mut::<TextColor>()
184 .ok_or(AnimationEvaluationError::ComponentNotPresent(TypeId::of::<
185 TextColor,
186 >(
187 )))?
188 .into_inner();
189 match text_color.0 {
190 Color::Srgba(ref mut color) => Ok(color),
191 _ => Err(AnimationEvaluationError::PropertyNotPresent(TypeId::of::<
192 Srgba,
193 >(
194 ))),
195 }
196 }
197}