@@ -0,0 +1,173 @@
1+use dodrio::bumpalo::{self, Bump};
2+use dodrio::{Node, Render, Vdom};
3+use std::cell::RefCell;
4+use std::rc::Rc;
5+use wasm_bindgen::prelude::*;
6+use wasm_bindgen::JsCast;
7+use web_sys::Window;
8+
9+// Triangle container defining target size.
10+struct Container {
11+ size: f64,
12+}
13+
14+impl Container {
15+ // Construct a new container.
16+ pub fn new() -> Container {
17+ Container { size: 25.0 }
18+ }
19+
20+ // Generate the container style to fluctuate triangle width.
21+ fn container_transform<'a, 'bump>(&'a self, bump: &'bump Bump, elapsed: f64) -> &'bump str
22+ where
23+ 'a: 'bump,
24+ {
25+ let t = elapsed % 10.0;
26+ let scale = 0.45 + (if t > 5.0 { 10.0 - t } else { t }) / 40.0;
27+ let transform = bumpalo::format!(
28+ in bump,
29+ "transform: scaleX({}) scaleY(0.7) translateZ(0.1px)",
30+ scale
31+ );
32+ transform.into_bump_str()
33+ }
34+
35+ // Generate the dot's position on the grid.
36+ fn dot_style<'a, 'bump>(&'a self, bump: &'bump Bump, x: f64, y: f64) -> &'bump str
37+ where
38+ 'a: 'bump,
39+ {
40+ let s = self.size * 1.3;
41+ let radius = s / 2.0;
42+ let styles = bumpalo::format!(
43+ in bump,
44+ r#"
45+ width: {}px;
46+ height: {}px;
47+ left: {}px;
48+ top: {}px;
49+ border-radius: {}px;
50+ line-height: {}px;
51+ "#,
52+ s, s, x, y, radius, s
53+ );
54+ styles.into_bump_str()
55+ }
56+
57+ // Create a dot node.
58+ fn dot<'a, 'bump>(&'a self, bump: &'bump Bump, x: f64, y: f64, content: u32) -> Node<'bump>
59+ where
60+ 'a: 'bump,
61+ {
62+ use dodrio::builder::{div, text};
63+
64+ div(bump)
65+ .attr("class", "dot")
66+ .attr("style", self.dot_style(bump, x, y))
67+ .child(text(
68+ bumpalo::format!(in bump, "{}", content).into_bump_str(),
69+ ))
70+ .finish()
71+ }
72+
73+ // Create a flattened vector of dot nodes (arranged in recursive triangles)
74+ fn triangle<'a, 'bump>(
75+ &'a self,
76+ bump: &'bump Bump,
77+ x: f64,
78+ y: f64,
79+ s: f64,
80+ content: u32,
81+ children: &mut bumpalo::collections::Vec<Node<'bump>>,
82+ ) where
83+ 'a: 'bump,
84+ {
85+ if s <= self.size {
86+ children.push(self.dot(bump, x, y, content));
87+ return;
88+ }
89+
90+ let s = s / 2.0;
91+
92+ // Top of triangle
93+ self.triangle(bump, x, y - (s / 2.0), s, content, children);
94+ // Bottom-left of triangle
95+ self.triangle(bump, x - s, y + (s / 2.0), s, content, children);
96+ // Bottom-right of triangle
97+ self.triangle(bump, x + s, y + (s / 2.0), s, content, children);
98+ }
99+}
100+
101+impl Render for Container {
102+ fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
103+ where
104+ 'a: 'bump,
105+ {
106+ use dodrio::builder::div;
107+
108+ let elapsed = web_sys::window()
109+ .unwrap_throw()
110+ .performance()
111+ .unwrap_throw()
112+ .now()
113+ / 1000.0;
114+
115+ let modulus = elapsed as u32 % 10;
116+
117+ let mut children = bumpalo::collections::Vec::new_in(bump);
118+ self.triangle(bump, 0.0, 0.0, 1000.0, modulus, &mut children);
119+
120+ div(bump)
121+ .attr("class", "container")
122+ .attr("style", self.container_transform(bump, elapsed))
123+ .children(children)
124+ .finish()
125+ }
126+}
127+
128+// Kick off a loop that keeps re-rendering on every animation frame.
129+fn animate(window: Window, vdom: Vdom) {
130+ let rc: Rc<RefCell<Option<Closure<FnMut()>>>> = Rc::new(RefCell::new(None));
131+ let rc2 = rc.clone();
132+ let window2 = window.clone();
133+ let weak = vdom.weak();
134+ let f = Closure::wrap(Box::new(move || {
135+ weak.schedule_render();
136+ window
137+ .request_animation_frame(
138+ rc.borrow()
139+ .as_ref()
140+ .unwrap_throw()
141+ .as_ref()
142+ .unchecked_ref::<js_sys::Function>(),
143+ )
144+ .unwrap_throw();
145+ }) as Box<FnMut()>);
146+ window2
147+ .request_animation_frame(f.as_ref().unchecked_ref::<js_sys::Function>())
148+ .unwrap_throw();
149+ *rc2.borrow_mut() = Some(f);
150+
151+ // Run the virtual DOM and its listeners forever.
152+ vdom.forget();
153+}
154+
155+#[wasm_bindgen(start)]
156+pub fn run() {
157+ // Set up the panic hook
158+ console_error_panic_hook::set_once();
159+
160+ // Get the scene element to render within
161+ let window = web_sys::window().unwrap();
162+ let document = window.document().unwrap();
163+ let scene = document.get_element_by_id("scene").unwrap();
164+
165+ // Construct a new Container component.
166+ let container = Container::new();
167+
168+ // Mount our container component to the scene div.
169+ let vdom = Vdom::new(&scene, container);
170+
171+ // Kick off animation loop
172+ animate(window, vdom);
173+}