#![doc = include_str!("./anim_el_example.md")]
use ambient_api_core::{
core::{
animation::{
self,
components::{play_clip_from_url, start_time},
},
app::components::name,
},
prelude::epoch_time,
};
use ambient_element::{element_component, use_ref_with, Element, ElementComponentExt, Hooks};
#[element_component]
pub fn AnimationPlayer(_hooks: &mut Hooks, root: Element) -> Element {
Element::new()
.with(animation::components::is_animation_player(), ())
.children(vec![root])
.with(name(), "Animation player".to_string())
}
#[element_component]
pub fn PlayClipFromUrl(
_hooks: &mut Hooks,
url: String,
looping: bool,
) -> Element {
Element::new()
.with(play_clip_from_url(), url)
.with(name(), "Play clip from URL".to_string())
.with(animation::components::looping(), looping)
.init(start_time(), epoch_time())
}
#[element_component(without_el)]
pub fn BlendNode(
_hooks: &mut Hooks,
left: Element,
right: Element,
weight: f32,
) -> Element {
if weight <= 0. {
left
} else if weight >= 1. {
right
} else {
Element::new()
.with(animation::components::blend(), weight)
.with(name(), "Blend".to_string())
.children(vec![left, right])
}
}
impl BlendNode {
pub fn el(left: Element, right: Element, weight: f32) -> Element {
if weight <= 0. {
left
} else if weight >= 1. {
right
} else {
Self {
left,
right,
weight,
}
.el()
}
}
pub fn normalize_multiblend(items: Vec<(Element, f32)>) -> Element {
let total_weight = items.iter().map(|x| x.1).sum::<f32>();
if total_weight <= 0. {
return Element::new();
}
let mut items = items
.into_iter()
.map(|(a, w)| (a, w / total_weight))
.collect::<Vec<_>>();
items.retain(|x| x.1 > 0.);
items.sort_by_key(|x| -ordered_float::OrderedFloat(x.1));
for x in items.iter_mut() {
x.1 = 1. - x.1;
}
Self::multiblend(items)
}
pub fn multiblend(mut items: Vec<(Element, f32)>) -> Element {
if items.is_empty() {
Element::new()
} else if items.len() == 1 {
items.pop().unwrap().0
} else {
let item = items.remove(0);
Self::el(item.0, Self::multiblend(items), item.1)
}
}
}
#[element_component]
pub fn Transition(
hooks: &mut Hooks,
animations: Vec<Element>,
active: usize,
speed: f32,
) -> Element {
let weights = use_ref_with(hooks, |_| { animations
.iter()
.enumerate()
.map(|(i, _)| if i == active { 1. } else { 0. })
.collect::<Vec<_>>()
});
let mut weights = weights.lock();
for (i, weight) in weights.iter_mut().enumerate() {
let target = if i == active { 1. } else { 0. };
*weight = *weight * (1. - speed) + target * speed;
}
BlendNode::normalize_multiblend(
animations
.into_iter()
.zip(weights.iter().cloned())
.collect(),
)
}