use leptos_motion_core::*;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn test_flip_animation_initialization() {
let flip_system = FlipSystem::new();
assert!(flip_system.is_ok());
}
#[wasm_bindgen_test]
fn test_element_position_recording() {
let position = ElementPosition {
x: 100.0,
y: 200.0,
width: 300.0,
height: 150.0,
rotation: 0.0,
scale_x: 1.0,
scale_y: 1.0,
};
assert_eq!(position.x, 100.0);
assert_eq!(position.y, 200.0);
assert_eq!(position.width, 300.0);
assert_eq!(position.height, 150.0);
assert_eq!(position.rotation, 0.0);
assert_eq!(position.scale_x, 1.0);
assert_eq!(position.scale_y, 1.0);
}
#[wasm_bindgen_test]
#[wasm_bindgen_test]
fn test_flip_animation_calculation() {
let first_position = ElementPosition {
x: 0.0,
y: 0.0,
width: 100.0,
height: 100.0,
rotation: 0.0,
scale_x: 1.0,
scale_y: 1.0,
};
let last_position = ElementPosition {
x: 200.0,
y: 300.0,
width: 150.0,
height: 150.0,
rotation: 45.0,
scale_x: 1.5,
scale_y: 1.5,
};
let flip_animation = calculate_flip_animation(&first_position, &last_position);
assert_eq!(flip_animation.translate_x, 200.0);
assert_eq!(flip_animation.translate_y, 300.0);
assert_eq!(flip_animation.scale_x, 1.5);
assert_eq!(flip_animation.scale_y, 1.5);
assert_eq!(flip_animation.rotate, 45.0);
}
#[wasm_bindgen_test]
#[wasm_bindgen_test]
fn test_layout_transition_detection() {
let old_layout = LayoutState {
elements: vec![
ElementLayout {
id: "item1".to_string(),
position: ElementPosition {
x: 0.0,
y: 0.0,
width: 100.0,
height: 100.0,
rotation: 0.0,
scale_x: 1.0,
scale_y: 1.0,
},
},
ElementLayout {
id: "item2".to_string(),
position: ElementPosition {
x: 100.0,
y: 0.0,
width: 100.0,
height: 100.0,
rotation: 0.0,
scale_x: 1.0,
scale_y: 1.0,
},
},
],
};
let new_layout = LayoutState {
elements: vec![
ElementLayout {
id: "item1".to_string(),
position: ElementPosition {
x: 0.0,
y: 100.0,
width: 100.0,
height: 100.0,
rotation: 0.0,
scale_x: 1.0,
scale_y: 1.0,
},
},
ElementLayout {
id: "item2".to_string(),
position: ElementPosition {
x: 0.0,
y: 0.0,
width: 100.0,
height: 100.0,
rotation: 0.0,
scale_x: 1.0,
scale_y: 1.0,
},
},
],
};
let transitions = detect_layout_transitions(&old_layout, &new_layout);
assert_eq!(transitions.len(), 2);
let item1_transition = transitions
.iter()
.find(|t| t.element_id == "item1")
.unwrap();
assert_eq!(item1_transition.translate_y, 100.0);
let item2_transition = transitions
.iter()
.find(|t| t.element_id == "item2")
.unwrap();
assert_eq!(item2_transition.translate_x, -100.0);
}
#[wasm_bindgen_test]
#[wasm_bindgen_test]
fn test_staggered_animation_timing() {
let transitions = vec![
LayoutTransition {
element_id: "item1".to_string(),
translate_x: 0.0,
translate_y: 100.0,
scale_x: 1.0,
scale_y: 1.0,
rotate: 0.0,
duration: 300.0,
delay: 0.0,
},
LayoutTransition {
element_id: "item2".to_string(),
translate_x: -100.0,
translate_y: 0.0,
scale_x: 1.0,
scale_y: 1.0,
rotate: 0.0,
duration: 300.0,
delay: 0.0,
},
LayoutTransition {
element_id: "item3".to_string(),
translate_x: 0.0,
translate_y: -100.0,
scale_x: 1.0,
scale_y: 1.0,
rotate: 0.0,
duration: 300.0,
delay: 0.0,
},
];
let staggered_transitions = apply_stagger(transitions, 50.0);
assert_eq!(staggered_transitions[0].delay, 0.0);
assert_eq!(staggered_transitions[1].delay, 50.0);
assert_eq!(staggered_transitions[2].delay, 100.0);
}
#[wasm_bindgen_test]
#[wasm_bindgen_test]
fn test_layout_animation_performance() {
let start_time = std::time::Instant::now();
let mut layout = LayoutState {
elements: Vec::new(),
};
for i in 0..100 {
layout.elements.push(ElementLayout {
id: format!("item{}", i),
position: ElementPosition {
x: (i % 10) as f64 * 100.0,
y: (i / 10) as f64 * 100.0,
width: 100.0,
height: 100.0,
rotation: 0.0,
scale_x: 1.0,
scale_y: 1.0,
},
});
}
let mut new_layout = layout.clone();
for element in &mut new_layout.elements {
element.position.x += 50.0;
element.position.y += 50.0;
}
let transitions = detect_layout_transitions(&layout, &new_layout);
let duration = start_time.elapsed();
assert!(duration.as_millis() < 50); assert_eq!(transitions.len(), 100);
}
#[wasm_bindgen_test]
#[wasm_bindgen_test]
fn test_layout_animation_easing() {
let easings = vec![
Easing::Linear,
Easing::EaseIn,
Easing::EaseOut,
Easing::EaseInOut,
Easing::CircIn,
Easing::CircOut,
Easing::CircInOut,
];
for easing in easings {
let transition = LayoutTransition {
element_id: "test".to_string(),
translate_x: 100.0,
translate_y: 0.0,
scale_x: 1.0,
scale_y: 1.0,
rotate: 0.0,
duration: 300.0,
delay: 0.0,
};
let eased_transition = apply_easing(transition, easing);
assert_eq!(eased_transition.element_id, "test");
}
}
#[wasm_bindgen_test]
#[wasm_bindgen_test]
fn test_layout_animation_cancellation() {
let mut flip_system = FlipSystem::new().unwrap();
let transition = LayoutTransition {
element_id: "test".to_string(),
translate_x: 100.0,
translate_y: 0.0,
scale_x: 1.0,
scale_y: 1.0,
rotate: 0.0,
duration: 1000.0,
delay: 0.0,
};
let animation_id = flip_system.start_animation(transition);
assert!(animation_id.is_ok());
let result = flip_system.cancel_animation(animation_id.unwrap());
assert!(result.is_ok());
}
#[wasm_bindgen_test]
#[wasm_bindgen_test]
fn test_layout_animation_completion() {
let mut flip_system = FlipSystem::new().unwrap();
let transition = LayoutTransition {
element_id: "test".to_string(),
translate_x: 100.0,
translate_y: 0.0,
scale_x: 1.0,
scale_y: 1.0,
rotate: 0.0,
duration: 100.0, delay: 0.0,
};
let animation_id = flip_system.start_animation(transition).unwrap();
std::thread::sleep(std::time::Duration::from_millis(150));
let is_complete = flip_system.is_animation_complete(animation_id);
assert!(is_complete);
}
#[wasm_bindgen_test]
#[wasm_bindgen_test]
fn test_layout_animation_state_management() {
let mut flip_system = FlipSystem::new().unwrap();
assert_eq!(flip_system.active_animations(), 0);
for i in 0..5 {
let transition = LayoutTransition {
element_id: format!("item{}", i),
translate_x: 100.0,
translate_y: 0.0,
scale_x: 1.0,
scale_y: 1.0,
rotate: 0.0,
duration: 300.0,
delay: 0.0,
};
let _ = flip_system.start_animation(transition);
}
assert_eq!(flip_system.active_animations(), 5);
flip_system.cancel_all_animations();
assert_eq!(flip_system.active_animations(), 0);
}
#[wasm_bindgen_test]
#[wasm_bindgen_test]
fn test_layout_animation_interpolation() {
let start_position = ElementPosition {
x: 0.0,
y: 0.0,
width: 100.0,
height: 100.0,
rotation: 0.0,
scale_x: 1.0,
scale_y: 1.0,
};
let end_position = ElementPosition {
x: 100.0,
y: 100.0,
width: 200.0,
height: 200.0,
rotation: 90.0,
scale_x: 2.0,
scale_y: 2.0,
};
let interpolated = interpolate_position(&start_position, &end_position, 0.5);
assert_eq!(interpolated.x, 50.0);
assert_eq!(interpolated.y, 50.0);
assert_eq!(interpolated.width, 150.0);
assert_eq!(interpolated.height, 150.0);
assert_eq!(interpolated.rotation, 45.0);
assert_eq!(interpolated.scale_x, 1.5);
assert_eq!(interpolated.scale_y, 1.5);
}
#[wasm_bindgen_test]
#[wasm_bindgen_test]
fn test_layout_animation_bounds_checking() {
let bounds = LayoutBounds {
min_x: 0.0,
max_x: 1000.0,
min_y: 0.0,
max_y: 1000.0,
};
let position = ElementPosition {
x: 500.0,
y: 500.0,
width: 100.0,
height: 100.0,
rotation: 0.0,
scale_x: 1.0,
scale_y: 1.0,
};
assert!(is_within_layout_bounds(&position, &bounds));
let position_outside = ElementPosition {
x: 1200.0,
y: 500.0,
width: 100.0,
height: 100.0,
rotation: 0.0,
scale_x: 1.0,
scale_y: 1.0,
};
assert!(!is_within_layout_bounds(&position_outside, &bounds));
}
#[derive(Debug, Clone, PartialEq)]
pub struct ElementPosition {
pub x: f64,
pub y: f64,
pub width: f64,
pub height: f64,
pub rotation: f64,
pub scale_x: f64,
pub scale_y: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ElementLayout {
pub id: String,
pub position: ElementPosition,
}
#[derive(Debug, Clone, PartialEq)]
pub struct LayoutState {
pub elements: Vec<ElementLayout>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct LayoutTransition {
pub element_id: String,
pub translate_x: f64,
pub translate_y: f64,
pub scale_x: f64,
pub scale_y: f64,
pub rotate: f64,
pub duration: f64,
pub delay: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FlipAnimation {
pub translate_x: f64,
pub translate_y: f64,
pub scale_x: f64,
pub scale_y: f64,
pub rotate: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct LayoutBounds {
pub min_x: f64,
pub max_x: f64,
pub min_y: f64,
pub max_y: f64,
}
pub struct FlipSystem {
active_animations: std::collections::HashMap<u64, LayoutTransition>,
next_id: u64,
}
impl FlipSystem {
pub fn new() -> std::result::Result<Self, String> {
Ok(Self {
active_animations: std::collections::HashMap::new(),
next_id: 1,
})
}
pub fn start_animation(
&mut self,
transition: LayoutTransition,
) -> std::result::Result<u64, String> {
let id = self.next_id;
self.next_id += 1;
self.active_animations.insert(id, transition);
Ok(id)
}
pub fn cancel_animation(&mut self, id: u64) -> std::result::Result<(), String> {
self.active_animations.remove(&id);
Ok(())
}
pub fn cancel_all_animations(&mut self) {
self.active_animations.clear();
}
pub fn active_animations(&self) -> usize {
self.active_animations.len()
}
pub fn is_animation_complete(&self, _id: u64) -> bool {
true
}
}
fn calculate_flip_animation(first: &ElementPosition, last: &ElementPosition) -> FlipAnimation {
FlipAnimation {
translate_x: last.x - first.x,
translate_y: last.y - first.y,
scale_x: last.scale_x / first.scale_x,
scale_y: last.scale_y / first.scale_y,
rotate: last.rotation - first.rotation,
}
}
fn detect_layout_transitions(old: &LayoutState, new: &LayoutState) -> Vec<LayoutTransition> {
let mut transitions = Vec::new();
for new_element in &new.elements {
if let Some(old_element) = old.elements.iter().find(|e| e.id == new_element.id) {
let dx = new_element.position.x - old_element.position.x;
let dy = new_element.position.y - old_element.position.y;
let scale_x = new_element.position.scale_x / old_element.position.scale_x;
let scale_y = new_element.position.scale_y / old_element.position.scale_y;
let rotate = new_element.position.rotation - old_element.position.rotation;
if dx != 0.0 || dy != 0.0 || scale_x != 1.0 || scale_y != 1.0 || rotate != 0.0 {
transitions.push(LayoutTransition {
element_id: new_element.id.clone(),
translate_x: dx,
translate_y: dy,
scale_x,
scale_y,
rotate,
duration: 300.0,
delay: 0.0,
});
}
}
}
transitions
}
fn apply_stagger(
mut transitions: Vec<LayoutTransition>,
stagger_delay: f64,
) -> Vec<LayoutTransition> {
for (i, transition) in transitions.iter_mut().enumerate() {
transition.delay = i as f64 * stagger_delay;
}
transitions
}
fn apply_easing(transition: LayoutTransition, _easing: Easing) -> LayoutTransition {
transition
}
fn interpolate_position(
start: &ElementPosition,
end: &ElementPosition,
progress: f64,
) -> ElementPosition {
ElementPosition {
x: start.x + (end.x - start.x) * progress,
y: start.y + (end.y - start.y) * progress,
width: start.width + (end.width - start.width) * progress,
height: start.height + (end.height - start.height) * progress,
rotation: start.rotation + (end.rotation - start.rotation) * progress,
scale_x: start.scale_x + (end.scale_x - start.scale_x) * progress,
scale_y: start.scale_y + (end.scale_y - start.scale_y) * progress,
}
}
fn is_within_layout_bounds(position: &ElementPosition, bounds: &LayoutBounds) -> bool {
position.x >= bounds.min_x
&& position.x + position.width <= bounds.max_x
&& position.y >= bounds.min_y
&& position.y + position.height <= bounds.max_y
}