rive_rs/animation/
linear_animation_instance.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::animation::{LinearAnimation, Loop};
6use crate::artboard::Artboard;
7use crate::core::Object;
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10enum Direction {
11    Forwards,
12    Backwards,
13}
14
15impl From<Direction> for f32 {
16    fn from(direction: Direction) -> Self {
17        match direction {
18            Direction::Forwards => 1.0,
19            Direction::Backwards => -1.0,
20        }
21    }
22}
23
24#[derive(Clone, Debug)]
25pub struct LinearAnimationInstance {
26    linear_animation: Object<LinearAnimation>,
27    time: f32,
28    direction: Direction,
29    did_loop: bool,
30}
31
32impl LinearAnimationInstance {
33    pub fn new(linear_animation: Object<LinearAnimation>) -> Self {
34        let time = linear_animation
35            .as_ref()
36            .enable_work_area()
37            .then(|| {
38                linear_animation.as_ref().work_start() as f32
39                    / linear_animation.as_ref().fps() as f32
40            })
41            .unwrap_or_default();
42
43        Self { linear_animation, time, direction: Direction::Forwards, did_loop: false }
44    }
45
46    pub fn is_done(&self) -> bool {
47        self.linear_animation.as_ref().r#loop() == Loop::OneShot && self.did_loop
48    }
49
50    pub fn reset(&mut self) {
51        *self = Self::new(self.linear_animation.clone());
52    }
53
54    pub fn set_time(&mut self, time: f32) {
55        if time == self.time {
56            return;
57        }
58
59        self.time = time;
60        self.direction = Direction::Forwards;
61    }
62
63    pub fn advance(&mut self, elapsed_seconds: f32) -> bool {
64        let linear_animation = self.linear_animation.as_ref();
65        let direction: f32 = self.direction.into();
66        self.time += elapsed_seconds * linear_animation.speed() * direction;
67
68        let fps = linear_animation.fps() as f32;
69        let mut frames = self.time * fps;
70
71        let start = linear_animation
72            .enable_work_area()
73            .then(|| linear_animation.work_start() as f32)
74            .unwrap_or_default();
75        let end = linear_animation
76            .enable_work_area()
77            .then(|| linear_animation.work_end() as f32)
78            .unwrap_or_else(|| linear_animation.duration() as f32);
79        let range = end - start;
80
81        self.did_loop = false;
82        let mut keep_going = true;
83
84        match linear_animation.r#loop() {
85            Loop::OneShot => {
86                if frames > end {
87                    keep_going = false;
88                    frames = end;
89                    self.time = frames / fps;
90                    self.did_loop = true;
91                }
92            }
93            Loop::Loop => {
94                if frames >= end {
95                    frames = start + (self.time * fps - start) % range;
96                    self.time = frames / fps;
97                    self.did_loop = true;
98                }
99            }
100            Loop::PingPong => loop {
101                if self.direction == Direction::Forwards && frames >= end {
102                    self.direction = Direction::Backwards;
103                    frames = end + (end - frames);
104                    self.time = frames / fps;
105                    self.did_loop = true;
106                } else if self.direction == Direction::Backwards && frames < start {
107                    self.direction = Direction::Forwards;
108                    frames = start + (start - frames);
109                    self.time = frames / fps;
110                    self.did_loop = true;
111                } else {
112                    break;
113                }
114            },
115        }
116
117        keep_going
118    }
119
120    pub fn apply(&self, artboard: Object<Artboard>, mix: f32) {
121        self.linear_animation.as_ref().apply(artboard, self.time, mix);
122    }
123}