From 710f48de69aa36aaea52d2662dd90d151872878b Mon Sep 17 00:00:00 2001 From: rail Date: Sat, 22 Nov 2025 22:30:28 +0300 Subject: [PATCH] Add fluid particle animation system with Rust/WASM - Implement 800+ flowing green particles with fluid motion dynamics - Add flow field physics using sine waves for organic movement - Particles feature trails, glow effects, and smooth edge wrapping - Canvas 2D rendering from Rust/WASM for 60fps performance - Fullscreen background animation with automatic window resize - Green gradient colors (emerald to lime, HSL 120-160) - Optimized with semi-transparent clearing for fluid trail effects Inspired by WebAssembly particle demos and WebGPU fluid simulations --- Cargo.toml | 3 + dist/railwayka_landing.d.ts | 1 + dist/railwayka_landing.js | 158 ++++++++++++++++++- dist/railwayka_landing_bg.wasm | Bin 26308 -> 61818 bytes dist/railwayka_landing_bg.wasm.d.ts | 1 + dist/style.css | 15 +- src/lib.rs | 235 +++++++++++++++++++++++++++- style.css | 15 +- 8 files changed, 422 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4075c89..5941ba7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,9 @@ features = [ "DomRect", "NodeList", "DomTokenList", + "HtmlCanvasElement", + "CanvasRenderingContext2d", + "Performance", ] [profile.release] diff --git a/dist/railwayka_landing.d.ts b/dist/railwayka_landing.d.ts index 7accea5..f9fa8af 100644 --- a/dist/railwayka_landing.d.ts +++ b/dist/railwayka_landing.d.ts @@ -12,6 +12,7 @@ export interface InitOutput { readonly __wbindgen_export_2: (a: number, b: number, c: number, d: number) => number; readonly __wbindgen_export_3: WebAssembly.Table; readonly __wbindgen_export_4: (a: number, b: number, c: number, d: number) => void; + readonly __wbindgen_export_5: (a: number, b: number) => void; readonly __wbindgen_start: () => void; } diff --git a/dist/railwayka_landing.js b/dist/railwayka_landing.js index 12d6721..2b765ae 100644 --- a/dist/railwayka_landing.js +++ b/dist/railwayka_landing.js @@ -122,6 +122,71 @@ function getDataViewMemory0() { return cachedDataViewMemory0; } +function debugString(val) { + // primitive types + const type = typeof val; + if (type == 'number' || type == 'boolean' || val == null) { + return `${val}`; + } + if (type == 'string') { + return `"${val}"`; + } + if (type == 'symbol') { + const description = val.description; + if (description == null) { + return 'Symbol'; + } else { + return `Symbol(${description})`; + } + } + if (type == 'function') { + const name = val.name; + if (typeof name == 'string' && name.length > 0) { + return `Function(${name})`; + } else { + return 'Function'; + } + } + // objects + if (Array.isArray(val)) { + const length = val.length; + let debug = '['; + if (length > 0) { + debug += debugString(val[0]); + } + for(let i = 1; i < length; i++) { + debug += ', ' + debugString(val[i]); + } + debug += ']'; + return debug; + } + // Test for built-in + const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); + let className; + if (builtInMatches && builtInMatches.length > 1) { + className = builtInMatches[1]; + } else { + // Failed to match the standard '[object ClassName]' + return toString.call(val); + } + if (className == 'Object') { + // we're a user defined class or Object + // JSON.stringify avoids problems with cycles, and is generally much + // easier than looping through ownProperties of `val`. + try { + return 'Object(' + JSON.stringify(val) + ')'; + } catch (_) { + return 'Object'; + } + } + // errors + if (val instanceof Error) { + return `${val.name}: ${val.message}\n${val.stack}`; + } + // TODO we could test for more things here, like `Set`s and `Map`s. + return className; +} + function dropObject(idx) { if (idx < 132) return; heap[idx] = heap_next; @@ -176,6 +241,10 @@ function __wbg_adapter_4(arg0, arg1, arg2, arg3) { wasm.__wbindgen_export_4(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3)); } +function __wbg_adapter_9(arg0, arg1) { + wasm.__wbindgen_export_5(arg0, arg1); +} + const EXPECTED_RESPONSE_TYPES = new Set(['basic', 'cors', 'default']); async function __wbg_load(module, imports) { @@ -214,6 +283,9 @@ async function __wbg_load(module, imports) { function __wbg_get_imports() { const imports = {}; imports.wbg = {}; + imports.wbg.__wbg_addEventListener_775911544ac9d643 = function() { return handleError(function (arg0, arg1, arg2, arg3) { + getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3)); + }, arguments) }; imports.wbg.__wbg_add_4e0283c00f7ecabe = function() { return handleError(function (arg0, arg1, arg2) { getObject(arg0).add(getStringFromWasm0(arg1, arg2)); }, arguments) }; @@ -221,6 +293,12 @@ function __wbg_get_imports() { const ret = getObject(arg0).appendChild(getObject(arg1)); return addHeapObject(ret); }, arguments) }; + imports.wbg.__wbg_arc_61cbec33cc96a55e = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5) { + getObject(arg0).arc(arg1, arg2, arg3, arg4, arg5); + }, arguments) }; + imports.wbg.__wbg_beginPath_119487ebd04e9e1c = function(arg0) { + getObject(arg0).beginPath(); + }; imports.wbg.__wbg_body_8822ca55cb3730d2 = function(arg0) { const ret = getObject(arg0).body; return isLikeNone(ret) ? 0 : addHeapObject(ret); @@ -241,6 +319,12 @@ function __wbg_get_imports() { const ret = getObject(arg0).document; return isLikeNone(ret) ? 0 : addHeapObject(ret); }; + imports.wbg.__wbg_fillRect_a160edfa11fce49b = function(arg0, arg1, arg2, arg3, arg4) { + getObject(arg0).fillRect(arg1, arg2, arg3, arg4); + }; + imports.wbg.__wbg_fill_7b331ac62ac7c50b = function(arg0) { + getObject(arg0).fill(); + }; imports.wbg.__wbg_getAttribute_8bfaf67e99ed2ee3 = function(arg0, arg1, arg2, arg3) { const ret = getObject(arg1).getAttribute(getStringFromWasm0(arg2, arg3)); var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_export_1, wasm.__wbindgen_export_2); @@ -248,10 +332,32 @@ function __wbg_get_imports() { getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); }; + imports.wbg.__wbg_getContext_15e158d04230a6f6 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = getObject(arg0).getContext(getStringFromWasm0(arg1, arg2)); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }, arguments) }; imports.wbg.__wbg_get_0da715ceaecea5c8 = function(arg0, arg1) { const ret = getObject(arg0)[arg1 >>> 0]; return addHeapObject(ret); }; + imports.wbg.__wbg_innerHeight_eacbddff807274db = function() { return handleError(function (arg0) { + const ret = getObject(arg0).innerHeight; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_innerWidth_dd42bfe6b5e91e59 = function() { return handleError(function (arg0) { + const ret = getObject(arg0).innerWidth; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_instanceof_CanvasRenderingContext2d_8c616198ec03b12f = function(arg0) { + let result; + try { + result = getObject(arg0) instanceof CanvasRenderingContext2D; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }; imports.wbg.__wbg_instanceof_Element_162e4334c7d6f450 = function(arg0) { let result; try { @@ -262,6 +368,16 @@ function __wbg_get_imports() { const ret = result; return ret; }; + imports.wbg.__wbg_instanceof_HtmlCanvasElement_299c60950dbb3428 = function(arg0) { + let result; + try { + result = getObject(arg0) instanceof HTMLCanvasElement; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }; imports.wbg.__wbg_instanceof_IntersectionObserverEntry_819e56422a481344 = function(arg0) { let result; try { @@ -320,12 +436,29 @@ function __wbg_get_imports() { const ret = getObject(arg0).querySelectorAll(getStringFromWasm0(arg1, arg2)); return addHeapObject(ret); }, arguments) }; + imports.wbg.__wbg_random_7ed63a0b38ee3b75 = function() { + const ret = Math.random(); + return ret; + }; + imports.wbg.__wbg_requestAnimationFrame_ddc84a7def436784 = function() { return handleError(function (arg0, arg1) { + const ret = getObject(arg0).requestAnimationFrame(getObject(arg1)); + return ret; + }, arguments) }; imports.wbg.__wbg_setAttribute_d1baf9023ad5696f = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { getObject(arg0).setAttribute(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); }, arguments) }; imports.wbg.__wbg_setclassName_c8bccad917b973f4 = function(arg0, arg1, arg2) { getObject(arg0).className = getStringFromWasm0(arg1, arg2); }; + imports.wbg.__wbg_setfillStyle_a9ad5b25cf62a5bc = function(arg0, arg1, arg2) { + getObject(arg0).fillStyle = getStringFromWasm0(arg1, arg2); + }; + imports.wbg.__wbg_setheight_4fce583024b2d088 = function(arg0, arg1) { + getObject(arg0).height = arg1 >>> 0; + }; + imports.wbg.__wbg_setid_891db4c7fb5255d1 = function(arg0, arg1, arg2) { + getObject(arg0).id = getStringFromWasm0(arg1, arg2); + }; imports.wbg.__wbg_setinnerHTML_34e240d6b8e8260c = function(arg0, arg1, arg2) { getObject(arg0).innerHTML = getStringFromWasm0(arg1, arg2); }; @@ -335,6 +468,9 @@ function __wbg_get_imports() { imports.wbg.__wbg_setthreshold_7daca4126268ea47 = function(arg0, arg1) { getObject(arg0).threshold = getObject(arg1); }; + imports.wbg.__wbg_setwidth_40a6ed203b92839d = function(arg0, arg1) { + getObject(arg0).width = arg1 >>> 0; + }; imports.wbg.__wbg_static_accessor_GLOBAL_8921f820c2ce3f12 = function() { const ret = typeof global === 'undefined' ? null : global; return isLikeNone(ret) ? 0 : addHeapObject(ret); @@ -364,10 +500,23 @@ function __wbg_get_imports() { const ret = false; return ret; }; + imports.wbg.__wbg_wbindgendebugstring_99ef257a3ddda34d = function(arg0, arg1) { + const ret = debugString(getObject(arg1)); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export_1, wasm.__wbindgen_export_2); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; imports.wbg.__wbg_wbindgenisundefined_c4b71d073b92f3c5 = function(arg0) { const ret = getObject(arg0) === undefined; return ret; }; + imports.wbg.__wbg_wbindgennumberget_f74b4c7525ac05cb = function(arg0, arg1) { + const obj = getObject(arg1); + const ret = typeof(obj) === 'number' ? obj : undefined; + getDataViewMemory0().setFloat64(arg0 + 8 * 1, isLikeNone(ret) ? 0 : ret, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, !isLikeNone(ret), true); + }; imports.wbg.__wbg_wbindgenrethrow_01815c9239d70cc2 = function(arg0) { throw takeObject(arg0); }; @@ -379,13 +528,18 @@ function __wbg_get_imports() { const ret = getStringFromWasm0(arg0, arg1); return addHeapObject(ret); }; + imports.wbg.__wbindgen_cast_aaa93aae03c115ab = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 1, function: Function { arguments: [], shim_idx: 2, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, 1, __wbg_adapter_9); + return addHeapObject(ret); + }; imports.wbg.__wbindgen_cast_d6cd19b81560fd6e = function(arg0) { // Cast intrinsic for `F64 -> Externref`. const ret = arg0; return addHeapObject(ret); }; - imports.wbg.__wbindgen_cast_fe9164a03cdb0b6b = function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { dtor_idx: 1, function: Function { arguments: [NamedExternref("Array"), NamedExternref("IntersectionObserver")], shim_idx: 2, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + imports.wbg.__wbindgen_cast_e58c2e3d55f4d158 = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 1, function: Function { arguments: [NamedExternref("Array"), NamedExternref("IntersectionObserver")], shim_idx: 4, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. const ret = makeMutClosure(arg0, arg1, 1, __wbg_adapter_4); return addHeapObject(ret); }; diff --git a/dist/railwayka_landing_bg.wasm b/dist/railwayka_landing_bg.wasm index 238df8f180319235ed4e95bec8c501369c9bd2d1..9c7bc77abfa6658996d266a2de3c57ed23d656ec 100644 GIT binary patch literal 61818 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 literal 26308 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 number; export const __wbindgen_export_2: (a: number, b: number, c: number, d: number) => number; export const __wbindgen_export_3: WebAssembly.Table; export const __wbindgen_export_4: (a: number, b: number, c: number, d: number) => void; +export const __wbindgen_export_5: (a: number, b: number) => void; export const __wbindgen_start: () => void; diff --git a/dist/style.css b/dist/style.css index 3ac623f..6d8b6a7 100644 --- a/dist/style.css +++ b/dist/style.css @@ -27,6 +27,17 @@ body { min-height: 100vh; } +/* Particle canvas background */ +#particle-canvas { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 0; + pointer-events: none; +} + /* Loading screen */ #loading { position: fixed; @@ -72,9 +83,9 @@ body { align-items: center; justify-content: center; text-align: center; - background: linear-gradient(135deg, #0a0e17 0%, #1a1f2e 100%); position: relative; overflow: hidden; + z-index: 1; } .hero::before { @@ -130,6 +141,8 @@ body { /* Section Styles */ section { padding: 5rem 0; + position: relative; + z-index: 1; } .section-title { diff --git a/src/lib.rs b/src/lib.rs index fceac10..c16c349 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,154 @@ use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; -use web_sys::{console, Document, Element, HtmlElement, Window, IntersectionObserver, IntersectionObserverEntry, IntersectionObserverInit}; +use web_sys::{ + console, Document, Element, HtmlElement, Window, + IntersectionObserver, IntersectionObserverEntry, IntersectionObserverInit, + HtmlCanvasElement, CanvasRenderingContext2d, +}; +use std::cell::RefCell; +use std::rc::Rc; + +// Particle structure +struct Particle { + x: f64, + y: f64, + vx: f64, + vy: f64, + size: f64, + life: f64, + max_life: f64, + hue: f64, +} + +impl Particle { + fn new(x: f64, y: f64, hue: f64) -> Self { + let angle = js_sys::Math::random() * std::f64::consts::PI * 2.0; + let speed = js_sys::Math::random() * 0.5 + 0.2; + Self { + x, + y, + vx: angle.cos() * speed, + vy: angle.sin() * speed, + size: js_sys::Math::random() * 2.0 + 1.0, + life: 1.0, + max_life: js_sys::Math::random() * 100.0 + 100.0, + hue, + } + } + + fn update(&mut self, width: f64, height: f64, time: f64) { + // Flow field influence using sine waves for fluid motion + let flow_x = ((self.y * 0.01 + time * 0.0005).sin() * 0.5).sin(); + let flow_y = ((self.x * 0.01 + time * 0.0003).cos() * 0.5).cos(); + + self.vx += flow_x * 0.1; + self.vy += flow_y * 0.1; + + // Damping for smooth motion + self.vx *= 0.98; + self.vy *= 0.98; + + self.x += self.vx; + self.y += self.vy; + + self.life -= 1.0; + + // Wrap around edges with smooth transition + if self.x < 0.0 { + self.x = width; + } + if self.x > width { + self.x = 0.0; + } + if self.y < 0.0 { + self.y = height; + } + if self.y > height { + self.y = 0.0; + } + } + + fn is_dead(&self) -> bool { + self.life <= 0.0 + } + + fn draw(&self, ctx: &CanvasRenderingContext2d) { + let alpha = (self.life / self.max_life).min(1.0); + let color = format!("hsla({}, 70%, 50%, {})", self.hue, alpha * 0.8); + + ctx.set_fill_style_str(&color); + ctx.begin_path(); + ctx.arc(self.x, self.y, self.size, 0.0, std::f64::consts::PI * 2.0).ok(); + ctx.fill(); + + // Add glow effect + let glow_color = format!("hsla({}, 70%, 60%, {})", self.hue, alpha * 0.3); + ctx.set_fill_style_str(&glow_color); + ctx.begin_path(); + ctx.arc(self.x, self.y, self.size * 2.0, 0.0, std::f64::consts::PI * 2.0).ok(); + ctx.fill(); + } +} + +// Particle system +struct ParticleSystem { + particles: Vec, + width: f64, + height: f64, + max_particles: usize, + time: f64, +} + +impl ParticleSystem { + fn new(width: f64, height: f64) -> Self { + Self { + particles: Vec::new(), + width, + height, + max_particles: 800, + time: 0.0, + } + } + + fn spawn_particles(&mut self, count: usize) { + for _ in 0..count { + if self.particles.len() < self.max_particles { + let x = js_sys::Math::random() * self.width; + let y = js_sys::Math::random() * self.height; + // Green hues: 120-160 (emerald to lime) + let hue = js_sys::Math::random() * 40.0 + 120.0; + self.particles.push(Particle::new(x, y, hue)); + } + } + } + + fn update(&mut self) { + self.time += 1.0; + + // Update existing particles + for particle in &mut self.particles { + particle.update(self.width, self.height, self.time); + } + + // Remove dead particles + self.particles.retain(|p| !p.is_dead()); + + // Spawn new particles + let spawn_count = (self.max_particles - self.particles.len()).min(5); + self.spawn_particles(spawn_count); + } + + fn draw(&self, ctx: &CanvasRenderingContext2d) { + for particle in &self.particles { + particle.draw(ctx); + } + } + + fn resize(&mut self, width: f64, height: f64) { + self.width = width; + self.height = height; + } +} #[wasm_bindgen(start)] pub fn main() -> Result<(), JsValue> { @@ -12,6 +160,10 @@ pub fn main() -> Result<(), JsValue> { // Clear body and build page if let Some(body) = document.body() { body.set_inner_html(""); + + // Create and setup particle canvas + setup_particle_canvas(&document, &body, &window)?; + build_page(&document, &body)?; setup_scroll_animations(&document)?; } @@ -20,6 +172,85 @@ pub fn main() -> Result<(), JsValue> { Ok(()) } +fn setup_particle_canvas(document: &Document, body: &HtmlElement, window: &Window) -> Result<(), JsValue> { + // Create canvas element + let canvas = document + .create_element("canvas")? + .dyn_into::()?; + canvas.set_id("particle-canvas"); + + // Get window dimensions + let width = window.inner_width()?.as_f64().unwrap_or(800.0); + let height = window.inner_height()?.as_f64().unwrap_or(600.0); + + canvas.set_width(width as u32); + canvas.set_height(height as u32); + + body.append_child(&canvas)?; + + // Get 2D context + let context = canvas + .get_context("2d")? + .unwrap() + .dyn_into::()?; + + // Initialize particle system + let particle_system = Rc::new(RefCell::new(ParticleSystem::new(width, height))); + + // Spawn initial particles + particle_system.borrow_mut().spawn_particles(400); + + // Setup animation loop + let system_clone = particle_system.clone(); + let context_clone = context.clone(); + + let animate_closure = Rc::new(RefCell::new(None::>)); + let animate_clone = animate_closure.clone(); + + *animate_closure.borrow_mut() = Some(Closure::wrap(Box::new(move || { + // Clear canvas with trail effect (don't fully clear for fluid trails) + context_clone.set_fill_style_str("rgba(10, 14, 23, 0.08)"); + context_clone.fill_rect(0.0, 0.0, width, height); + + // Update and draw particles + system_clone.borrow_mut().update(); + system_clone.borrow().draw(&context_clone); + + // Request next frame + if let Some(closure) = animate_clone.borrow().as_ref() { + window + .request_animation_frame(closure.as_ref().unchecked_ref()) + .ok(); + } + }) as Box)); + + // Start animation + if let Some(closure) = animate_closure.borrow().as_ref() { + window.request_animation_frame(closure.as_ref().unchecked_ref())?; + } + + // Setup resize handler + let canvas_clone = canvas.clone(); + let system_resize = particle_system.clone(); + let window_clone = window.clone(); + + let resize_closure = Closure::wrap(Box::new(move || { + let new_width = window_clone.inner_width().unwrap().as_f64().unwrap_or(800.0); + let new_height = window_clone.inner_height().unwrap().as_f64().unwrap_or(600.0); + + canvas_clone.set_width(new_width as u32); + canvas_clone.set_height(new_height as u32); + + system_resize.borrow_mut().resize(new_width, new_height); + }) as Box); + + window.add_event_listener_with_callback("resize", resize_closure.as_ref().unchecked_ref())?; + resize_closure.forget(); + + console::log_1(&"🌊 Particle system initialized!".into()); + Ok(()) +} + fn build_page(document: &Document, body: &HtmlElement) -> Result<(), JsValue> { // Hero Section let hero = create_hero(document)?; @@ -265,7 +496,7 @@ fn setup_scroll_animations(document: &Document) -> Result<(), JsValue> { }) as Box); // Create observer with options - let mut options = IntersectionObserverInit::new(); + let options = IntersectionObserverInit::new(); options.set_threshold(&JsValue::from_f64(0.1)); let observer = IntersectionObserver::new_with_options( diff --git a/style.css b/style.css index 3ac623f..6d8b6a7 100644 --- a/style.css +++ b/style.css @@ -27,6 +27,17 @@ body { min-height: 100vh; } +/* Particle canvas background */ +#particle-canvas { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 0; + pointer-events: none; +} + /* Loading screen */ #loading { position: fixed; @@ -72,9 +83,9 @@ body { align-items: center; justify-content: center; text-align: center; - background: linear-gradient(135deg, #0a0e17 0%, #1a1f2e 100%); position: relative; overflow: hidden; + z-index: 1; } .hero::before { @@ -130,6 +141,8 @@ body { /* Section Styles */ section { padding: 5rem 0; + position: relative; + z-index: 1; } .section-title {