diff --git a/dist/railwayka_landing.js b/dist/railwayka_landing.js index 2e16c44..58c717f 100644 --- a/dist/railwayka_landing.js +++ b/dist/railwayka_landing.js @@ -237,11 +237,11 @@ export function main() { wasm.main(); } -function __wbg_adapter_4(arg0, arg1) { +function __wbg_adapter_8(arg0, arg1) { wasm.__wbindgen_export_4(arg0, arg1); } -function __wbg_adapter_7(arg0, arg1, arg2, arg3) { +function __wbg_adapter_11(arg0, arg1, arg2, arg3) { wasm.__wbindgen_export_5(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3)); } @@ -296,9 +296,6 @@ 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(); }; @@ -314,14 +311,17 @@ function __wbg_get_imports() { const ret = getObject(arg0).classList; return addHeapObject(ret); }; + imports.wbg.__wbg_closePath_58530240bb00a7fc = function(arg0) { + getObject(arg0).closePath(); + }; imports.wbg.__wbg_createElement_4909dfa2011f2abe = function() { return handleError(function (arg0, arg1, arg2) { 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); + imports.wbg.__wbg_createLinearGradient_098463fca2d0190f = function(arg0, arg1, arg2, arg3, arg4) { + const ret = getObject(arg0).createLinearGradient(arg1, arg2, arg3, arg4); return addHeapObject(ret); - }, arguments) }; + }; imports.wbg.__wbg_document_7d29d139bd619045 = function(arg0) { const ret = getObject(arg0).document; return isLikeNone(ret) ? 0 : addHeapObject(ret); @@ -421,9 +421,15 @@ function __wbg_get_imports() { const ret = getObject(arg0).length; return ret; }; + imports.wbg.__wbg_lineTo_d9b895383c2303ba = function(arg0, arg1, arg2) { + getObject(arg0).lineTo(arg1, arg2); + }; imports.wbg.__wbg_log_6c7b5f4f00b8ce3f = function(arg0) { console.log(getObject(arg0)); }; + imports.wbg.__wbg_moveTo_a0b1ec729ba8ee5d = function(arg0, arg1, arg2) { + getObject(arg0).moveTo(arg1, arg2); + }; imports.wbg.__wbg_new_19c25a3f2fa63a02 = function() { const ret = new Object(); return addHeapObject(ret); @@ -443,10 +449,6 @@ 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; @@ -540,7 +542,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_4); + const ret = makeMutClosure(arg0, arg1, 1, __wbg_adapter_8); return addHeapObject(ret); }; imports.wbg.__wbindgen_cast_d6cd19b81560fd6e = function(arg0) { @@ -550,7 +552,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_7); + const ret = makeMutClosure(arg0, arg1, 1, __wbg_adapter_11); 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 6b25673..3101340 100644 Binary files a/dist/railwayka_landing_bg.wasm and b/dist/railwayka_landing_bg.wasm differ diff --git a/src/lib.rs b/src/lib.rs index bc97005..d208ace 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,180 +8,141 @@ use web_sys::{ 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, +// Wave layer for fluid simulation +struct WaveLayer { + amplitude: f64, + frequency: f64, + speed: f64, + offset: f64, } -impl Particle { - fn new(x: f64, y: f64, hue: f64) -> Self { - Self { - x, - y, - vx: 0.0, - vy: 0.0, - size: js_sys::Math::random() * 2.0 + 2.0, - life: 1.0, - max_life: js_sys::Math::random() * 500.0 + 500.0, - hue, - } - } - - fn update(&mut self, width: f64, height: f64, time: f64) { - // Multiple layered flow fields for complex fluid motion - let scale1 = 0.003; - let scale2 = 0.006; - let scale3 = 0.001; - - // 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; - - // 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 smoothly - if self.x < -10.0 { - self.x = width + 10.0; - } - if self.x > width + 10.0 { - self.x = -10.0; - } - if self.y < -10.0 { - self.y = height + 10.0; - } - if self.y > height + 10.0 { - self.y = -10.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); - - // 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(); - let _ = ctx.arc(self.x, self.y, self.size, 0.0, std::f64::consts::PI * 2.0); - ctx.fill(); - } -} - -// Particle system -struct ParticleSystem { - particles: Vec, +// Fluid wave system +struct FluidWaves { width: f64, height: f64, - max_particles: usize, time: f64, + layers: Vec, } -impl ParticleSystem { +impl FluidWaves { fn new(width: f64, height: f64) -> Self { + // Create multiple wave layers with different properties (faster speeds) + let layers = vec![ + // Larger, slower primary waves for big curves + WaveLayer { amplitude: 0.25, frequency: 0.001, speed: 0.0008, offset: 0.0 }, + // Secondary shape definition + WaveLayer { amplitude: 0.18, frequency: 0.0018, speed: 0.0015, offset: 1.5 }, + // Counter-movement for interest + WaveLayer { amplitude: 0.12, frequency: 0.003, speed: -0.0012, offset: 3.0 }, + // Detail waves + WaveLayer { amplitude: 0.08, frequency: 0.005, speed: 0.002, offset: 4.5 }, + // Fine detail + WaveLayer { amplitude: 0.05, frequency: 0.008, speed: -0.0025, offset: 2.0 }, + ]; + Self { - particles: Vec::new(), width, height, - max_particles: 1200, 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)); - } + layers, } } 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); + // Calculate wave height at a given position with randomness + fn wave_height(&self, x: f64, y: f64, randomness: f64) -> f64 { + let mut height = 0.0; + + for layer in &self.layers { + // Multiple sine waves at different frequencies for organic curves + let wave1 = (x * layer.frequency + self.time * layer.speed + layer.offset).sin(); + let wave2 = (x * layer.frequency * 1.7 - self.time * layer.speed * 0.5).sin(); + let wave3 = (x * layer.frequency * 2.3 + self.time * layer.speed * 1.2 + layer.offset).cos(); + + // Vertical component for depth + let wave_y = (y * layer.frequency * 0.4 - self.time * layer.speed * 0.6).cos(); + + // Combine with noise-like variation + let combined = (wave1 + wave2 * 0.5 + wave3 * 0.3 + wave_y * 0.4) * layer.amplitude; + height += combined * randomness; } - // 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); + height } fn draw(&self, ctx: &CanvasRenderingContext2d) { - for particle in &self.particles { - particle.draw(ctx); + // Draw smooth gradient waves at the bottom + self.draw_smooth_waves(ctx); + } + + fn draw_smooth_waves(&self, ctx: &CanvasRenderingContext2d) { + let num_waves = 12; + let wave_section_height = self.height * 0.6; // Bottom 60% of screen + let start_y = self.height - wave_section_height; + + // Draw from back to front for proper layering + for i in (0..num_waves).rev() { + let progress = i as f64 / (num_waves - 1) as f64; + + // Each wave layer at different depths + let layer_offset = progress * wave_section_height * 0.9; + let base_y = start_y + layer_offset; + + // Varying amplitude for each layer (more variation in front) + // Increased amplitude for "curvier" look + let amplitude = 40.0 + progress * 80.0; + + // Randomness factor increases for front layers + let randomness = 0.8 + progress * 0.4; + + ctx.begin_path(); + ctx.move_to(0.0, base_y); + + // Draw smooth wave curve - increased points for smoothness + let points = 300; + for j in 0..=points { + let x = (j as f64 / points as f64) * self.width; + let y_offset = self.wave_height(x, base_y, randomness) * amplitude; + let y = base_y + y_offset; + + ctx.line_to(x, y); + } + + // Close the path to bottom + ctx.line_to(self.width, self.height); + ctx.line_to(0.0, self.height); + ctx.close_path(); + + // Create gradient for depth effect + let gradient = ctx.create_linear_gradient(0.0, base_y - 80.0, 0.0, self.height); + + // Back layers are more transparent and darker (3D depth) + // Front layers are more opaque and brighter + let alpha_top = 0.05 + progress * 0.25; + let alpha_bottom = 0.1 + progress * 0.3; + + // Deep ocean colors + // Hue shift: Deep Blue (220) -> Teal/Cyan (160) + let hue = 220.0 - progress * 60.0; + let saturation = 50.0 + progress * 20.0; // 50-70% + let lightness_top = 15.0 + progress * 20.0; // 15-35% + let lightness_bottom = 10.0 + progress * 15.0; // 10-25% + + let _ = gradient.add_color_stop(0.0, &format!("hsla({}, {}%, {}%, {})", + hue, saturation, lightness_top, alpha_top)); + + // Middle stop for better volume/highlight + let _ = gradient.add_color_stop(0.4, &format!("hsla({}, {}%, {}%, {})", + hue - 5.0, saturation + 5.0, lightness_top + 5.0, alpha_top * 1.2)); + + let _ = gradient.add_color_stop(1.0, &format!("hsla({}, {}%, {}%, {})", + hue - 15.0, saturation, lightness_bottom, alpha_bottom)); + + ctx.set_fill_style(&JsValue::from(gradient)); + ctx.fill(); } } @@ -240,47 +201,24 @@ fn setup_particle_canvas(document: &Document, body: &HtmlElement, window: &Windo .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(600); - console::log_1(&format!("🌟 Spawned {} initial particles", particle_system.borrow().particles.len()).into()); + // Initialize fluid wave system + let wave_system = Rc::new(RefCell::new(FluidWaves::new(width, height))); + console::log_1(&"🌊 Fluid wave system initialized!".into()); // Setup animation loop - let system_clone = particle_system.clone(); + let system_clone = wave_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 || { - *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)"); + // Clear canvas + context_clone.set_fill_style_str("rgba(10, 14, 23, 1.0)"); context_clone.fill_rect(0.0, 0.0, width, height); - // Update and draw particles + // Update and draw waves system_clone.borrow_mut().update(); system_clone.borrow().draw(&context_clone); @@ -299,12 +237,12 @@ fn setup_particle_canvas(document: &Document, body: &HtmlElement, window: &Windo // Setup resize handler let canvas_clone = canvas.clone(); - let system_resize = particle_system.clone(); - let window_clone = window.clone(); + let system_resize = wave_system.clone(); + let window_resize = 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); + let new_width = window_resize.inner_width().unwrap().as_f64().unwrap_or(800.0); + let new_height = window_resize.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);