Skip to main content

rustmotion_components/
line.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use skia_safe::{Canvas, PaintStyle};
4
5use rustmotion_core::css::CssStyle;
6use rustmotion_core::engine::animator::AnimatedProperties;
7use rustmotion_core::engine::layout_pass::BoxLayout;
8use rustmotion_core::engine::renderer::paint_from_hex;
9use rustmotion_core::schema::TimelineStep;
10use rustmotion_core::traits::{PaintCtx, Painter, TimingConfig};
11
12/// A line component that draws a line from (x1, y1) to (x2, y2).
13#[derive(Debug, Serialize, Deserialize, JsonSchema)]
14pub struct Line {
15    #[serde(default)]
16    pub x1: f32,
17    #[serde(default)]
18    pub y1: f32,
19    pub x2: f32,
20    pub y2: f32,
21    #[serde(default = "default_line_width")]
22    pub width: f32,
23    #[serde(default = "default_line_color")]
24    pub color: String,
25    #[serde(default)]
26    pub dashed: Option<Vec<f32>>,
27    #[serde(flatten)]
28    pub timing: TimingConfig,
29    #[serde(default)]
30    pub style: CssStyle,
31    #[serde(default)]
32    pub timeline: Vec<TimelineStep>,
33    #[serde(default)]
34    pub stagger: Option<f32>,
35}
36
37fn default_line_width() -> f32 {
38    2.0
39}
40
41fn default_line_color() -> String {
42    "#FFFFFF".to_string()
43}
44
45rustmotion_core::impl_traits!(Line {
46    Animatable => animation,
47    Timed => timing,
48    Styled => style,
49});
50
51impl Line {
52    fn paint(&self, canvas: &Canvas, props: &AnimatedProperties) {
53        let mut paint = paint_from_hex(&self.color);
54        paint.set_style(PaintStyle::Stroke);
55        paint.set_stroke_width(self.width);
56        paint.set_anti_alias(true);
57        paint.set_stroke_cap(skia_safe::PaintCap::Round);
58
59        // Apply dashed style
60        if let Some(ref intervals) = self.dashed {
61            if intervals.len() >= 2 {
62                if let Some(dash) = skia_safe::PathEffect::dash(intervals, 0.0) {
63                    paint.set_path_effect(dash);
64                }
65            }
66        }
67
68        // Apply draw_progress
69        if props.draw_progress >= 0.0 && props.draw_progress < 1.0 {
70            let dx = self.x2 - self.x1;
71            let dy = self.y2 - self.y1;
72            let length = (dx * dx + dy * dy).sqrt();
73            let draw_len = length * props.draw_progress.clamp(0.0, 1.0);
74            let intervals = [draw_len, length - draw_len + 0.01];
75            if let Some(dash) = skia_safe::PathEffect::dash(&intervals, 0.0) {
76                paint.set_path_effect(dash);
77            }
78        }
79
80        canvas.draw_line((self.x1, self.y1), (self.x2, self.y2), &paint);
81    }
82}
83
84impl Painter for Line {
85    fn paint_content(
86        &self,
87        canvas: &Canvas,
88        _layout: &BoxLayout,
89        props: &AnimatedProperties,
90        _ctx: &PaintCtx,
91    ) {
92        self.paint(canvas, props);
93    }
94}