use crate as lento;
use crate::mrc::Mrc;
use crate::style::{ResolvedStyleProp, ScaleParams, StyleProp, StylePropKey, StylePropVal, StyleTransform, StyleTransformOp, TranslateLength, TranslateParams};
use crate::timer::{set_timeout, set_timeout_nanos, TimerHandle};
use crate::{js_value};
use anyhow::{anyhow, Error};
use ordered_float::OrderedFloat;
use quick_js::JsValue;
use std::cell::RefCell;
use std::collections::{BTreeMap, HashMap};
use std::ops::Bound::{Excluded, Included};
use std::time::SystemTime;
use tokio::time::Instant;
use yoga::StyleUnit;
use crate::base::Callback;
use crate::element::Element;
use crate::frame::FrameWeak;
macro_rules! interpolate_values {
($prev: expr, $next: expr, $percent: expr; $($ty: ident => $handler: ident,)* ) => {
$(
if let StyleProp::$ty(pre) = $prev {
if let StyleProp::$ty(next) = $next {
if let StylePropVal::Custom(p) = pre {
if let StylePropVal::Custom(n) = next {
if let Some(v) = $handler(p, n, $percent) {
return Some(StyleProp::$ty(StylePropVal::Custom(v)));
}
}
}
}
return None
}
)*
};
}
#[macro_export]
macro_rules! match_both {
($def: path, $expr1: expr, $expr2: expr) => {
if let $def(e1) = $expr1 {
if let $def(e2) = $expr2 {
Some((e1, e2))
} else {
None
}
} else {
None
}
};
}
thread_local! {
pub static ANIMATIONS: RefCell<HashMap<String, Animation>> = RefCell::new(HashMap::new());
}
fn interpolate_f32(prev: &f32, next: &f32, position: f32) -> Option<f32> {
let delta = (next - prev) * position;
Some(prev + delta)
}
fn interpolate_style_unit(prev: &StyleUnit, next: &StyleUnit, position: f32) -> Option<StyleUnit> {
if let StyleUnit::Point(p) = prev {
if let StyleUnit::Point(n) = next {
let v = interpolate_f32(&p.0, &n.0, position).unwrap_or(0.0);
return Some(StyleUnit::Point(OrderedFloat(v)));
}
} else if let StyleUnit::Percent(p) = prev {
if let StyleUnit::Percent(n) = next {
let v = interpolate_f32(&p.0, &n.0, position).unwrap_or(0.0);
return Some(StyleUnit::Percent(OrderedFloat(v)));
}
}
return None;
}
fn interpolate_transform(prev: &StyleTransform, next: &StyleTransform, position: f32) -> Option<StyleTransform> {
let p_list = &prev.op_list;
let n_list = &next.op_list;
if p_list.len() != n_list.len() {
return None
}
let mut op_list = Vec::new();
for i in 0..p_list.len() {
let p = unsafe { p_list.get_unchecked(i) };
let n = unsafe { n_list.get_unchecked(i) };
if let Some(v) = interpolate_transform_op(p, n, position) {
op_list.push(v);
} else {
println!("Unsupported animation value");
}
}
Some(StyleTransform {
op_list
})
}
fn interpolate_translate_len(p: &TranslateLength, n: &TranslateLength, position: f32) -> Option<TranslateLength> {
if let Some((px, nx)) = match_both!(TranslateLength::Point, p, n) {
let x = interpolate_f32(&px, &nx, position)?;
Some(TranslateLength::Point(x))
} else if let Some((px, nx)) = match_both!(TranslateLength::Percent, p, n) {
let x = interpolate_f32(&px, &nx, position)?;
Some(TranslateLength::Percent(x))
} else {
None
}
}
fn interpolate_transform_op(prev: &StyleTransformOp, next: &StyleTransformOp, position: f32) -> Option<StyleTransformOp> {
if let Some((p_deg, n_deg)) = match_both!(StyleTransformOp::Rotate, prev, next) {
let deg = interpolate_f32(p_deg, n_deg, position)?;
Some(StyleTransformOp::Rotate(deg))
} else if let Some((pv, nv)) = match_both!(StyleTransformOp::Translate, prev, next) {
let (mut px, mut nx) = (pv.0.clone(), nv.0.clone());
px.adapt_zero(&mut nx);
let (mut py, mut ny) = (pv.1.clone(), nv.1.clone());
py.adapt_zero(&mut ny);
let x = interpolate_translate_len(&px, &nx, position)?;
let y = interpolate_translate_len(&py, &ny, position)?;
Some(StyleTransformOp::Translate(TranslateParams(x, y)))
} else if let Some((pv, nv)) = match_both!(StyleTransformOp::Scale, prev, next) {
let (px, nx) = (pv.0, nv.0);
let (py, ny) = (pv.1, nv.1);
let x = interpolate_f32(&px, &nx, position)?;
let y = interpolate_f32(&py, &ny, position)?;
Some(StyleTransformOp::Scale(ScaleParams(x, y)))
} else {
None
}
}
fn interpolate(pre_position: f32, pre_value: StyleProp, next_position: f32, next_value: StyleProp, current_position: f32) -> Option<StyleProp> {
let duration = next_position - pre_position;
let percent = (current_position - pre_position) / duration;
interpolate_values!(
&pre_value, &next_value, percent;
Width => interpolate_style_unit,
Height => interpolate_style_unit,
PaddingTop => interpolate_style_unit,
PaddingRight => interpolate_style_unit,
PaddingBottom => interpolate_style_unit,
PaddingLeft => interpolate_style_unit,
MarginTop => interpolate_style_unit,
MarginRight => interpolate_style_unit,
MarginBottom => interpolate_style_unit,
MarginLeft => interpolate_style_unit,
BorderTopLeftRadius => interpolate_f32,
BorderTopRightRadius => interpolate_f32,
BorderBottomRightRadius => interpolate_f32,
BorderBottomLeftRadius => interpolate_f32,
Top => interpolate_style_unit,
Right => interpolate_style_unit,
Bottom => interpolate_style_unit,
Left => interpolate_style_unit,
RowGap => interpolate_f32,
ColumnGap => interpolate_f32,
Transform => interpolate_transform,
);
None
}
pub struct AnimationDef {
key_frames: BTreeMap<OrderedFloat<f32>, Vec<StyleProp>>,
}
#[derive(Clone)]
pub struct Animation {
styles: HashMap<StylePropKey, BTreeMap<OrderedFloat<f32>, StyleProp>>,
}
pub trait FrameController {
fn request_next_frame(&mut self, callback: Box<dyn FnOnce()>);
}
pub struct AnimationState {
animation: Animation,
start_time: Instant,
duration: f32,
iteration_count: f32,
frame_controller: Box<dyn FrameController>,
stopped: bool,
}
pub struct AnimationInstance {
state: Mrc<AnimationState>,
}
impl AnimationDef {
pub fn new() -> Self {
Self { key_frames: BTreeMap::new() }
}
pub fn key_frame(mut self, position: f32, styles: Vec<StyleProp>) -> Self {
self.key_frames.insert(OrderedFloat::from(position), styles);
self
}
pub fn build(mut self) -> Animation {
let mut styles = HashMap::new();
for (p, key_styles) in &self.key_frames {
for s in key_styles {
let map = styles.entry(s.key()).or_insert_with(|| BTreeMap::new());
map.insert(p.clone(), s.clone());
}
}
Animation {
styles
}
}
}
impl Animation {
pub fn preprocess(&self) -> Animation {
let mut styles = HashMap::new();
for (p, m) in self.styles.clone() {
let mut new_style = BTreeMap::new();
for (k, v) in m {
new_style.insert(k, Self::preprocess_style(v));
}
styles.insert(p, new_style);
}
Animation { styles }
}
fn preprocess_style(style: StyleProp) -> StyleProp {
if let StyleProp::Transform(tf) = &style {
if let StylePropVal::Custom(tf) = tf {
return StyleProp::Transform(StylePropVal::Custom(tf.preprocess()));
}
}
style
}
pub fn get_frame(&self, position: f32) -> Vec<StyleProp> {
if position > 1.0 {
return Vec::new();
}
let position = f32::clamp(position, 0.0, 1.0);
let mut result = Vec::new();
let p = OrderedFloat(position);
for (_k, v) in &self.styles {
let begin = OrderedFloat::from(0.0);
let end = OrderedFloat::from(1.0);
let prev = v.range((Included(begin), Included(p))).last();
let next = v.range((Excluded(p), Included(end))).next();
if let Some((prev_position, prev_value)) = prev {
if let Some((next_position, next_value)) = next {
if let Some(value) = interpolate(prev_position.0, prev_value.clone(), next_position.0, next_value.clone(), p.0) {
result.push(value);
}
} else {
result.push(prev_value.clone());
}
}
}
result
}
}
impl AnimationInstance {
pub fn new(animation: Animation, duration: f32, iteration_count: f32, frame_controller: Box<dyn FrameController>) -> Self {
let state = AnimationState {
animation,
start_time: Instant::now(),
duration,
iteration_count,
frame_controller,
stopped: false,
};
Self {
state: Mrc::new(state),
}
}
pub fn run(&mut self, renderer: Box<dyn FnMut(Vec<StyleProp>)>) {
let mut state = self.state.clone();
self.state.frame_controller.request_next_frame(Box::new(move || {
state.start_time = Instant::now();
Self::render_frame(state, renderer);
}));
}
fn stop(&mut self) {
self.state.stopped = true;
}
fn render_frame(mut state: Mrc<AnimationState>, mut renderer: Box<dyn FnMut(Vec<StyleProp>)>) {
let elapsed = state.start_time.elapsed().as_nanos() as f32;
let position = elapsed / state.duration;
let frame = if position >= state.iteration_count || state.stopped {
Vec::new()
} else {
state.animation.get_frame(position - position as usize as f32)
};
let is_ended = frame.is_empty();
renderer(frame);
if !is_ended {
let s = state.clone();
state.frame_controller.request_next_frame(Box::new(|| {
Self::render_frame(s, renderer);
}))
} else {
}
}
}
impl Drop for AnimationInstance {
fn drop(&mut self) {
self.stop();
}
}
pub struct WindowAnimationController {
frame: FrameWeak,
}
impl WindowAnimationController {
pub fn new(frame: FrameWeak) -> Self {
Self { frame }
}
}
impl FrameController for WindowAnimationController {
fn request_next_frame(&mut self, callback: Box<dyn FnOnce()>) {
if let Ok(mut frame) = self.frame.upgrade() {
frame.request_next_frame_callback(Callback::from_box(callback));
}
}
}
pub struct SimpleFrameController {
timer: SystemTime,
prev_frame_time: u128,
timer_handle: Option<TimerHandle>,
}
impl SimpleFrameController {
pub fn new() -> Self {
Self {
timer: SystemTime::now(),
prev_frame_time: 0,
timer_handle: None,
}
}
}
impl FrameController for SimpleFrameController {
fn request_next_frame(&mut self, callback: Box<dyn FnOnce()>) {
let now = self.timer.elapsed().unwrap().as_nanos();
let next_frame_time = self.prev_frame_time + 16666667;
self.prev_frame_time = next_frame_time;
if next_frame_time > now {
let sleep_time = (next_frame_time - now) as u64;
self.timer_handle = Some(set_timeout_nanos(move || {
callback();
}, sleep_time));
} else {
self.timer_handle = Some(set_timeout(move || {
callback();
}, 0));
}
}
}