diff --git a/Cargo.toml b/Cargo.toml index 5941ba7..c4c19ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ features = [ "DomTokenList", "HtmlCanvasElement", "CanvasRenderingContext2d", - "Performance", + "CanvasGradient", ] [profile.release] diff --git a/dist/railwayka_landing.d.ts b/dist/railwayka_landing.d.ts index f9fa8af..0bda2d8 100644 --- a/dist/railwayka_landing.d.ts +++ b/dist/railwayka_landing.d.ts @@ -11,8 +11,8 @@ export interface InitOutput { readonly __wbindgen_export_1: (a: number, b: number) => number; 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_export_4: (a: number, b: number) => void; + readonly __wbindgen_export_5: (a: number, b: number, c: number, d: number) => void; readonly __wbindgen_start: () => void; } diff --git a/dist/railwayka_landing.js b/dist/railwayka_landing.js index 2b765ae..2e16c44 100644 --- a/dist/railwayka_landing.js +++ b/dist/railwayka_landing.js @@ -237,12 +237,12 @@ export function main() { wasm.main(); } -function __wbg_adapter_4(arg0, arg1, arg2, arg3) { - wasm.__wbindgen_export_4(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3)); +function __wbg_adapter_4(arg0, arg1) { + wasm.__wbindgen_export_4(arg0, arg1); } -function __wbg_adapter_9(arg0, arg1) { - wasm.__wbindgen_export_5(arg0, arg1); +function __wbg_adapter_7(arg0, arg1, arg2, arg3) { + wasm.__wbindgen_export_5(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3)); } const EXPECTED_RESPONSE_TYPES = new Set(['basic', 'cors', 'default']); @@ -283,6 +283,9 @@ async function __wbg_load(module, imports) { function __wbg_get_imports() { const imports = {}; imports.wbg = {}; + imports.wbg.__wbg_addColorStop_02d04059a526a3f4 = function() { return handleError(function (arg0, arg1, arg2, arg3) { + getObject(arg0).addColorStop(arg1, getStringFromWasm0(arg2, arg3)); + }, arguments) }; imports.wbg.__wbg_addEventListener_775911544ac9d643 = function() { return handleError(function (arg0, arg1, arg2, arg3) { getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3)); }, arguments) }; @@ -315,6 +318,10 @@ function __wbg_get_imports() { const ret = getObject(arg0).createElement(getStringFromWasm0(arg1, arg2)); return addHeapObject(ret); }, arguments) }; + imports.wbg.__wbg_createRadialGradient_b10566e092cb7089 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5, arg6) { + const ret = getObject(arg0).createRadialGradient(arg1, arg2, arg3, arg4, arg5, arg6); + return addHeapObject(ret); + }, arguments) }; imports.wbg.__wbg_document_7d29d139bd619045 = function(arg0) { const ret = getObject(arg0).document; return isLikeNone(ret) ? 0 : addHeapObject(ret); @@ -450,6 +457,9 @@ function __wbg_get_imports() { imports.wbg.__wbg_setclassName_c8bccad917b973f4 = function(arg0, arg1, arg2) { getObject(arg0).className = getStringFromWasm0(arg1, arg2); }; + imports.wbg.__wbg_setfillStyle_9d745b4440df0b8b = function(arg0, arg1) { + getObject(arg0).fillStyle = getObject(arg1); + }; imports.wbg.__wbg_setfillStyle_a9ad5b25cf62a5bc = function(arg0, arg1, arg2) { getObject(arg0).fillStyle = getStringFromWasm0(arg1, arg2); }; @@ -530,7 +540,7 @@ function __wbg_get_imports() { }; 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); + const ret = makeMutClosure(arg0, arg1, 1, __wbg_adapter_4); return addHeapObject(ret); }; imports.wbg.__wbindgen_cast_d6cd19b81560fd6e = function(arg0) { @@ -540,7 +550,7 @@ function __wbg_get_imports() { }; 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); + const ret = makeMutClosure(arg0, arg1, 1, __wbg_adapter_7); return addHeapObject(ret); }; imports.wbg.__wbindgen_object_clone_ref = function(arg0) { diff --git a/dist/railwayka_landing_bg.wasm b/dist/railwayka_landing_bg.wasm index 9c7bc77..6b25673 100644 Binary files a/dist/railwayka_landing_bg.wasm and b/dist/railwayka_landing_bg.wasm differ diff --git a/dist/railwayka_landing_bg.wasm.d.ts b/dist/railwayka_landing_bg.wasm.d.ts index 49d0bbf..17d4b5f 100644 --- a/dist/railwayka_landing_bg.wasm.d.ts +++ b/dist/railwayka_landing_bg.wasm.d.ts @@ -6,6 +6,6 @@ export const __wbindgen_export_0: (a: number) => void; export const __wbindgen_export_1: (a: number, b: number) => 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_export_4: (a: number, b: number) => void; +export const __wbindgen_export_5: (a: number, b: number, c: number, d: number) => void; export const __wbindgen_start: () => void; diff --git a/src/lib.rs b/src/lib.rs index c16c349..bc97005 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,49 +22,78 @@ struct Particle { 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, + vx: 0.0, + vy: 0.0, + size: js_sys::Math::random() * 2.0 + 2.0, life: 1.0, - max_life: js_sys::Math::random() * 100.0 + 100.0, + max_life: js_sys::Math::random() * 500.0 + 500.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(); + // Multiple layered flow fields for complex fluid motion + let scale1 = 0.003; + let scale2 = 0.006; + let scale3 = 0.001; - self.vx += flow_x * 0.1; - self.vy += flow_y * 0.1; + // Layer 1: Large sweeping currents + let angle1 = (self.x * scale1 + time * 0.0002).sin() * 3.0 + + (self.y * scale1 + time * 0.0001).cos() * 3.0; - // Damping for smooth motion - self.vx *= 0.98; - self.vy *= 0.98; + // Layer 2: Medium turbulence + let angle2 = (self.x * scale2 - time * 0.0003).cos() * 2.0 + + (self.y * scale2 + time * 0.0002).sin() * 2.0; + // Layer 3: Fine detail + let angle3 = ((self.x * scale3 + time * 0.0001).sin() + + (self.y * scale3 - time * 0.00015).cos()) * 1.5; + + // Combine flow fields + let angle = angle1 + angle2 + angle3; + + // Convert to force + let force = 0.15; + let fx = angle.cos() * force; + let fy = angle.sin() * force; + + // Apply force with gentle acceleration + self.vx += fx; + self.vy += fy; + + // Smooth damping for fluid motion + self.vx *= 0.95; + self.vy *= 0.95; + + // Limit maximum velocity for smooth flow + let max_speed = 2.0; + let speed = (self.vx * self.vx + self.vy * self.vy).sqrt(); + if speed > max_speed { + self.vx = (self.vx / speed) * max_speed; + self.vy = (self.vy / speed) * max_speed; + } + + // Update position 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; + // Wrap around edges smoothly + if self.x < -10.0 { + self.x = width + 10.0; } - if self.x > width { - self.x = 0.0; + if self.x > width + 10.0 { + self.x = -10.0; } - if self.y < 0.0 { - self.y = height; + if self.y < -10.0 { + self.y = height + 10.0; } - if self.y > height { - self.y = 0.0; + if self.y > height + 10.0 { + self.y = -10.0; } } @@ -74,18 +103,30 @@ impl Particle { 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); + // Glow effect + let glow_gradient = ctx.create_radial_gradient( + self.x, self.y, 0.0, + self.x, self.y, self.size * 3.0 + ).ok(); + + if let Some(gradient) = glow_gradient { + let alpha_hex = format!("{:02x}", (alpha * 60.0) as u8); + gradient.add_color_stop(0.0, &format!("#{}", alpha_hex.repeat(3))).ok(); + gradient.add_color_stop(0.5, &format!("rgba(50, 255, 150, {})", alpha * 0.3)).ok(); + gradient.add_color_stop(1.0, "rgba(50, 255, 150, 0)").ok(); + + ctx.set_fill_style(&JsValue::from(gradient)); + ctx.begin_path(); + let _ = ctx.arc(self.x, self.y, self.size * 3.0, 0.0, std::f64::consts::PI * 2.0); + ctx.fill(); + } + + // Core particle + let color = format!("rgba(100, 255, 180, {})", 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(); + let _ = ctx.arc(self.x, self.y, self.size, 0.0, std::f64::consts::PI * 2.0); ctx.fill(); } } @@ -105,7 +146,7 @@ impl ParticleSystem { particles: Vec::new(), width, height, - max_particles: 800, + max_particles: 1200, time: 0.0, } } @@ -173,6 +214,8 @@ pub fn main() -> Result<(), JsValue> { } fn setup_particle_canvas(document: &Document, body: &HtmlElement, window: &Window) -> Result<(), JsValue> { + console::log_1(&"🎨 Setting up particle canvas...".into()); + // Create canvas element let canvas = document .create_element("canvas")? @@ -183,10 +226,13 @@ fn setup_particle_canvas(document: &Document, body: &HtmlElement, window: &Windo let width = window.inner_width()?.as_f64().unwrap_or(800.0); let height = window.inner_height()?.as_f64().unwrap_or(600.0); + console::log_1(&format!("📐 Canvas size: {}x{}", width, height).into()); + canvas.set_width(width as u32); canvas.set_height(height as u32); body.append_child(&canvas)?; + console::log_1(&"✅ Canvas appended to body".into()); // Get 2D context let context = canvas @@ -198,18 +244,40 @@ fn setup_particle_canvas(document: &Document, body: &HtmlElement, window: &Windo let particle_system = Rc::new(RefCell::new(ParticleSystem::new(width, height))); // Spawn initial particles - particle_system.borrow_mut().spawn_particles(400); + particle_system.borrow_mut().spawn_particles(600); + console::log_1(&format!("🌟 Spawned {} initial particles", particle_system.borrow().particles.len()).into()); // Setup animation loop let system_clone = particle_system.clone(); let context_clone = context.clone(); + let frame_count = Rc::new(RefCell::new(0u32)); + let window_clone = window.clone(); let animate_closure = Rc::new(RefCell::new(None::>)); let animate_clone = animate_closure.clone(); + let frame_clone = frame_count.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)"); + *frame_clone.borrow_mut() += 1; + + if *frame_clone.borrow() == 1 { + console::log_1(&"🎬 Animation loop started!".into()); + } + + if *frame_clone.borrow() % 60 == 0 { + let sys = system_clone.borrow(); + console::log_1(&format!("🔄 Frame {}, {} particles", + *frame_clone.borrow(), + sys.particles.len()).into()); + + if let Some(p) = sys.particles.first() { + console::log_1(&format!("🔍 First particle: x={:.1}, y={:.1}, vx={:.2}, vy={:.2}, size={:.1}, life={:.1}", + p.x, p.y, p.vx, p.vy, p.size, p.life).into()); + } + } + + // Semi-transparent clear for fluid trails + context_clone.set_fill_style_str("rgba(10, 14, 23, 0.12)"); context_clone.fill_rect(0.0, 0.0, width, height); // Update and draw particles @@ -218,7 +286,7 @@ fn setup_particle_canvas(document: &Document, body: &HtmlElement, window: &Windo // Request next frame if let Some(closure) = animate_clone.borrow().as_ref() { - window + window_clone .request_animation_frame(closure.as_ref().unchecked_ref()) .ok(); }