Changing Timestamps to use thread_local + Rc + RefCell rather than Arc + Mutex

This commit is contained in:
Pauan 2018-03-17 01:17:06 -10:00
parent ed137a5aa1
commit 165fd7e5fa
2 changed files with 128 additions and 77 deletions

View File

@ -2,11 +2,12 @@
extern crate stdweb;
#[macro_use]
extern crate dominator;
#[macro_use]
extern crate signals;
use stdweb::traits::*;
use stdweb::web::{document, HtmlElement};
use stdweb::web::event::{MouseDownEvent, MouseUpEvent};
use stdweb::web::event::{MouseOverEvent, MouseOutEvent};
use signals::signal::Signal;
use signals::signal_vec::unsync::MutableVec;
use dominator::traits::*;
@ -18,7 +19,7 @@ use dominator::animation::unsync::MutableAnimation;
fn make_animated_box<A>(value: u32, t: A) -> Dom where A: Signal<Item = Percentage> + Clone + 'static {
let animation = MutableAnimation::new(3000.0);
let click_animation = MutableAnimation::new(500.0);
let hover_animation = MutableAnimation::new(300.0);
let low: f64 = value as f64;
let high: f64 = (value + 60) as f64;
@ -37,12 +38,12 @@ fn make_animated_box<A>(value: u32, t: A) -> Dom where A: Signal<Item = Percenta
Ok(())
})));
event(clone!(click_animation => move |_: MouseDownEvent| {
click_animation.animate_to(Percentage::new(1.0));
event(clone!(hover_animation => move |_: MouseOverEvent| {
hover_animation.animate_to(Percentage::new(1.0));
}));
event(clone!(click_animation => move |_: MouseUpEvent| {
click_animation.animate_to(Percentage::new(0.0));
event(clone!(hover_animation => move |_: MouseOutEvent| {
hover_animation.animate_to(Percentage::new(0.0));
}));
style("border-radius", "10px");
@ -57,7 +58,7 @@ fn make_animated_box<A>(value: u32, t: A) -> Dom where A: Signal<Item = Percenta
style("margin-left", animation.signal()
.map(|t| t.invert())
.map(|t| easing::in_out(t, easing::cubic))
.map(|t| Some(format!("{}px", t.range_inclusive(50.0, 0.0))))
.map(|t| Some(format!("{}px", t.range_inclusive(20.0, 0.0))))
.dynamic());
style("left", t.clone()
@ -65,9 +66,12 @@ fn make_animated_box<A>(value: u32, t: A) -> Dom where A: Signal<Item = Percenta
.map(|t| Some(format!("{}px", t.range_inclusive(100.0, 0.0))))
.dynamic());
style("height", t.clone()
.map(|t| easing::in_out(t, easing::cubic))
.map(|t| Some(format!("{}px", t.range_inclusive(0.0, 5.0))))
style("height",
map_clone! {
let animation = t.clone().map(|t| easing::in_out(t, easing::cubic)),
let hover = hover_animation.signal().map(|t| easing::out(t, easing::cubic)) =>
Some(format!("{}px", animation.range_inclusive(0.0, hover.range_inclusive(5.0, 15.0))))
}
.dynamic());
style("background-color", animation.signal()
@ -127,21 +131,51 @@ fn main() {
color += 10;
});
js! { @(no_return)
setInterval(function () {
let _timer_id = js!(
return setInterval(function () {
@{f}();
}, 500);
}
dominator::append_dom(&body,
Dom::with_state(state, |state| {
html!("div", {
children(state.boxes.signal_vec()
.animated_map(2000.0, |value, t| {
make_animated_box(value, t)
})
.dynamic());
})
})
);
/*dominator::append_dom(&body,
html!("button", {
event(clone!(state => move |_: ClickEvent| {
js! { @(no_return)
clearInterval(@{&timer_id});
}
state.boxes.clear();
}));
children(&mut [
text("Clear all animations")
]);
})
);*/
for _ in 0..2 {
dominator::append_dom(&body,
html!("div", {
style("display", "flex");
children(&mut [
html!("div", {
children(state.boxes.signal_vec()
.animated_map(2000.0, |value, t| {
make_animated_box(value, t)
})
.dynamic());
}),
html!("div", {
children(state.boxes.signal_vec()
.animated_map(2000.0, |value, t| {
make_animated_box(value, t)
})
.dynamic());
}),
]);
})
);
}
}

View File

@ -1,6 +1,6 @@
use self::unsync::MutableAnimation;
use std::rc::{Rc, Weak};
use std::cell::RefCell;
use std::cell::{Cell, RefCell};
use futures::{Async, task};
use futures::future::Future;
use futures::task::Task;
@ -48,37 +48,45 @@ impl Drop for Raf {
struct TimestampsGlobal {
raf: Option<Raf>,
value: Option<f64>,
// TODO make this more efficient
states: Vec<Weak<RefCell<TimestampsState>>>,
states: Vec<Weak<TimestampsState>>,
}
#[derive(Clone, Copy)]
enum TimestampsEnum {
First,
Changed,
NotChanged,
}
struct TimestampsState {
first: bool,
value: Option<f64>,
task: Option<Task>,
state: Cell<TimestampsEnum>,
task: RefCell<Option<Task>>,
global: Rc<RefCell<TimestampsGlobal>>,
}
// TODO make this more efficient
pub struct Timestamps(Rc<RefCell<TimestampsState>>);
pub struct Timestamps(Rc<TimestampsState>);
impl Signal for Timestamps {
type Item = Option<f64>;
fn poll(&mut self) -> State<Self::Item> {
let mut lock = self.0.borrow_mut();
let value = lock.value.take();
if lock.first {
lock.first = false;
State::Changed(value)
} else if value.is_some() {
State::Changed(value)
} else {
lock.task = Some(task::current());
State::NotChanged
match self.0.state.get() {
TimestampsEnum::Changed => {
self.0.state.set(TimestampsEnum::NotChanged);
// TODO make this more efficient ?
State::Changed(self.0.global.borrow().value.clone())
},
TimestampsEnum::First => {
self.0.state.set(TimestampsEnum::NotChanged);
State::Changed(None)
},
TimestampsEnum::NotChanged => {
*self.0.task.borrow_mut() = Some(task::current());
State::NotChanged
},
}
}
}
@ -86,56 +94,60 @@ impl Signal for Timestamps {
thread_local! {
static TIMESTAMPS: Rc<RefCell<TimestampsGlobal>> = Rc::new(RefCell::new(TimestampsGlobal {
raf: None,
value: None,
states: vec![],
}));
}
pub fn timestamps() -> Timestamps {
let state = Rc::new(RefCell::new(TimestampsState {
first: true,
value: None,
task: None,
}));
TIMESTAMPS.with(|timestamps| {
let mut lock = timestamps.borrow_mut();
let state = Rc::new(TimestampsState {
state: Cell::new(TimestampsEnum::First),
task: RefCell::new(None),
global: timestamps.clone(),
});
lock.states.push(Rc::downgrade(&state));
{
let mut lock = timestamps.borrow_mut();
if let None = lock.raf {
let timestamps = timestamps.clone();
lock.states.push(Rc::downgrade(&state));
lock.raf = Some(Raf::new(move |time| {
let mut lock = timestamps.borrow_mut();
if let None = lock.raf {
let timestamps = timestamps.clone();
lock.states.retain(|state| {
if let Some(state) = state.upgrade() {
let mut lock = state.borrow_mut();
lock.raf = Some(Raf::new(move |time| {
let mut lock = timestamps.borrow_mut();
// TODO it should always poll the most recent time, so this needs to be a has_changed boolean instead
lock.value = Some(time);
lock.value = Some(time);
if let Some(task) = lock.task.take() {
drop(lock);
task.notify();
lock.states.retain(|state| {
if let Some(state) = state.upgrade() {
state.state.set(TimestampsEnum::Changed);
let mut lock = state.task.borrow_mut();
if let Some(task) = lock.take() {
drop(lock);
task.notify();
}
true
} else {
false
}
});
true
} else {
false
if lock.states.len() == 0 {
lock.raf = None;
lock.states = vec![];
}
});
if lock.states.len() == 0 {
lock.raf = None;
lock.states = vec![];
}
}));
}));
}
}
});
Timestamps(state)
Timestamps(state)
})
}
@ -593,6 +605,11 @@ pub mod easing {
powi(p, 3)
}
#[inline]
pub fn out<F>(p: Percentage, f: F) -> Percentage where F: FnOnce(Percentage) -> Percentage {
f(p.invert()).invert()
}
pub fn in_out<F>(p: Percentage, f: F) -> Percentage where F: FnOnce(Percentage) -> Percentage {
p.map_unchecked(|p| {
if p <= 0.5 {