on master help sign in
Merge pull request #32 from fitzgen/moire
85e2048Nick Fitzgerald
Cargo.lock+17
@@ -154,6 +154,23 @@ dependencies = [
154 "web-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
154 "web-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
155]
155]
156
156
157+[[package]]
158+name = "dodrio-moire"
159+version = "0.1.0"
160+dependencies = [
161+ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
162+ "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
163+ "console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
164+ "dodrio 0.1.0",
165+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
166+ "js-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
167+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
168+ "wasm-bindgen 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
169+ "wasm-bindgen-futures 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
170+ "wasm-bindgen-test 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
171+ "web-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
172+]
173+
157[[package]]
174[[package]]
158name = "dodrio-todomvc"
175name = "dodrio-todomvc"
159version = "0.1.0"
176version = "0.1.0"
Cargo.toml+7−6
@@ -8,13 +8,13 @@ edition = "2018"
8bumpalo = "2.1.0"
8bumpalo = "2.1.0"
9cfg-if = "0.1.6"
9cfg-if = "0.1.6"
10futures = "0.1.25"
10futures = "0.1.25"
11−wasm-bindgen = "0.2.36"
11+wasm-bindgen = "0.2.37"
12−wasm-bindgen-futures = "0.3.13"
12+wasm-bindgen-futures = "0.3.14"
13−js-sys = "0.3.13"
13+js-sys = "0.3.14"
14log = { version = "0.4.6", optional = true }
14log = { version = "0.4.6", optional = true }
15
15
16[dependencies.web-sys]
16[dependencies.web-sys]
17−version = "0.3.13"
17+version = "0.3.14"
18features = [
18features = [
19 "console",
19 "console",
20 "Document",
20 "Document",
@@ -28,10 +28,10 @@ features = [
28console_log = "0.1.2"
28console_log = "0.1.2"
29dodrio-js-api = { version = "=0.1.0", path = "./crates/js-api" }
29dodrio-js-api = { version = "=0.1.0", path = "./crates/js-api" }
30log = "0.4.6"
30log = "0.4.6"
31−wasm-bindgen-test = "0.2.36"
31+wasm-bindgen-test = "0.2.37"
32
32
33[dev-dependencies.web-sys]
33[dev-dependencies.web-sys]
34−version = "0.3.13"
34+version = "0.3.14"
35features = [
35features = [
36 "Attr",
36 "Attr",
37 "EventTarget",
37 "EventTarget",
@@ -52,5 +52,6 @@ members = [
52 "./examples/hello-world",
52 "./examples/hello-world",
53 "./examples/input-form",
53 "./examples/input-form",
54 "./examples/js-component",
54 "./examples/js-component",
55+ "./examples/moire",
55 "./examples/todomvc",
56 "./examples/todomvc",
56]
57]
examples/counter/Cargo.toml+3−3
@@ -14,10 +14,10 @@ console_error_panic_hook = "0.1.6"
14console_log = "0.1.2"
14console_log = "0.1.2"
15dodrio = { path = "../.." }
15dodrio = { path = "../.." }
16log = "0.4.6"
16log = "0.4.6"
17−wasm-bindgen = "0.2.36"
17+wasm-bindgen = "0.2.37"
18
18
19[dependencies.web-sys]
19[dependencies.web-sys]
20−version = "0.3.13"
20+version = "0.3.14"
21features = [
21features = [
22 "console",
22 "console",
23 "Document",
23 "Document",
@@ -30,4 +30,4 @@ features = [
30]
30]
31
31
32[dev-dependencies]
32[dev-dependencies]
33−wasm-bindgen-test = "0.2.36"
33+wasm-bindgen-test = "0.2.37"
examples/hello-world/Cargo.toml+3−3
@@ -11,11 +11,11 @@ crate-type = ["cdylib"]
11
11
12[dependencies]
12[dependencies]
13dodrio = { path = "../.." }
13dodrio = { path = "../.." }
14−wasm-bindgen = "0.2.36"
14+wasm-bindgen = "0.2.37"
15console_error_panic_hook = "0.1.6"
15console_error_panic_hook = "0.1.6"
16
16
17[dependencies.web-sys]
17[dependencies.web-sys]
18−version = "0.3.13"
18+version = "0.3.14"
19features = [
19features = [
20 "Document",
20 "Document",
21 "HtmlElement",
21 "HtmlElement",
@@ -24,4 +24,4 @@ features = [
24]
24]
25
25
26[dev-dependencies]
26[dev-dependencies]
27−wasm-bindgen-test = "0.2.36"
27+wasm-bindgen-test = "0.2.37"
examples/input-form/Cargo.toml+3−3
@@ -11,11 +11,11 @@ crate-type = ["cdylib"]
11
11
12[dependencies]
12[dependencies]
13dodrio = { path = "../.." }
13dodrio = { path = "../.." }
14−wasm-bindgen = "0.2.36"
14+wasm-bindgen = "0.2.37"
15console_error_panic_hook = "0.1.6"
15console_error_panic_hook = "0.1.6"
16
16
17[dependencies.web-sys]
17[dependencies.web-sys]
18−version = "0.3.13"
18+version = "0.3.14"
19features = [
19features = [
20 "console",
20 "console",
21 "Document",
21 "Document",
@@ -29,4 +29,4 @@ features = [
29]
29]
30
30
31[dev-dependencies]
31[dev-dependencies]
32−wasm-bindgen-test = "0.2.36"
32+wasm-bindgen-test = "0.2.37"
examples/js-component/Cargo.toml+2−2
@@ -12,12 +12,12 @@ crate-type = ["cdylib"]
12[dependencies]
12[dependencies]
13dodrio = { path = "../.." }
13dodrio = { path = "../.." }
14dodrio-js-api = { path = "../../crates/js-api" }
14dodrio-js-api = { path = "../../crates/js-api" }
15−wasm-bindgen = "0.2.36"
15+wasm-bindgen = "0.2.37"
16console_error_panic_hook = "0.1.6"
16console_error_panic_hook = "0.1.6"
17js-sys = "0.3.14"
17js-sys = "0.3.14"
18
18
19[dependencies.web-sys]
19[dependencies.web-sys]
20−version = "0.3.13"
20+version = "0.3.14"
21features = [
21features = [
22 "Document",
22 "Document",
23 "HtmlElement",
23 "HtmlElement",
examples/moire/Cargo.tomlnew file+44
@@ -0,0 +1,44 @@
1+[package]
2+name = "dodrio-moire"
3+version = "0.1.0"
4+authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
5+edition = "2018"
6+
7+[lib]
8+crate-type = ["cdylib"]
9+
10+[dependencies]
11+cfg-if = "0.1.6"
12+dodrio = { path = "../.." }
13+futures = "0.1.25"
14+log = "0.4.6"
15+wasm-bindgen = "0.2.37"
16+wasm-bindgen-futures = "0.3.14"
17+
18+# Optional dependencies for logging.
19+console_error_panic_hook = { optional = true, version = "0.1.6" }
20+console_log = { optional = true, version = "0.1.2" }
21+js-sys = "0.3.14"
22+
23+[dependencies.web-sys]
24+version = "0.3.14"
25+features = [
26+ "Document",
27+ "Element",
28+ "Event",
29+ "EventTarget",
30+ "HtmlElement",
31+ "HtmlInputElement",
32+ "Node",
33+ "Performance",
34+ "Window",
35+]
36+
37+[dev-dependencies]
38+wasm-bindgen-test = "0.2.37"
39+
40+[features]
41+logging = [
42+ "console_log",
43+ "console_error_panic_hook",
44+]
examples/moire/README.mdnew file+26
@@ -0,0 +1,26 @@
1+# Moiré Patterns!
2+
3+This was originally a demo meant to show off [WebRender][], and so the
4+performance bottleneck is painting, not DOM manipulation, but it looks cool
5+either way!
6+
7+[WebRender]: https://hacks.mozilla.org/2017/10/the-whole-web-at-maximum-fps-how-webrender-gets-rid-of-jank/
8+
9+## Source
10+
11+See `src/lib.rs` for the main bits, and `src/colors.rs` for HSV to RGB color
12+conversion and other color utilities.
13+
14+## Build
15+
16+```
17+wasm-pack build --target no-modules
18+```
19+
20+## Serve
21+
22+Use any HTTP server, for example:
23+
24+```
25+python -m SimpleHTTPServer
26+```
examples/moire/index.htmlnew file+67
@@ -0,0 +1,67 @@
1+<!DOCTYPE html>
2+<html>
3+ <head>
4+ <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
5+ <title>Moire patterns! Originally a WebRender demo</title>
6+ <style>
7+ main {
8+ position: absolute;
9+ top: 0;
10+ left: 0;
11+ right: 0;
12+ bottom: 0;
13+ overflow: hidden;
14+ }
15+ .circle {
16+ border-radius: 50%;
17+ position: absolute;
18+ top: 50%;
19+ left: 50%;
20+ border-style: solid;
21+ border-width: 3px;
22+ }
23+ .object {
24+ position: absolute;
25+ width: 100%;
26+ height: 100%;
27+ left: 0;
28+ top: 0;
29+ }
30+ code {
31+ position: relative;
32+ font-size: 150%;
33+ z-index: 10;
34+ }
35+ #circle-count {
36+ position: absolute;
37+ margin-top: 30px;
38+ z-index: 2;
39+ }
40+ </style>
41+ </head>
42+ <body>
43+ <code>FPS: <output id="fps"></code></code>
44+ <div id="scene"></div>
45+ <script src="pkg/dodrio_moire.js"></script>
46+ <script>
47+ wasm_bindgen("pkg/dodrio_moire_bg.wasm");
48+ </script>
49+ <script>
50+ let lastTime = 0
51+ let frameCount = 0
52+ const fps = document.getElementById('fps')
53+ function updateFPS(time) {
54+ frameCount++
55+ if (lastTime + 1000.0 <= time) {
56+ fps.textContent = frameCount
57+ lastTime = time
58+ frameCount = 0
59+ }
60+ }
61+ requestAnimationFrame(function loop(now) {
62+ updateFPS(now);
63+ requestAnimationFrame(loop);
64+ })
65+ </script>
66+ </body>
67+</html>
examples/moire/src/colors.rsnew file+162
@@ -0,0 +1,162 @@
1+//! Color utility functions.
2+
3+use std::f64;
4+
5+/// An RGB color.
6+#[derive(Copy, Clone)]
7+pub struct Rgb {
8+ pub r: u8,
9+ pub g: u8,
10+ pub b: u8,
11+}
12+
13+/// An HSV color.
14+#[derive(Copy, Clone)]
15+pub struct Hsv {
16+ pub h: f64,
17+ pub s: f64,
18+ pub v: f64,
19+}
20+
21+/// Convert an RGB color into HSV.
22+impl From<Rgb> for Hsv {
23+ fn from(Rgb { r, g, b }: Rgb) -> Hsv {
24+ let r = r as f64 / 255.0;
25+ let g = g as f64 / 255.0;
26+ let b = b as f64 / 255.0;
27+ let max = f64::max(f64::max(r, g), b);
28+ let min = f64::min(f64::min(r, g), b);
29+ let v = max;
30+ let diff = v - min;
31+ let mut h = 0.0;
32+ let mut s = 0.0;
33+ if diff != 0.0 {
34+ s = diff / v;
35+ let diffc = |c| (v - c) / 6.0 / diff + 1.0 / 2.0;
36+ let rr = diffc(r);
37+ let gg = diffc(g);
38+ let bb = diffc(b);
39+ if r == v {
40+ h = bb - gg;
41+ } else if g == v {
42+ h = (1.0 / 3.0) + rr - bb;
43+ } else if b == v {
44+ h = (2.0 / 3.0) + gg - rr;
45+ }
46+ }
47+ Hsv { h, s, v }
48+ }
49+}
50+
51+/// Convert an HSV color into RGB.
52+impl From<Hsv> for Rgb {
53+ fn from(Hsv { h, s, v }: Hsv) -> Rgb {
54+ let i = (h * 6.0).floor();
55+ let f = h * 6.0 - i;
56+ let p = v * (1.0 - s);
57+ let q = v * (1.0 - f * s);
58+ let t = v * (1.0 - (1.0 - f) * s);
59+ let r;
60+ let g;
61+ let b;
62+ match i as u64 % 6 {
63+ 0 => {
64+ r = v;
65+ g = t;
66+ b = p;
67+ }
68+ 1 => {
69+ r = q;
70+ g = v;
71+ b = p;
72+ }
73+ 2 => {
74+ r = p;
75+ g = v;
76+ b = t;
77+ }
78+ 3 => {
79+ r = p;
80+ g = q;
81+ b = v;
82+ }
83+ 4 => {
84+ r = t;
85+ g = p;
86+ b = v;
87+ }
88+ 5 => {
89+ r = v;
90+ g = p;
91+ b = q;
92+ }
93+ _ => wasm_bindgen::throw_str("impossible"),
94+ }
95+ let r = (r * 255.0).round() as u8;
96+ let g = (g * 255.0).round() as u8;
97+ let b = (b * 255.0).round() as u8;
98+ Rgb { r, g, b }
99+ }
100+}
101+
102+/// Linearly interpolate between two values.
103+fn lerp(a: f64, b: f64, t: f64) -> f64 {
104+ a + (b - a) * t
105+}
106+
107+/// Clamp the `x` value to within the range `lo..hi`.
108+fn clamp(lo: f64, hi: f64, x: f64) -> f64 {
109+ if x < lo {
110+ lo
111+ } else if x > hi {
112+ hi
113+ } else {
114+ x
115+ }
116+}
117+
118+/// Linearly interpolate two HSV colors and convert the result into RGB.
119+fn lerp_hsv_to_rgb(a: Hsv, b: Hsv, t: f64) -> Rgb {
120+ let h = lerp(a.h, b.h, t);
121+ let s = lerp(a.s, b.s, t);
122+ let v = lerp(a.v, b.v, t);
123+ let hsv = Hsv { h, s, v };
124+ hsv.into()
125+}
126+
127+thread_local! {
128+ static PALETTES: [(Hsv, Hsv); 3] = [
129+ (
130+ (Rgb { r: 0xfe, g: 0xf6, b: 0xdf }).into(),
131+ (Rgb { r: 0x64, g: 0xc3, b: 0xc8 }).into(),
132+ ),
133+ (
134+ (Rgb { r: 0xfc, g: 0xcf, b: 0x74 }).into(),
135+ (Rgb { r: 0x41, g: 0x65, b: 0x7f }).into(),
136+ ),
137+ (
138+ (Rgb { r: 0xd9, g: 0xe0, b: 0xe8 }).into(),
139+ (Rgb { r: 0x94, g: 0x5d, b: 0x72 }).into(),
140+ ),
141+ ];
142+}
143+
144+/// Get a color from our color palette that is linearly interpolated based on
145+/// the `t` elapsed time value.
146+///
147+/// The `which` function allows callers to select whether they want the
148+/// foreground or background color.
149+pub fn get_interpolated_color<F>(mut which: F, t: f64) -> Rgb
150+where
151+ F: FnMut((Hsv, Hsv)) -> Hsv,
152+{
153+ PALETTES.with(|palettes| {
154+ let t = t * (palettes.len() as f64);
155+ let prev_index = t.floor() as usize % palettes.len();
156+ let next_index = (prev_index + 1) % palettes.len();
157+ let t = clamp(-0.5, 0.5, (t % 1.0 - 0.5) * 5.0) + 0.5;
158+ let a = which(palettes[prev_index]);
159+ let b = which(palettes[next_index]);
160+ lerp_hsv_to_rgb(a, b, t)
161+ })
162+}
examples/moire/src/lib.rsnew file+285
@@ -0,0 +1,285 @@
1+mod colors;
2+
3+use cfg_if::cfg_if;
4+use dodrio::bumpalo::{self, Bump};
5+use dodrio::{Node, Render, Vdom};
6+use std::cell::RefCell;
7+use std::rc::Rc;
8+use std::str;
9+use wasm_bindgen::prelude::*;
10+use wasm_bindgen::JsCast;
11+
12+/// This is the main function that is automatically invoked when the wasm module
13+/// is loaded.
14+#[wasm_bindgen(start)]
15+pub fn run() {
16+ // Set up the panic hook for debugging when things go wrong.
17+ init_logging();
18+
19+ // Grab the document's `<body>`.
20+ let window = web_sys::window().unwrap_throw();
21+ let document = window.document().unwrap_throw();
22+ let scene = document.get_element_by_id("scene").unwrap_throw();
23+
24+ // Create a new `Moire` render component.
25+ let component = Moire::new();
26+
27+ // Create a virtual DOM and mount it and the `Hello` render component to the
28+ // scene.
29+ let vdom = Vdom::new(&scene, component);
30+
31+ // Kick off a loop that keeps re-rendering on every animation frame.
32+ let rc: Rc<RefCell<Option<Closure<FnMut()>>>> = Rc::new(RefCell::new(None));
33+ let rc2 = rc.clone();
34+ let window2 = window.clone();
35+ let weak = vdom.weak();
36+ let f = Closure::wrap(Box::new(move || {
37+ weak.schedule_render();
38+ window
39+ .request_animation_frame(
40+ rc.borrow()
41+ .as_ref()
42+ .unwrap_throw()
43+ .as_ref()
44+ .unchecked_ref::<js_sys::Function>(),
45+ )
46+ .unwrap_throw();
47+ }) as Box<FnMut()>);
48+ window2
49+ .request_animation_frame(f.as_ref().unchecked_ref::<js_sys::Function>())
50+ .unwrap_throw();
51+ *rc2.borrow_mut() = Some(f);
52+
53+ // Run the virtual DOM forever and don't unmount it.
54+ vdom.forget();
55+}
56+
57+cfg_if! {
58+ if #[cfg(feature = "logging")] {
59+ fn init_logging() {
60+ console_error_panic_hook::set_once();
61+ console_log::init_with_level(log::Level::Trace)
62+ .expect_throw("should initialize logging OK");
63+ }
64+ } else {
65+ fn init_logging() {
66+ // Do nothing.
67+ }
68+ }
69+}
70+
71+/// A rendering component that renders two overlapping sets of concentric
72+/// circles that move around and form Moiré patterns.
73+///
74+/// https://en.wikipedia.org/wiki/Moir%C3%A9_pattern
75+pub struct Moire {
76+ // How many circles to render in each of our sets. This is controlled by the
77+ // `<input>` slider.
78+ count: u32,
79+}
80+
81+impl Moire {
82+ /// Construct a new `Moire` rendering component.
83+ ///
84+ /// Defaults to 200 circles.
85+ pub fn new() -> Moire {
86+ Moire { count: 200 }
87+ }
88+
89+ /// Callback for "change" events emitted on the `<input>` slider that
90+ /// controls how many circles we render on each frame.
91+ fn on_change(&mut self, event: web_sys::Event) {
92+ // Get the `<input>` element.
93+ let input = event
94+ .target()
95+ .unwrap_throw()
96+ .unchecked_into::<web_sys::HtmlInputElement>();
97+
98+ // Parse its new value.
99+ let value = input.value();
100+ let value: u32 = value
101+ .parse()
102+ .expect_throw("<input type=\"range\"> value should always be an integer");
103+
104+ // And update our circle count!
105+ self.count = value;
106+ }
107+}
108+
109+impl Render for Moire {
110+ fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
111+ where
112+ 'a: 'bump,
113+ {
114+ use dodrio::builder::*;
115+
116+ let elapsed = web_sys::window()
117+ .unwrap_throw()
118+ .performance()
119+ .unwrap_throw()
120+ .now();
121+ let elapsed = elapsed / 1600.0;
122+
123+ main(bump)
124+ .attr("style", self.moire_style(bump, elapsed))
125+ .children([
126+ // The `<input>` that lets users control how many circles to
127+ // render.
128+ input(bump)
129+ .attr("id", "circle-count")
130+ .attr("type", "range")
131+ .attr("min", "30")
132+ .attr("max", "500")
133+ .attr("value", {
134+ let count = bumpalo::format!(in bump, "{}", self.count);
135+ count.into_bump_str()
136+ })
137+ .on("change", |root, _vdom, event| {
138+ root.unwrap_mut::<Moire>().on_change(event);
139+
140+ // Note: there is no need to manually schedule a
141+ // re-render here, since we already started a
142+ // `requestAnimationFrame` loop that automatically
143+ // re-renders on every frame in the `run` function.
144+ })
145+ .finish(),
146+ // And the actual concentric circles that form the Moire patterns.
147+ self.orbiting_objects(bump, elapsed),
148+ self.lemniscate_objects(bump, elapsed),
149+ ])
150+ .finish()
151+ }
152+}
153+
154+/// Rendering helper methods.
155+impl Moire {
156+ /// Generate the main Moire element's inline CSS styles.
157+ fn moire_style<'a, 'bump>(&'a self, bump: &'bump Bump, elapsed: f64) -> &'bump str
158+ where
159+ 'a: 'bump,
160+ {
161+ let elapsed = elapsed / 3.0;
162+ let color = colors::get_interpolated_color(|(_, bg)| bg, elapsed % 1.0);
163+ let style = bumpalo::format!(
164+ in bump,
165+ "background-color: rgb({}, {}, {})",
166+ color.r,
167+ color.g,
168+ color.b
169+ );
170+ style.into_bump_str()
171+ }
172+
173+ /// Generate the orbiting circle objects.
174+ fn orbiting_objects<'a, 'bump>(&'a self, bump: &'bump Bump, elapsed: f64) -> Node<'bump>
175+ where
176+ 'a: 'bump,
177+ {
178+ const D: f64 = 200.0;
179+ let x = elapsed.sin() * D;
180+ let y = elapsed.cos() * D;
181+ self.moving_object(bump, elapsed, x, y)
182+ }
183+
184+ /// Generate the lemniscate circle objects that interact with the orbiting
185+ /// circle objects to form Moire patterns.
186+ fn lemniscate_objects<'a, 'bump>(&'a self, bump: &'bump Bump, elapsed: f64) -> Node<'bump>
187+ where
188+ 'a: 'bump,
189+ {
190+ const A: f64 = 150.0;
191+ let x = elapsed.sin() * A;
192+ let y = elapsed.sin() * elapsed.cos() * A;
193+ self.moving_object(bump, elapsed, x, y)
194+ }
195+
196+ fn moving_object<'a, 'bump>(
197+ &'a self,
198+ bump: &'bump Bump,
199+ elapsed: f64,
200+ x: f64,
201+ y: f64,
202+ ) -> Node<'bump>
203+ where
204+ 'a: 'bump,
205+ {
206+ use dodrio::builder::*;
207+ div(bump)
208+ .attr("class", "object")
209+ .attr("style", self.moving_object_style(bump, x, y))
210+ .children([self.circle(bump, elapsed, self.count)])
211+ .finish()
212+ }
213+
214+ /// Generate inline CSS styles for a moving object.
215+ fn moving_object_style<'a, 'bump>(&'a self, bump: &'bump Bump, x: f64, y: f64) -> &'bump str
216+ where
217+ 'a: 'bump,
218+ {
219+ bumpalo::format!(in bump, "left: {}px; top: {}px;", x, y).into_bump_str()
220+ }
221+
222+ /// Recursively generate `self.count` concentric circles.
223+ fn circle<'a, 'bump>(&'a self, bump: &'bump Bump, elapsed: f64, n: u32) -> Node<'bump>
224+ where
225+ 'a: 'bump,
226+ {
227+ use dodrio::builder::*;
228+
229+ let r = n * 16;
230+
231+ let mut circle = div(bump)
232+ .attr("class", "circle")
233+ .attr("data-radius", {
234+ let r = bumpalo::format!(in bump, "{}", r);
235+ r.into_bump_str()
236+ })
237+ .attr("style", self.circle_style(bump, elapsed, n, r));
238+
239+ if n > 0 {
240+ circle = circle.child(self.circle(bump, elapsed, n - 1));
241+ }
242+
243+ circle.finish()
244+ }
245+
246+ /// Generate inline CSS styles for a circle.
247+ fn circle_style<'a, 'bump>(
248+ &'a self,
249+ bump: &'bump Bump,
250+ elapsed: f64,
251+ n: u32,
252+ r: u32,
253+ ) -> &'bump str
254+ where
255+ 'a: 'bump,
256+ {
257+ let (border, alpha) = self.circle_color(elapsed, n);
258+ let margin = r / 2 + 3;
259+ let style = bumpalo::format!(
260+ in bump,
261+ "border-color: rgba({}, {}, {}, {});\
262+ margin-left: -{}px;\
263+ margin-top: -{}px;\
264+ width: {}px;\
265+ height: {}px;",
266+ border.r,
267+ border.g,
268+ border.b,
269+ alpha,
270+ margin,
271+ margin,
272+ r,
273+ r
274+ );
275+ style.into_bump_str()
276+ }
277+
278+ /// Compute the color for a circle.
279+ fn circle_color(&self, elapsed: f64, n: u32) -> (colors::Rgb, f64) {
280+ let elapsed = elapsed / 3.0;
281+ let base = colors::get_interpolated_color(|(fg, _)| fg, elapsed % 1.0);
282+ let lightness = 1.0 - (n as f64) / (self.count as f64);
283+ (base, lightness)
284+ }
285+}
examples/todomvc/Cargo.toml+5−5
@@ -8,11 +8,11 @@ version = "0.1.0"
8cfg-if = "0.1.6"
8cfg-if = "0.1.6"
9dodrio = { path = "../.." }
9dodrio = { path = "../.." }
10futures = "0.1.25"
10futures = "0.1.25"
11−js-sys = "0.3.13"
11+js-sys = "0.3.14"
12serde = { features = ["derive"], version = "1.0.89" }
12serde = { features = ["derive"], version = "1.0.89" }
13serde_json = "1.0.39"
13serde_json = "1.0.39"
14−wasm-bindgen = "0.2.36"
14+wasm-bindgen = "0.2.37"
15−wasm-bindgen-futures = "0.3.13"
15+wasm-bindgen-futures = "0.3.14"
16
16
17# Optional dependencies for logging.
17# Optional dependencies for logging.
18console_error_panic_hook = { optional = true, version = "0.1.6" }
18console_error_panic_hook = { optional = true, version = "0.1.6" }
@@ -20,7 +20,7 @@ console_log = { optional = true, version = "0.1.2" }
20log = { optional = true, version = "0.4.6" }
20log = { optional = true, version = "0.4.6" }
21
21
22[dependencies.web-sys]
22[dependencies.web-sys]
23−version = "0.3.13"
23+version = "0.3.14"
24features = [
24features = [
25 "Document",
25 "Document",
26 "Event",
26 "Event",
@@ -35,7 +35,7 @@ features = [
35]
35]
36
36
37[dev-dependencies]
37[dev-dependencies]
38−wasm-bindgen-test = "0.2.36"
38+wasm-bindgen-test = "0.2.37"
39
39
40[features]
40[features]
41logging = [
41logging = [