rustmotion_components/
line.rs1use 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#[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 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 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}