1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! 布局过渡:对子元素应用错峰进场动画的容器。
use crate::{prelude::FluentBuilder as _, *};
use std::time::Duration;
use crate::animation::{durations, easing::easings};
/// 进场动画类型。
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum LayoutAnimation {
/// 淡入上移。
#[default]
FadeUp,
/// 淡入下移。
FadeDown,
/// 从左滑入。
SlideLeft,
/// 从右滑入。
SlideRight,
/// 缩放淡入。
Scale,
}
/// 布局过渡组件。
#[derive(IntoElement)]
pub struct LayoutTransition {
/// 元素 ID。
id: ElementId,
/// 子元素列表。
children: Vec<AnyElement>,
/// 单个动画时长。
duration: Duration,
/// 子元素间错峰间隔。
stagger: Duration,
/// 动画类型。
animation: LayoutAnimation,
/// 版本号(重播动画用)。
version: usize,
/// 用户样式。
style: StyleRefinement,
}
impl LayoutTransition {
/// 创建布局过渡组件。
pub fn new(id: impl Into<ElementId>) -> Self {
Self {
id: id.into(),
children: Vec::new(),
duration: durations::NORMAL,
stagger: Duration::from_millis(50),
animation: LayoutAnimation::default(),
version: 0,
style: StyleRefinement::default(),
}
}
/// 设置动画时长。
pub fn duration(mut self, duration: Duration) -> Self {
self.duration = duration;
self
}
/// 设置错峰间隔。
pub fn stagger(mut self, stagger: Duration) -> Self {
self.stagger = stagger;
self
}
/// 设置动画类型。
pub fn animation(mut self, animation: LayoutAnimation) -> Self {
self.animation = animation;
self
}
/// 设置版本号(改变后重播动画)。
pub fn version(mut self, version: usize) -> Self {
self.version = version;
self
}
}
impl Styled for LayoutTransition {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.style
}
}
impl ParentElement for LayoutTransition {
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
self.children.extend(elements);
}
}
impl RenderOnce for LayoutTransition {
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
let user_style = self.style;
let duration = self.duration;
let stagger = self.stagger;
let animation = self.animation;
let version = self.version;
div()
.id(self.id)
.children(
self.children
.into_iter()
.enumerate()
.map(move |(idx, child)| {
let delay = Duration::from_millis(stagger.as_millis() as u64 * idx as u64);
let total_duration = duration + delay;
let delay_fraction = if total_duration.as_secs_f32() > 0.0 {
delay.as_secs_f32() / total_duration.as_secs_f32()
} else {
0.0
};
div()
.id(ElementId::Name(
format!("lt-child-{}-{}", idx, version).into(),
))
.child(child)
.with_animation(
ElementId::Name(format!("lt-anim-{}-{}", idx, version).into()),
Animation::new(total_duration).with_easing(easings::ease_out_cubic),
move |el, raw_delta| {
let delta = if raw_delta <= delay_fraction {
0.0
} else {
((raw_delta - delay_fraction) / (1.0 - delay_fraction))
.min(1.0)
};
match animation {
LayoutAnimation::FadeUp => {
el.opacity(delta).mt(px(-12.0 * (1.0 - delta)))
}
LayoutAnimation::FadeDown => {
el.opacity(delta).mt(px(12.0 * (1.0 - delta)))
}
LayoutAnimation::SlideLeft => {
el.opacity(delta).ml(px(20.0 * (1.0 - delta)))
}
LayoutAnimation::SlideRight => {
el.opacity(delta).ml(px(-20.0 * (1.0 - delta)))
}
LayoutAnimation::Scale => {
let scale_val = 0.8 + 0.2 * delta;
el.opacity(delta)
.w(relative(scale_val))
.h(relative(scale_val))
}
}
},
)
}),
)
.map(|this| {
let mut el = this;
el.style().refine(&user_style);
el
})
}
}