euv_engine/sprite/
impl.rs1use crate::*;
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 context.save();
97 let _ = context.translate(
98 transform.get_position().get_x(),
99 transform.get_position().get_y(),
100 );
101 let _ = context.rotate(transform.get_rotation());
102 let _ = context.scale(transform.get_scale().get_x(), transform.get_scale().get_y());
103 let _ = context
104 .draw_image_with_html_image_element_and_sw_and_sh_and_dx_and_dy_and_dw_and_dh(
105 self.get_image(),
106 source.get_x(),
107 source.get_y(),
108 source.get_width(),
109 source.get_height(),
110 -source.get_width() * 0.5,
111 -source.get_height() * 0.5,
112 source.get_width(),
113 source.get_height(),
114 );
115 context.restore();
116 }
117}
118
119impl Animator {
121 pub fn create() -> Animator {
127 Animator::new(AnimationState::Paused, 1)
128 }
129
130 pub fn play(&mut self, animation: SpriteAnimation) {
136 self.set_current_animation(Some(animation));
137 self.set_current_frame_index(0);
138 self.set_elapsed_time(0.0);
139 self.set_state(AnimationState::Playing);
140 self.set_direction(1);
141 }
142
143 pub fn pause(&mut self) {
145 if self.get_state() == AnimationState::Playing {
146 self.set_state(AnimationState::Paused);
147 }
148 }
149
150 pub fn resume(&mut self) {
152 if self.get_state() == AnimationState::Paused {
153 self.set_state(AnimationState::Playing);
154 }
155 }
156
157 pub fn stop(&mut self) {
159 self.set_current_frame_index(0);
160 self.set_elapsed_time(0.0);
161 self.set_state(AnimationState::Paused);
162 self.set_direction(1);
163 }
164
165 pub fn update(&mut self, delta_time: f64) {
171 if self.get_state() != AnimationState::Playing {
172 return;
173 }
174 let current_frame_index: usize = self.get_current_frame_index();
175 let (frame_count, current_duration, mode) = {
176 let Some(animation) = self.get_mut_current_animation().as_ref() else {
177 return;
178 };
179 if animation.get_frames().is_empty() {
180 return;
181 }
182 (
183 animation.get_frames().len(),
184 animation.get_frames()[current_frame_index].get_duration(),
185 animation.get_mode(),
186 )
187 };
188 *self.get_mut_elapsed_time() += delta_time;
189 if self.get_elapsed_time() < current_duration {
190 return;
191 }
192 self.set_elapsed_time(0.0);
193 self.advance_frame(frame_count, mode);
194 }
195
196 pub fn current_frame_source(&self) -> Option<Rect> {
202 let animation: Option<SpriteAnimation> = self.get_current_animation();
203 let animation: &SpriteAnimation = animation.as_ref()?;
204 let frame: &SpriteFrame = animation.get_frames().get(self.get_current_frame_index())?;
205 Some(frame.get_source())
206 }
207
208 fn advance_frame(&mut self, frame_count: usize, mode: AnimationMode) {
214 match mode {
215 AnimationMode::Loop => {
216 self.set_current_frame_index((self.get_current_frame_index() + 1) % frame_count);
217 }
218 AnimationMode::Once => {
219 if self.get_current_frame_index() + 1 < frame_count {
220 *self.get_mut_current_frame_index() += 1;
221 } else {
222 self.set_state(AnimationState::Finished);
223 }
224 }
225 AnimationMode::PingPong => {
226 if self.get_direction() > 0 {
227 if self.get_current_frame_index() + 1 < frame_count {
228 *self.get_mut_current_frame_index() += 1;
229 } else {
230 self.set_direction(-1);
231 if self.get_current_frame_index() > 0 {
232 *self.get_mut_current_frame_index() -= 1;
233 }
234 }
235 } else {
236 if self.get_current_frame_index() > 0 {
237 *self.get_mut_current_frame_index() -= 1;
238 } else {
239 self.set_direction(1);
240 if self.get_current_frame_index() + 1 < frame_count {
241 *self.get_mut_current_frame_index() += 1;
242 }
243 }
244 }
245 }
246 }
247 }
248}
249
250impl Animator {
252 pub fn draw(
260 &self,
261 context: &CanvasRenderingContext2d,
262 sheet: &SpriteSheet,
263 transform: &Transform2D,
264 ) {
265 let Some(source) = self.current_frame_source() else {
266 return;
267 };
268 context.save();
269 let _ = context.translate(
270 transform.get_position().get_x(),
271 transform.get_position().get_y(),
272 );
273 let _ = context.rotate(transform.get_rotation());
274 let _ = context.scale(
275 if self.get_flip_x() {
276 -transform.get_scale().get_x()
277 } else {
278 transform.get_scale().get_x()
279 },
280 if self.get_flip_y() {
281 -transform.get_scale().get_y()
282 } else {
283 transform.get_scale().get_y()
284 },
285 );
286 let _ = context
287 .draw_image_with_html_image_element_and_sw_and_sh_and_dx_and_dy_and_dw_and_dh(
288 sheet.get_image(),
289 source.get_x(),
290 source.get_y(),
291 source.get_width(),
292 source.get_height(),
293 -source.get_width() * 0.5,
294 -source.get_height() * 0.5,
295 source.get_width(),
296 source.get_height(),
297 );
298 context.restore();
299 }
300}
301
302impl Default for Animator {
304 fn default() -> Animator {
305 Animator::create()
306 }
307}