1use super::*;
2
3impl SpriteSheet {
5 pub fn from_image(image: HtmlImageElement, frame_width: f64, frame_height: f64) -> SpriteSheet {
19 let total_width: f64 = image.width() as f64;
20 let total_height: f64 = image.height() as f64;
21 let columns: u32 = (total_width / frame_width).max(1.0) as u32;
22 let rows: u32 = (total_height / frame_height).max(1.0) as u32;
23 SpriteSheet::new(image, frame_width, frame_height, columns, rows)
24 }
25
26 pub fn frame_source(&self, index: u32) -> Rect {
36 let column: u32 = index % self.get_columns();
37 let row: u32 = index / self.get_columns();
38 Rect::new(
39 column as f64 * self.get_frame_width(),
40 row as f64 * self.get_frame_height(),
41 self.get_frame_width(),
42 self.get_frame_height(),
43 )
44 }
45
46 pub fn frame(&self, index: u32) -> SpriteFrame {
56 SpriteFrame::new(self.frame_source(index), SPRITE_DEFAULT_FRAME_DURATION)
57 }
58
59 pub fn animation(
72 &self,
73 name: &str,
74 start: u32,
75 end: u32,
76 mode: AnimationMode,
77 ) -> SpriteAnimation {
78 let frames: Vec<SpriteFrame> = (start..end).map(|index: u32| self.frame(index)).collect();
79 SpriteAnimation::new(name.to_string(), frames, mode)
80 }
81
82 pub fn draw_frame(
90 &self,
91 context: &CanvasRenderingContext2d,
92 frame_index: u32,
93 transform: &Transform2D,
94 ) {
95 let source: Rect = self.frame_source(frame_index);
96 let rotation: f64 = transform.get_rotation();
100 let cos: f64 = rotation.cos();
101 let sin: f64 = rotation.sin();
102 let scale_x: f64 = transform.get_scale().get_x();
103 let scale_y: f64 = transform.get_scale().get_y();
104 let _: Result<(), JsValue> = context.set_transform(
105 cos * scale_x,
106 sin * scale_x,
107 -sin * scale_y,
108 cos * scale_y,
109 transform.get_position().get_x(),
110 transform.get_position().get_y(),
111 );
112 let _: Result<(), JsValue> = context
113 .draw_image_with_html_image_element_and_sw_and_sh_and_dx_and_dy_and_dw_and_dh(
114 self.get_image(),
115 source.get_x(),
116 source.get_y(),
117 source.get_width(),
118 source.get_height(),
119 -source.get_width() * 0.5,
120 -source.get_height() * 0.5,
121 source.get_width(),
122 source.get_height(),
123 );
124 let _: Result<(), JsValue> = context.set_transform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
125 }
126}
127
128impl Animator {
130 pub fn create() -> Animator {
136 Animator::new(AnimationState::Paused, 1)
137 }
138
139 pub fn play(&mut self, animation: SpriteAnimation) {
145 self.set_current_animation(Some(animation));
146 self.set_current_frame_index(0);
147 self.set_elapsed_time(0.0);
148 self.set_state(AnimationState::Playing);
149 self.set_direction(1);
150 }
151
152 pub fn pause(&mut self) {
154 if self.get_state() == AnimationState::Playing {
155 self.set_state(AnimationState::Paused);
156 }
157 }
158
159 pub fn resume(&mut self) {
161 if self.get_state() == AnimationState::Paused {
162 self.set_state(AnimationState::Playing);
163 }
164 }
165
166 pub fn stop(&mut self) {
168 self.set_current_frame_index(0);
169 self.set_elapsed_time(0.0);
170 self.set_state(AnimationState::Paused);
171 self.set_direction(1);
172 }
173
174 pub fn update(&mut self, delta_time: f64) {
180 if self.get_state() != AnimationState::Playing {
181 return;
182 }
183 let current_frame_index: usize = self.get_current_frame_index();
184 let (frame_count, current_duration, mode) = {
185 let Some(animation) = self.get_mut_current_animation().as_ref() else {
186 return;
187 };
188 if animation.get_frames().is_empty() {
189 return;
190 }
191 (
192 animation.get_frames().len(),
193 animation.get_frames()[current_frame_index].get_duration(),
194 animation.get_mode(),
195 )
196 };
197 *self.get_mut_elapsed_time() += delta_time;
198 if self.get_elapsed_time() < current_duration {
199 return;
200 }
201 self.set_elapsed_time(0.0);
202 self.advance_frame(frame_count, mode);
203 }
204
205 pub fn current_frame_source(&self) -> Option<Rect> {
211 let animation: Option<SpriteAnimation> = self.get_current_animation();
212 let animation: &SpriteAnimation = animation.as_ref()?;
213 let frame: &SpriteFrame = animation.get_frames().get(self.get_current_frame_index())?;
214 Some(frame.get_source())
215 }
216
217 fn advance_frame(&mut self, frame_count: usize, mode: AnimationMode) {
223 match mode {
224 AnimationMode::Loop => {
225 self.set_current_frame_index((self.get_current_frame_index() + 1) % frame_count);
226 }
227 AnimationMode::Once => {
228 if self.get_current_frame_index() + 1 < frame_count {
229 *self.get_mut_current_frame_index() += 1;
230 } else {
231 self.set_state(AnimationState::Finished);
232 }
233 }
234 AnimationMode::PingPong => {
235 if self.get_direction() > 0 {
236 if self.get_current_frame_index() + 1 < frame_count {
237 *self.get_mut_current_frame_index() += 1;
238 } else {
239 self.set_direction(-1);
240 if self.get_current_frame_index() > 0 {
241 *self.get_mut_current_frame_index() -= 1;
242 }
243 }
244 } else {
245 if self.get_current_frame_index() > 0 {
246 *self.get_mut_current_frame_index() -= 1;
247 } else {
248 self.set_direction(1);
249 if self.get_current_frame_index() + 1 < frame_count {
250 *self.get_mut_current_frame_index() += 1;
251 }
252 }
253 }
254 }
255 }
256 }
257}
258
259impl Updatable for Animator {
267 fn update(&mut self, delta_time: f64) {
268 Animator::update(self, delta_time);
269 }
270}
271
272impl Animator {
274 pub fn draw(
282 &self,
283 context: &CanvasRenderingContext2d,
284 sheet: &SpriteSheet,
285 transform: &Transform2D,
286 ) {
287 let Some(source) = self.current_frame_source() else {
288 return;
289 };
290 let rotation: f64 = transform.get_rotation();
293 let cos: f64 = rotation.cos();
294 let sin: f64 = rotation.sin();
295 let scale_x: f64 = if self.get_flip_x() {
296 -transform.get_scale().get_x()
297 } else {
298 transform.get_scale().get_x()
299 };
300 let scale_y: f64 = if self.get_flip_y() {
301 -transform.get_scale().get_y()
302 } else {
303 transform.get_scale().get_y()
304 };
305 let _: Result<(), JsValue> = context.set_transform(
306 cos * scale_x,
307 sin * scale_x,
308 -sin * scale_y,
309 cos * scale_y,
310 transform.get_position().get_x(),
311 transform.get_position().get_y(),
312 );
313 let _: Result<(), JsValue> = context
314 .draw_image_with_html_image_element_and_sw_and_sh_and_dx_and_dy_and_dw_and_dh(
315 sheet.get_image(),
316 source.get_x(),
317 source.get_y(),
318 source.get_width(),
319 source.get_height(),
320 -source.get_width() * 0.5,
321 -source.get_height() * 0.5,
322 source.get_width(),
323 source.get_height(),
324 );
325 let _: Result<(), JsValue> = context.set_transform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
326 }
327}
328
329impl Default for Animator {
331 fn default() -> Animator {
332 Animator::create()
333 }
334}