Skip to main content

dioxuscut_paths/
evolve_path.rs

1//! SVG path evolution for line drawing animations.
2
3use crate::length::get_length;
4use crate::types::EvolvedPath;
5
6/// Animates an SVG path from invisible to full length.
7///
8/// Ported from Remotion's `evolvePath(progress, path)`.
9///
10/// Returns an [`EvolvedPath`] struct containing CSS `stroke_dasharray` and `stroke_dashoffset`.
11pub fn evolve_path(progress: f64, path: &str) -> EvolvedPath {
12    let length = get_length(path);
13    let clamped_p = progress.clamp(0.0, 1.0);
14
15    if clamped_p == 0.0 {
16        let extended_length = length * 1.5;
17        return EvolvedPath {
18            stroke_dasharray: format!("{extended_length:.4} {extended_length:.4}"),
19            stroke_dashoffset: extended_length,
20        };
21    }
22
23    let stroke_dasharray = format!("{length:.4} {length:.4}");
24    let stroke_dashoffset = length - clamped_p * length;
25
26    EvolvedPath {
27        stroke_dasharray,
28        stroke_dashoffset,
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35
36    #[test]
37    fn test_evolve_path_stages() {
38        let path = "M 0 0 L 100 0 Z"; // length 200
39        let ev_zero = evolve_path(0.0, path);
40        assert_eq!(ev_zero.stroke_dashoffset, 300.0);
41
42        let ev_half = evolve_path(0.5, path);
43        assert_eq!(ev_half.stroke_dasharray, "200.0000 200.0000");
44        assert_eq!(ev_half.stroke_dashoffset, 100.0);
45
46        let ev_full = evolve_path(1.0, path);
47        assert_eq!(ev_full.stroke_dashoffset, 0.0);
48    }
49}