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

- 姓名
- 全能波
- GitHub
- @weicracker
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 =
``;
} catch (error) {
document.getElementById('file-result').innerHTML = ``;
}
}
};
// 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 =
``;
} catch (error) {
document.getElementById('http-result').innerHTML = ``;
}
};
run();
</script>
</body>
</html>
总结
Rust-Wasm高级Web应用开发的核心要点:
🎯 高级特性
- wasm-bindgen高级绑定:复杂数据类型和异步操作
- Canvas 2D:图形渲染、动画和图像处理
- WebGL集成:3D渲染和GPU加速计算
- 异步编程:Promise和Future的无缝集成
✅ 实用技术
- 事件处理和用户交互
- 文件上传和处理
- HTTP客户端和网络通信
- 动画系统和性能优化
🚀 应用场景
- 数据可视化应用
- 图像和视频处理
- 游戏和交互式应用
- 科学计算和仿真
💡 最佳实践
- 合理的架构设计
- 性能监控和优化
- 错误处理和用户体验
- 跨浏览器兼容性
掌握高级特性,构建现代化的高性能Web应用!
Rust-Wasm为Web应用开发带来了新的可能性,通过合理运用高级特性,可以构建出性能卓越的现代Web应用。