发布于

Rust-Wasm高级Web应用开发:构建现代化的高性能Web应用

作者

Rust-Wasm高级Web应用开发:构建现代化的高性能Web应用

随着WebAssembly技术的成熟,Rust与Web平台的结合为开发者提供了构建高性能Web应用的新途径。本文将探讨高级Web应用开发的各个方面。

wasm-bindgen高级特性

项目配置和依赖

# Cargo.toml - 高级Web应用配置
[package]
name = "advanced-web-app"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
web-sys = "0.3"
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"
getrandom = { version = "0.2", features = ["js"] }
console_error_panic_hook = "0.1"
wee_alloc = "0.4"

[dependencies.web-sys]
version = "0.3"
features = [
  "console",
  "Document",
  "Element",
  "HtmlElement",
  "HtmlCanvasElement",
  "CanvasRenderingContext2d",
  "WebGlRenderingContext",
  "WebGl2RenderingContext",
  "WebGlProgram",
  "WebGlShader",
  "WebGlBuffer",
  "ImageData",
  "Window",
  "Performance",
  "Request",
  "RequestInit",
  "Response",
  "Headers",
  "Blob",
  "File",
  "FileReader",
  "Event",
  "EventTarget",
  "MouseEvent",
  "KeyboardEvent",
  "AnimationFrame",
]

高级绑定技术

// src/lib.rs - 高级绑定和Web API集成
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use web_sys::*;
use js_sys::*;
use serde::{Serialize, Deserialize};

// 全局设置
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen(start)]
pub fn main() {
    console_error_panic_hook::set_once();
}

// 高级事件处理
#[wasm_bindgen]
pub struct EventManager {
    element: HtmlElement,
    callbacks: Vec<Closure<dyn FnMut(Event)>>,
}

#[wasm_bindgen]
impl EventManager {
    #[wasm_bindgen(constructor)]
    pub fn new(element: HtmlElement) -> EventManager {
        EventManager {
            element,
            callbacks: Vec::new(),
        }
    }
    
    #[wasm_bindgen]
    pub fn add_click_listener(&mut self, callback: &js_sys::Function) {
        let callback_clone = callback.clone();
        let closure = Closure::wrap(Box::new(move |event: Event| {
            let _ = callback_clone.call1(&JsValue::NULL, &event);
        }) as Box<dyn FnMut(Event)>);
        
        self.element
            .add_event_listener_with_callback("click", closure.as_ref().unchecked_ref())
            .unwrap();
        
        self.callbacks.push(closure);
    }
    
    #[wasm_bindgen]
    pub fn add_mouse_move_listener(&mut self, callback: &js_sys::Function) {
        let callback_clone = callback.clone();
        let closure = Closure::wrap(Box::new(move |event: Event| {
            if let Ok(mouse_event) = event.dyn_into::<MouseEvent>() {
                let data = js_sys::Object::new();
                js_sys::Reflect::set(&data, &"x".into(), &mouse_event.client_x().into()).unwrap();
                js_sys::Reflect::set(&data, &"y".into(), &mouse_event.client_y().into()).unwrap();
                let _ = callback_clone.call1(&JsValue::NULL, &data);
            }
        }) as Box<dyn FnMut(Event)>);
        
        self.element
            .add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref())
            .unwrap();
        
        self.callbacks.push(closure);
    }
}

// 异步HTTP客户端
#[wasm_bindgen]
pub struct HttpClient;

#[wasm_bindgen]
impl HttpClient {
    #[wasm_bindgen(constructor)]
    pub fn new() -> HttpClient {
        HttpClient
    }
    
    #[wasm_bindgen]
    pub async fn get(&self, url: &str) -> Result<JsValue, JsValue> {
        let window = web_sys::window().unwrap();
        let request = Request::new_with_str(url)?;
        
        let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
        let resp: Response = resp_value.dyn_into()?;
        
        let text = JsFuture::from(resp.text()?).await?;
        Ok(text)
    }
    
    #[wasm_bindgen]
    pub async fn post(&self, url: &str, data: &JsValue) -> Result<JsValue, JsValue> {
        let window = web_sys::window().unwrap();
        
        let mut opts = RequestInit::new();
        opts.method("POST");
        opts.body(Some(data));
        
        let request = Request::new_with_str_and_init(url, &opts)?;
        request.headers().set("Content-Type", "application/json")?;
        
        let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
        let resp: Response = resp_value.dyn_into()?;
        
        let text = JsFuture::from(resp.text()?).await?;
        Ok(text)
    }
}

// 文件处理
#[wasm_bindgen]
pub struct FileProcessor;

#[wasm_bindgen]
impl FileProcessor {
    #[wasm_bindgen(constructor)]
    pub fn new() -> FileProcessor {
        FileProcessor
    }
    
    #[wasm_bindgen]
    pub async fn read_file_as_text(&self, file: &File) -> Result<String, JsValue> {
        let file_reader = FileReader::new()?;
        
        let promise = js_sys::Promise::new(&mut |resolve, reject| {
            let reader_clone = file_reader.clone();
            let onload = Closure::wrap(Box::new(move |_: Event| {
                if let Ok(result) = reader_clone.result() {
                    resolve.call1(&JsValue::NULL, &result).unwrap();
                }
            }) as Box<dyn FnMut(Event)>);
            
            let onerror = Closure::wrap(Box::new(move |_: Event| {
                reject.call1(&JsValue::NULL, &"File read error".into()).unwrap();
            }) as Box<dyn FnMut(Event)>);
            
            file_reader.set_onload(Some(onload.as_ref().unchecked_ref()));
            file_reader.set_onerror(Some(onerror.as_ref().unchecked_ref()));
            
            onload.forget();
            onerror.forget();
        });
        
        file_reader.read_as_text(file)?;
        let result = JsFuture::from(promise).await?;
        
        Ok(result.as_string().unwrap_or_default())
    }
    
    #[wasm_bindgen]
    pub async fn read_file_as_array_buffer(&self, file: &File) -> Result<js_sys::ArrayBuffer, JsValue> {
        let file_reader = FileReader::new()?;
        
        let promise = js_sys::Promise::new(&mut |resolve, reject| {
            let reader_clone = file_reader.clone();
            let onload = Closure::wrap(Box::new(move |_: Event| {
                if let Ok(result) = reader_clone.result() {
                    resolve.call1(&JsValue::NULL, &result).unwrap();
                }
            }) as Box<dyn FnMut(Event)>);
            
            let onerror = Closure::wrap(Box::new(move |_: Event| {
                reject.call1(&JsValue::NULL, &"File read error".into()).unwrap();
            }) as Box<dyn FnMut(Event)>);
            
            file_reader.set_onload(Some(onload.as_ref().unchecked_ref()));
            file_reader.set_onerror(Some(onerror.as_ref().unchecked_ref()));
            
            onload.forget();
            onerror.forget();
        });
        
        file_reader.read_as_array_buffer(file)?;
        let result = JsFuture::from(promise).await?;
        
        Ok(result.dyn_into()?)
    }
}

Canvas 2D 图形处理

// src/canvas2d.rs - Canvas 2D 高级操作
use wasm_bindgen::prelude::*;
use web_sys::*;
use js_sys::*;

#[wasm_bindgen]
pub struct Canvas2DRenderer {
    canvas: HtmlCanvasElement,
    context: CanvasRenderingContext2d,
    width: u32,
    height: u32,
}

#[wasm_bindgen]
impl Canvas2DRenderer {
    #[wasm_bindgen(constructor)]
    pub fn new(canvas_id: &str) -> Result<Canvas2DRenderer, JsValue> {
        let document = web_sys::window().unwrap().document().unwrap();
        let canvas = document
            .get_element_by_id(canvas_id)
            .unwrap()
            .dyn_into::<HtmlCanvasElement>()?;
        
        let context = canvas
            .get_context("2d")?
            .unwrap()
            .dyn_into::<CanvasRenderingContext2d>()?;
        
        let width = canvas.width();
        let height = canvas.height();
        
        Ok(Canvas2DRenderer {
            canvas,
            context,
            width,
            height,
        })
    }
    
    #[wasm_bindgen]
    pub fn clear(&self) {
        self.context.clear_rect(0.0, 0.0, self.width as f64, self.height as f64);
    }
    
    #[wasm_bindgen]
    pub fn draw_circle(&self, x: f64, y: f64, radius: f64, color: &str) {
        self.context.begin_path();
        self.context.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI).unwrap();
        self.context.set_fill_style(&color.into());
        self.context.fill();
    }
    
    #[wasm_bindgen]
    pub fn draw_line(&self, x1: f64, y1: f64, x2: f64, y2: f64, color: &str, width: f64) {
        self.context.begin_path();
        self.context.move_to(x1, y1);
        self.context.line_to(x2, y2);
        self.context.set_stroke_style(&color.into());
        self.context.set_line_width(width);
        self.context.stroke();
    }
    
    #[wasm_bindgen]
    pub fn draw_text(&self, text: &str, x: f64, y: f64, font: &str, color: &str) {
        self.context.set_font(font);
        self.context.set_fill_style(&color.into());
        self.context.fill_text(text, x, y).unwrap();
    }
    
    // 粒子系统
    #[wasm_bindgen]
    pub fn draw_particles(&self, particles: &[f64]) {
        // particles数组格式: [x1, y1, vx1, vy1, life1, x2, y2, vx2, vy2, life2, ...]
        for chunk in particles.chunks(5) {
            if chunk.len() == 5 {
                let x = chunk[0];
                let y = chunk[1];
                let life = chunk[4];
                
                if life > 0.0 {
                    let alpha = life / 100.0;
                    let color = format!("rgba(255, 255, 255, {})", alpha);
                    self.draw_circle(x, y, 2.0, &color);
                }
            }
        }
    }
    
    // 图像处理
    #[wasm_bindgen]
    pub fn apply_filter(&self, filter_type: &str) -> Result<(), JsValue> {
        let image_data = self.context.get_image_data(0.0, 0.0, self.width as f64, self.height as f64)?;
        let mut data = image_data.data();
        
        match filter_type {
            "grayscale" => {
                for i in (0..data.length()).step_by(4) {
                    let r = data.get_index(i) as f64;
                    let g = data.get_index(i + 1) as f64;
                    let b = data.get_index(i + 2) as f64;
                    let gray = (0.299 * r + 0.587 * g + 0.114 * b) as u8;
                    
                    data.set_index(i, gray);
                    data.set_index(i + 1, gray);
                    data.set_index(i + 2, gray);
                }
            },
            "invert" => {
                for i in (0..data.length()).step_by(4) {
                    data.set_index(i, 255 - data.get_index(i));
                    data.set_index(i + 1, 255 - data.get_index(i + 1));
                    data.set_index(i + 2, 255 - data.get_index(i + 2));
                }
            },
            "sepia" => {
                for i in (0..data.length()).step_by(4) {
                    let r = data.get_index(i) as f64;
                    let g = data.get_index(i + 1) as f64;
                    let b = data.get_index(i + 2) as f64;
                    
                    let new_r = ((r * 0.393) + (g * 0.769) + (b * 0.189)).min(255.0) as u8;
                    let new_g = ((r * 0.349) + (g * 0.686) + (b * 0.168)).min(255.0) as u8;
                    let new_b = ((r * 0.272) + (g * 0.534) + (b * 0.131)).min(255.0) as u8;
                    
                    data.set_index(i, new_r);
                    data.set_index(i + 1, new_g);
                    data.set_index(i + 2, new_b);
                }
            },
            _ => return Err("Unknown filter type".into()),
        }
        
        self.context.put_image_data(&image_data, 0.0, 0.0)?;
        Ok(())
    }
}

// 动画系统
#[wasm_bindgen]
pub struct AnimationSystem {
    renderer: Canvas2DRenderer,
    animation_id: Option<i32>,
    last_time: f64,
}

#[wasm_bindgen]
impl AnimationSystem {
    #[wasm_bindgen(constructor)]
    pub fn new(canvas_id: &str) -> Result<AnimationSystem, JsValue> {
        let renderer = Canvas2DRenderer::new(canvas_id)?;
        
        Ok(AnimationSystem {
            renderer,
            animation_id: None,
            last_time: 0.0,
        })
    }
    
    #[wasm_bindgen]
    pub fn start_animation(&mut self, callback: &js_sys::Function) {
        let callback_clone = callback.clone();
        let window = web_sys::window().unwrap();
        
        let animate_closure = Rc::new(RefCell::new(None));
        let animate_clone = animate_closure.clone();
        
        *animate_clone.borrow_mut() = Some(Closure::wrap(Box::new(move |time: f64| {
            let delta = time - self.last_time;
            self.last_time = time;
            
            // 调用JavaScript回调
            let _ = callback_clone.call2(&JsValue::NULL, &time.into(), &delta.into());
            
            // 请求下一帧
            if let Some(closure) = animate_closure.borrow().as_ref() {
                let id = window.request_animation_frame(closure.as_ref().unchecked_ref()).unwrap();
                self.animation_id = Some(id);
            }
        }) as Box<dyn FnMut(f64)>));
        
        // 开始动画
        if let Some(closure) = animate_clone.borrow().as_ref() {
            let id = window.request_animation_frame(closure.as_ref().unchecked_ref()).unwrap();
            self.animation_id = Some(id);
        }
    }
    
    #[wasm_bindgen]
    pub fn stop_animation(&mut self) {
        if let Some(id) = self.animation_id.take() {
            web_sys::window().unwrap().cancel_animation_frame(id).unwrap();
        }
    }
    
    #[wasm_bindgen]
    pub fn get_renderer(&self) -> Canvas2DRenderer {
        // 注意:这里需要克隆或使用引用计数
        // 为了简化示例,这里返回一个新的实例
        Canvas2DRenderer::new("canvas").unwrap()
    }
}

WebGL 3D 渲染

// src/webgl.rs - WebGL 高级渲染
use wasm_bindgen::prelude::*;
use web_sys::*;
use js_sys::*;

#[wasm_bindgen]
pub struct WebGLRenderer {
    canvas: HtmlCanvasElement,
    gl: WebGlRenderingContext,
    program: Option<WebGlProgram>,
}

#[wasm_bindgen]
impl WebGLRenderer {
    #[wasm_bindgen(constructor)]
    pub fn new(canvas_id: &str) -> Result<WebGLRenderer, JsValue> {
        let document = web_sys::window().unwrap().document().unwrap();
        let canvas = document
            .get_element_by_id(canvas_id)
            .unwrap()
            .dyn_into::<HtmlCanvasElement>()?;
        
        let gl = canvas
            .get_context("webgl")?
            .unwrap()
            .dyn_into::<WebGlRenderingContext>()?;
        
        Ok(WebGLRenderer {
            canvas,
            gl,
            program: None,
        })
    }
    
    #[wasm_bindgen]
    pub fn create_shader_program(&mut self, vertex_source: &str, fragment_source: &str) -> Result<(), JsValue> {
        let vertex_shader = self.compile_shader(WebGlRenderingContext::VERTEX_SHADER, vertex_source)?;
        let fragment_shader = self.compile_shader(WebGlRenderingContext::FRAGMENT_SHADER, fragment_source)?;
        
        let program = self.gl.create_program().ok_or("Failed to create program")?;
        
        self.gl.attach_shader(&program, &vertex_shader);
        self.gl.attach_shader(&program, &fragment_shader);
        self.gl.link_program(&program);
        
        if !self.gl.get_program_parameter(&program, WebGlRenderingContext::LINK_STATUS).as_bool().unwrap_or(false) {
            let info = self.gl.get_program_info_log(&program).unwrap_or_default();
            return Err(format!("Failed to link program: {}", info).into());
        }
        
        self.program = Some(program);
        Ok(())
    }
    
    fn compile_shader(&self, shader_type: u32, source: &str) -> Result<WebGlShader, JsValue> {
        let shader = self.gl.create_shader(shader_type).ok_or("Failed to create shader")?;
        
        self.gl.shader_source(&shader, source);
        self.gl.compile_shader(&shader);
        
        if !self.gl.get_shader_parameter(&shader, WebGlRenderingContext::COMPILE_STATUS).as_bool().unwrap_or(false) {
            let info = self.gl.get_shader_info_log(&shader).unwrap_or_default();
            return Err(format!("Failed to compile shader: {}", info).into());
        }
        
        Ok(shader)
    }
    
    #[wasm_bindgen]
    pub fn create_buffer(&self, data: &[f32]) -> Result<WebGlBuffer, JsValue> {
        let buffer = self.gl.create_buffer().ok_or("Failed to create buffer")?;
        
        self.gl.bind_buffer(WebGlRenderingContext::ARRAY_BUFFER, Some(&buffer));
        
        // 将Rust数组转换为JavaScript Float32Array
        unsafe {
            let array = js_sys::Float32Array::view(data);
            self.gl.buffer_data_with_array_buffer_view(
                WebGlRenderingContext::ARRAY_BUFFER,
                &array,
                WebGlRenderingContext::STATIC_DRAW,
            );
        }
        
        Ok(buffer)
    }
    
    #[wasm_bindgen]
    pub fn draw_triangle(&self) -> Result<(), JsValue> {
        if let Some(program) = &self.program {
            self.gl.use_program(Some(program));
            
            // 三角形顶点数据
            let vertices: [f32; 9] = [
                -0.5, -0.5, 0.0,
                 0.5, -0.5, 0.0,
                 0.0,  0.5, 0.0,
            ];
            
            let buffer = self.create_buffer(&vertices)?;
            
            // 获取属性位置
            let position_location = self.gl.get_attrib_location(program, "a_position") as u32;
            
            self.gl.enable_vertex_attrib_array(position_location);
            self.gl.bind_buffer(WebGlRenderingContext::ARRAY_BUFFER, Some(&buffer));
            self.gl.vertex_attrib_pointer_with_i32(
                position_location,
                3,
                WebGlRenderingContext::FLOAT,
                false,
                0,
                0,
            );
            
            // 清除画布
            self.gl.clear_color(0.0, 0.0, 0.0, 1.0);
            self.gl.clear(WebGlRenderingContext::COLOR_BUFFER_BIT);
            
            // 绘制三角形
            self.gl.draw_arrays(WebGlRenderingContext::TRIANGLES, 0, 3);
        }
        
        Ok(())
    }
    
    #[wasm_bindgen]
    pub fn set_viewport(&self, width: i32, height: i32) {
        self.gl.viewport(0, 0, width, height);
    }
    
    #[wasm_bindgen]
    pub fn set_uniform_matrix4fv(&self, name: &str, matrix: &[f32]) -> Result<(), JsValue> {
        if let Some(program) = &self.program {
            let location = self.gl.get_uniform_location(program, name);
            if let Some(loc) = location {
                unsafe {
                    let array = js_sys::Float32Array::view(matrix);
                    self.gl.uniform_matrix4fv_with_f32_array(Some(&loc), false, &array);
                }
            }
        }
        Ok(())
    }
}

// 3D数学库
#[wasm_bindgen]
pub struct Matrix4 {
    data: [f32; 16],
}

#[wasm_bindgen]
impl Matrix4 {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Matrix4 {
        Matrix4 {
            data: [
                1.0, 0.0, 0.0, 0.0,
                0.0, 1.0, 0.0, 0.0,
                0.0, 0.0, 1.0, 0.0,
                0.0, 0.0, 0.0, 1.0,
            ],
        }
    }
    
    #[wasm_bindgen]
    pub fn perspective(fov: f32, aspect: f32, near: f32, far: f32) -> Matrix4 {
        let f = 1.0 / (fov / 2.0).tan();
        let range_inv = 1.0 / (near - far);
        
        Matrix4 {
            data: [
                f / aspect, 0.0, 0.0, 0.0,
                0.0, f, 0.0, 0.0,
                0.0, 0.0, (near + far) * range_inv, -1.0,
                0.0, 0.0, near * far * range_inv * 2.0, 0.0,
            ],
        }
    }
    
    #[wasm_bindgen]
    pub fn translate(&mut self, x: f32, y: f32, z: f32) {
        self.data[12] += x;
        self.data[13] += y;
        self.data[14] += z;
    }
    
    #[wasm_bindgen]
    pub fn rotate_y(&mut self, angle: f32) {
        let cos = angle.cos();
        let sin = angle.sin();
        
        let m00 = self.data[0];
        let m02 = self.data[2];
        let m20 = self.data[8];
        let m22 = self.data[10];
        
        self.data[0] = cos * m00 + sin * m20;
        self.data[2] = cos * m02 + sin * m22;
        self.data[8] = cos * m20 - sin * m00;
        self.data[10] = cos * m22 - sin * m02;
    }
    
    #[wasm_bindgen]
    pub fn get_data(&self) -> Vec<f32> {
        self.data.to_vec()
    }
}

实际应用示例

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Advanced Rust-Wasm Web App</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
        .app-container { display: flex; gap: 20px; }
        .canvas-container { flex: 1; }
        .controls { width: 300px; }
        canvas { border: 1px solid #ccc; display: block; margin: 10px 0; }
        button { padding: 10px; margin: 5px; }
        .section { margin: 20px 0; padding: 15px; border: 1px solid #ddd; }
    </style>
</head>
<body>
    <h1>Advanced Rust-Wasm Web Application</h1>
    
    <div class="app-container">
        <div class="canvas-container">
            <h2>Canvas 2D Demo</h2>
            <canvas id="canvas2d" width="600" height="400"></canvas>
            
            <h2>WebGL Demo</h2>
            <canvas id="webgl-canvas" width="600" height="400"></canvas>
        </div>
        
        <div class="controls">
            <div class="section">
                <h3>Canvas 2D Controls</h3>
                <button onclick="clearCanvas()">Clear</button>
                <button onclick="drawShapes()">Draw Shapes</button>
                <button onclick="startParticles()">Start Particles</button>
                <button onclick="applyFilter('grayscale')">Grayscale</button>
                <button onclick="applyFilter('sepia')">Sepia</button>
            </div>
            
            <div class="section">
                <h3>WebGL Controls</h3>
                <button onclick="initWebGL()">Initialize WebGL</button>
                <button onclick="drawTriangle()">Draw Triangle</button>
                <button onclick="startRotation()">Start Rotation</button>
            </div>
            
            <div class="section">
                <h3>File Upload</h3>
                <input type="file" id="file-input" accept="image/*">
                <button onclick="processFile()">Process File</button>
                <div id="file-result"></div>
            </div>
            
            <div class="section">
                <h3>HTTP Demo</h3>
                <button onclick="fetchData()">Fetch Data</button>
                <div id="http-result"></div>
            </div>
        </div>
    </div>

    <script type="module">
        import init, { 
            Canvas2DRenderer,
            WebGLRenderer,
            Matrix4,
            EventManager,
            HttpClient,
            FileProcessor,
            AnimationSystem
        } from './pkg/advanced_web_app.js';

        let canvas2d, webglRenderer, httpClient, fileProcessor;
        let animationSystem;
        let particles = [];

        async function run() {
            await init();
            
            // 初始化组件
            canvas2d = new Canvas2DRenderer('canvas2d');
            webglRenderer = new WebGLRenderer('webgl-canvas');
            httpClient = new HttpClient();
            fileProcessor = new FileProcessor();
            animationSystem = new AnimationSystem('canvas2d');
            
            // 设置事件监听
            setupEventListeners();
            
            console.log('Advanced web app loaded!');
        }

        function setupEventListeners() {
            const canvas = document.getElementById('canvas2d');
            const eventManager = new EventManager(canvas);
            
            eventManager.add_click_listener((event) => {
                console.log('Canvas clicked!', event);
            });
            
            eventManager.add_mouse_move_listener((data) => {
                // 可以在这里添加鼠标跟踪效果
            });
        }

        // Canvas 2D 功能
        window.clearCanvas = function() {
            canvas2d.clear();
        };

        window.drawShapes = function() {
            canvas2d.clear();
            
            // 绘制各种形状
            canvas2d.draw_circle(100, 100, 50, '#ff0000');
            canvas2d.draw_circle(200, 100, 30, '#00ff00');
            canvas2d.draw_line(50, 200, 250, 200, '#0000ff', 5);
            canvas2d.draw_text('Hello Rust-Wasm!', 50, 250, '20px Arial', '#333333');
        };

        window.startParticles = function() {
            // 初始化粒子
            particles = [];
            for (let i = 0; i < 100; i++) {
                particles.push(
                    Math.random() * 600, // x
                    Math.random() * 400, // y
                    (Math.random() - 0.5) * 4, // vx
                    (Math.random() - 0.5) * 4, // vy
                    100 // life
                );
            }
            
            // 开始动画
            animationSystem.start_animation((time, delta) => {
                updateParticles(delta);
                renderParticles();
            });
        };

        function updateParticles(delta) {
            for (let i = 0; i < particles.length; i += 5) {
                particles[i] += particles[i + 2]; // x += vx
                particles[i + 1] += particles[i + 3]; // y += vy
                particles[i + 4] -= 1; // life--
                
                // 边界检查
                if (particles[i] < 0 || particles[i] > 600) particles[i + 2] *= -1;
                if (particles[i + 1] < 0 || particles[i + 1] > 400) particles[i + 3] *= -1;
                
                // 重生粒子
                if (particles[i + 4] <= 0) {
                    particles[i] = Math.random() * 600;
                    particles[i + 1] = Math.random() * 400;
                    particles[i + 4] = 100;
                }
            }
        }

        function renderParticles() {
            canvas2d.clear();
            canvas2d.draw_particles(particles);
        }

        window.applyFilter = function(filterType) {
            canvas2d.apply_filter(filterType);
        };

        // WebGL 功能
        window.initWebGL = function() {
            const vertexShader = `
                attribute vec4 a_position;
                uniform mat4 u_matrix;
                void main() {
                    gl_Position = u_matrix * a_position;
                }
            `;
            
            const fragmentShader = `
                precision mediump float;
                void main() {
                    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
                }
            `;
            
            webglRenderer.create_shader_program(vertexShader, fragmentShader);
            webglRenderer.set_viewport(600, 400);
        };

        window.drawTriangle = function() {
            webglRenderer.draw_triangle();
        };

        window.startRotation = function() {
            let rotation = 0;
            
            function animate() {
                rotation += 0.02;
                
                const matrix = Matrix4.perspective(Math.PI / 4, 600 / 400, 0.1, 100);
                matrix.translate(0, 0, -3);
                matrix.rotate_y(rotation);
                
                webglRenderer.set_uniform_matrix4fv('u_matrix', matrix.get_data());
                webglRenderer.draw_triangle();
                
                requestAnimationFrame(animate);
            }
            
            animate();
        };

        // 文件处理
        window.processFile = async function() {
            const fileInput = document.getElementById('file-input');
            const file = fileInput.files[0];
            
            if (file) {
                try {
                    const text = await fileProcessor.read_file_as_text(file);
                    document.getElementById('file-result').innerHTML = 
                        `File processed: ${file.name}<br>Size: ${file.size} bytes<br>Content preview: ${text.substring(0, 100)}...`;
                } catch (error) {
                    document.getElementById('file-result').innerHTML = `Error: ${error}`;
                }
            }
        };

        // HTTP 请求
        window.fetchData = async function() {
            try {
                const response = await httpClient.get('https://jsonplaceholder.typicode.com/posts/1');
                const data = JSON.parse(response);
                document.getElementById('http-result').innerHTML = 
                    `<strong>Fetched data:</strong><br>Title: ${data.title}<br>Body: ${data.body.substring(0, 100)}...`;
            } catch (error) {
                document.getElementById('http-result').innerHTML = `Error: ${error}`;
            }
        };

        run();
    </script>
</body>
</html>

总结

Rust-Wasm高级Web应用开发的核心要点:

🎯 高级特性

  1. wasm-bindgen高级绑定:复杂数据类型和异步操作
  2. Canvas 2D:图形渲染、动画和图像处理
  3. WebGL集成:3D渲染和GPU加速计算
  4. 异步编程:Promise和Future的无缝集成

✅ 实用技术

  • 事件处理和用户交互
  • 文件上传和处理
  • HTTP客户端和网络通信
  • 动画系统和性能优化

🚀 应用场景

  • 数据可视化应用
  • 图像和视频处理
  • 游戏和交互式应用
  • 科学计算和仿真

💡 最佳实践

  • 合理的架构设计
  • 性能监控和优化
  • 错误处理和用户体验
  • 跨浏览器兼容性

掌握高级特性,构建现代化的高性能Web应用!


Rust-Wasm为Web应用开发带来了新的可能性,通过合理运用高级特性,可以构建出性能卓越的现代Web应用。