
開發(fā)工具【免費下載鏈接】wasm-bindgenFacilitating high-level interactions between Wasm modules and JavaScript項目地址https://gitcode.com/gh_mirrors/wa/wasm-bindgen點擊查看免費下載Paint 是 wasm-bindgen 倉庫中一個典型的DOM 交互型示例它用 Rust 直接創(chuàng)建canvas元素、獲取 2D 繪圖上下文、并通過 JavaScript 閉包監(jiān)聽鼠標事件最終在瀏覽器中實現(xiàn)一個可拖拽畫線的迷你畫板程序。本文以 examples/paint/README.md 為骨架結(jié)合 src/lib.rs、Cargo.toml 與 webpack.config.js 等倉庫源碼帶你完整走通從構(gòu)建、運行到逐行理解繪圖邏輯的實戰(zhàn)流程讀完即可照搬這套wasm-bindgen web-sys 事件閉包的模式去開發(fā)自己的瀏覽器圖形應(yīng)用。示例概覽一個用 Rust 寫成的畫板Paint 示例的核心目標非常樸素在瀏覽器頁面中創(chuàng)建一個 640x480 的canvas畫布用戶按下鼠標開始落筆拖動鼠標繪制路徑松開鼠標結(jié)束一筆。整個過程完全由 Rust 代碼驅(qū)動——DOM 操作、CSS 樣式、Canvas 2D API 和鼠標事件監(jiān)聽都通過web-sys綁定實現(xiàn)#[wasm_bindgen(start)]入口在 Wasm 模塊加載后自動執(zhí)行。官方對該示例的定位在 guide/src/examples/paint.md 中有明確描述A simple painting program并指出其Cargo.toml啟用了操作 DOM、事件和 2D canvas 所必需的特性。也就是說這個示例是理解Rust 如何直接接管瀏覽器繪圖與交互的最佳入門素材之一。構(gòu)建與運行一條命令起服務(wù)倉庫內(nèi) examples/paint/README.md 給出了本地運行的全部操作步驟全文核心只有兩條信息用npm run serve構(gòu)建并啟動開發(fā)服務(wù)器然后在瀏覽器訪問http://localhost:8080即可看到效果$ npm run serve這條命令的實際執(zhí)行鏈是 package.json 中預(yù)定義的腳本{ scripts: { build: webpack, serve: webpack serve } }也就是說npm run serve本質(zhì)上調(diào)用的是webpack servewebpack-dev-server。它由倉庫根目錄的 pnpm workspace見 pnpm-workspace.yaml統(tǒng)一管理依賴版本package.json中的catalog:即工作區(qū)目錄引用catalog protocol聲明的開發(fā)依賴包括wasm-tool/wasm-pack-plugin在 webpack 構(gòu)建流程中自動調(diào)用 wasm-pack 編譯 Rust cratehtml-webpack-plugin以index.html為模板生成 HTMLwebpack/webpack-cli/webpack-dev-server構(gòu)建與開發(fā)服務(wù)器本體。在運行之前建議先執(zhí)行一次npm install安裝依賴倉庫采用 pnpm workspace若使用 pnpm 則為pnpm install由于該示例依賴wasm-bindgen、web-sys、js-sys的本地源碼版本見下文Cargo.toml的path依賴無需發(fā)布到 crates.io 即可直接編譯這也是倉庫內(nèi)示例通用的做法。Cargo.toml為 DOM 與 Canvas 精準開啟 web-sys 特性examples/paint/Cargo.toml 是整個示例的 Rust 側(cè)配置幾個要點逐一看[package] authors [The wasm-bindgen Developers] edition 2021 name wasm-bindgen-paint publish false version 0.0.0 [lib] crate-type [cdylib]crate-type [cdylib]這是 wasm 最終產(chǎn)物最常用的 crate 類型wasm-bindgen 官方文檔guide/src/examples/hello-world.md專門強調(diào)過largely used for wasm final artifacts today即把 Rust 庫編譯成可以被 JS 側(cè)直接import的 Wasm 模塊publish false示例不發(fā)布到 crates.io僅供倉庫內(nèi)使用。依賴部分值得細讀它充分體現(xiàn)了 wasm-bindgen 生態(tài)的三件套[dependencies] js-sys { path ../../crates/js-sys } wasm-bindgen { path ../../ } [dependencies.web-sys] features [ CanvasRenderingContext2d, CssStyleDeclaration, Document, Element, EventTarget, HtmlCanvasElement, HtmlElement, MouseEvent, Node, Window, ] path ../../crates/web-syswasm-bindgen提供核心宏與運行時#[wasm_bindgen]、Closure等對應(yīng)倉庫根目錄的src/js-sys提供 ECMAScript 標準 API 的綁定對應(yīng) crates/js-sysweb-sys提供 Web APIDOM、Canvas、事件等的綁定對應(yīng) crates/web-sys且采用 feature 門控——只有顯式開啟的特性才會被編譯這正是上方features列表的意義。對照 src/lib.rs 的代碼逐行看每個特性都有真實用途web-sys 特性對應(yīng)代碼用途W(wǎng)indow/Document/Node/Elementweb_sys::window()、document()、create_element、append_childHtmlCanvasElementdyn_into::HtmlCanvasElement()類型轉(zhuǎn)換CanvasRenderingContext2dget_context(2d)與begin_path/move_to/line_to/strokeCssStyleDeclarationcanvas.style().set_property(border, solid)EventTarget/MouseEventadd_event_listener_with_callback與鼠標事件對象這種按需開啟特性的設(shè)計是 web-sys 的核心使用模式不要盲目開啟全部特性而是對照代碼逐項列出可顯著減小最終 Wasm 體積。源碼逐段拆解start 入口到事件閉包examples/paint/src/lib.rs 共 57 行完整實現(xiàn)了整個畫板邏輯。下面按代碼執(zhí)行順序拆解每一段。1. 入口#[wasm_bindgen(start)]use std::cell::Cell; use std::rc::Rc; use wasm_bindgen::prelude::*; #[wasm_bindgen(start)] fn start() - Result(), JsValue {#[wasm_bindgen(start)]是 wasm-bindgen 提供的一個特殊入口屬性被標記的函數(shù)會在 Wasm 模塊實例化完成后自動執(zhí)行無需 JS 側(cè)顯式調(diào)用。返回Result(), JsValue允許把 DOM 操作中的錯誤直接以JsValue形式向上拋出。注意這里提前引入了Rc和Cell——它們將在閉包共享狀態(tài)環(huán)節(jié)發(fā)揮關(guān)鍵作用。2. 創(chuàng)建畫布、設(shè)置樣式并掛載let document web_sys::window().unwrap().document().unwrap(); let canvas document .create_element(canvas)? .dyn_into::web_sys::HtmlCanvasElement()?; document.body().unwrap().append_child(canvas)?; canvas.set_width(640); canvas.set_height(480); canvas.style().set_property(border, solid)?;這段代碼通過web_sys依次完成取window→ 取document→create_element(canvas)創(chuàng)建元素 → 用dyn_into將泛型Element向下轉(zhuǎn)型為HtmlCanvasElement→ 追加到body→ 設(shè)置 640x480 尺寸 → 通過style()返回CssStyleDeclaration加邊框樣式。?運算符配合Result(), JsValue讓錯誤傳播非常簡潔dyn_into則是 wasm-bindgen 提供的 DOM 類型向下轉(zhuǎn)型 API用于把基類實例轉(zhuǎn)換為具體的子類類型。3. 獲取 2D 繪圖上下文并準備共享狀態(tài)let context canvas .get_context(2d)? .unwrap() .dyn_into::web_sys::CanvasRenderingContext2d()?; let context Rc::new(context); let pressed Rc::new(Cell::new(false));get_context(2d)返回Option先unwrap再dyn_into成CanvasRenderingContext2d。隨后是關(guān)鍵設(shè)計Rc讓多個閉包安全共享同一個 context 引用RcCellbool作為鼠標是否按下的共享標志位。之所以用Cell而非RefCell是因為這里只需要get/set一個布爾值Cell的開銷更小、語義更貼合之所以用Rc而非Arc是因為這些閉包都只在主線程的事件循環(huán)中運行不需要跨線程同步。4. mousedown落筆起點{ let context context.clone(); let pressed pressed.clone(); let closure Closure::dyn FnMut(_)::new(move |event: web_sys::MouseEvent| { context.begin_path(); context.move_to(event.offset_x() as f64, event.offset_y() as f64); pressed.set(true); }); canvas.add_event_listener_with_callback(mousedown, closure.as_ref().unchecked_ref())?; closure.forget(); }這是 wasm-bindgen 事件閉包的標準用法Closure::dyn FnMut(_)::new(...)把 Rust 閉包包裝成可傳給 JS 的函數(shù)指針其中_表示閉包參數(shù)類型由編譯器推斷add_event_listener_with_callback(mousedown, ...)注冊監(jiān)聽closure.as_ref().unchecked_ref()把閉包轉(zhuǎn)成 JS 可識別的函數(shù)引用closure.forget()至關(guān)重要默認情況下Closure是有生命周期的 Rust 對象作用域結(jié)束即被回收JS 側(cè)將得到懸空指針forget()放棄 Rust 側(cè)所有權(quán)讓該閉包常駐內(nèi)存、由 JS 側(cè)長期持有直至頁面卸載。這是 wasm-bindgen 官方文檔guide/src/reference 中 closures 相關(guān)章節(jié)反復強調(diào)的生命周期要點。mousedown回調(diào)中begin_path()開啟新路徑move_to將畫筆移動到鼠標當前坐標pressed.set(true)記錄按下狀態(tài)。offset_x/offset_y是相對事件目標畫布的坐標需轉(zhuǎn)為f64以匹配 Canvas API 參數(shù)。5. mousemove拖動繪制{ let context context.clone(); let pressed pressed.clone(); let closure Closure::dyn FnMut(_)::new(move |event: web_sys::MouseEvent| { if pressed.get() { context.line_to(event.offset_x() as f64, event.offset_y() as f64); context.stroke(); context.begin_path(); context.move_to(event.offset_x() as f64, event.offset_y() as f64); } }); canvas.add_event_listener_with_callback(mousemove, closure.as_ref().unchecked_ref())?; closure.forget(); }mousemove回調(diào)只在pressed.get()為 true鼠標處于按下狀態(tài)時才繪制line_to連接到新坐標、stroke()畫線隨后立即begin_path()move_to重新開一條路徑并把起點設(shè)到當前點。這個畫一筆就重開路徑的技巧避免了線段之間出現(xiàn)不需要的連接線是 Canvas 連續(xù)繪制最常見的工程手法。每個閉包內(nèi)部都通過context.clone()/pressed.clone()拿到自己的共享引用move關(guān)鍵字把克隆的引用移入閉包。6. mouseup收筆{ let closure Closure::dyn FnMut(_)::new(move |event: web_sys::MouseEvent| { pressed.set(false); context.line_to(event.offset_x() as f64, event.offset_y() as f64); context.stroke(); }); canvas.add_event_listener_with_callback(mouseup, closure.as_ref().unchecked_ref())?; closure.forget(); }mouseup時先把pressed復位為false防止松開后 mousemove 繼續(xù)畫再補上最后一筆line_tostroke保證按下→移動→松開這一整條路徑在松開瞬間完整閉合。最后函數(shù)返回Ok(())整個初始化過程結(jié)束。前端配套極簡 JS 入口與 webpack 管線index.js一條 import 啟動一切examples/paint/index.js 是整個 JS 側(cè)的全部代碼import(./pkg) .catch(console.error);動態(tài)import(./pkg)加載 wasm-pack 生成的pkg目錄產(chǎn)物加載后 Wasm 模塊實例化#[wasm_bindgen(start)]入口隨即自動執(zhí)行畫布便出現(xiàn)在頁面上catch(console.error)負責在加載失敗時輸出錯誤。index.html空殼模板examples/paint/index.html 只有html、head與空的body——因為畫布元素完全由 Rust 代碼動態(tài)創(chuàng)建并 append 到 bodyHTML 本身不需要任何靜態(tài)內(nèi)容。這也反襯出該示例一切 DOM 操作皆在 Rust的特性。webpack.config.jswasm-pack 插件與異步 Wasmexamples/paint/webpack.config.js 是構(gòu)建管線的關(guān)鍵module.exports { entry: ./index.js, output: { path: path.resolve(__dirname, .., dist, paint), filename: index.js, }, plugins: [ new HtmlWebpackPlugin({ template: index.html }), new WasmPackPlugin({ crateDirectory: __dirname }), ], mode: development, experiments: { asyncWebAssembly: true } };entry: ./index.jsJS 入口即上面那條動態(tài) importWasmPackPlugin({ crateDirectory: __dirname })告訴插件當前目錄即 examples/paint就是 Rust crate 所在目錄構(gòu)建時自動執(zhí)行 wasm-pack 編譯并產(chǎn)出pkgexperiments.asyncWebAssembly: true啟用 webpack 5 的異步 WebAssembly 支持讓import(./pkg)能按需異步加載 Wasm輸出目錄統(tǒng)一落在倉庫根dist/paint下。官方文檔的補充說明倉庫內(nèi) guide/src/examples/paint.md 是對本示例的官方文檔化描述要點與上文分析一一對應(yīng)對Cargo.toml的說明它啟用了操作 DOM、事件和 2D canvas 所必需的特性對src/lib.rs的說明創(chuàng)建canvas元素、應(yīng)用 CSS 樣式、加入文檔、獲取 2D 渲染上下文并添加鼠標事件監(jiān)聽器。本文對這兩點的展開特性清單與源碼用途對照、閉包生命周期、繪制路徑技巧即是對官方文檔的源碼級深化可作為閱讀官方文檔時的補充參考。小結(jié)與延伸方向Paint 示例雖小卻濃縮了 wasm-bindgen 在瀏覽器圖形/交互場景下的全部核心模式#[wasm_bindgen(start)]自動入口 Result(), JsValue錯誤傳播web-sys的 DOM 創(chuàng)建、類型向下轉(zhuǎn)型dyn_into與按需特性開啟Closure::dyn FnMut(_)unchecked_ref()forget()三段式事件監(jiān)聽以及Rc/Cell共享可變狀態(tài)的慣用法Canvas 連續(xù)繪制的重開路徑技巧。如果想要繼續(xù)深入倉庫內(nèi)還有大量可對照學習的示例理解閉包進階用法可看 guide/src/examples/closures.md 與 examples/closures想了解不使用打包器的純原生 ES 模塊方案可參考 guide/src/examples/without-a-bundler.md若對更復雜的 DOM 交互感興趣examples/todomvc 和 examples/webgl 提供了規(guī)模更大的實戰(zhàn)樣板。在此基礎(chǔ)上你完全可以仿照 Paint 的骨架快速實現(xiàn)自定義繪圖工具、白板協(xié)作應(yīng)用或圖形編輯器。贊分享開發(fā)工具【免費下載鏈接】wasm-bindgenFacilitating high-level interactions between Wasm modules and JavaScript項目地址https://gitcode.com/gh_mirrors/wa/wasm-bindgen點擊查看免費下載相關(guān)推薦wasm-bindgen Julia Set 分形繪制示例用 Rust 與 web-sys 在 Canvas 上生成 Julia 集wasm bindgen Julia Set 分形繪制示例用 Rust 與 web sys 在 Canvas 上生成 Julia 集 導讀 本文以 wasm開發(fā)工具大麥自動搶票工具實操手冊部署、配置與成功率優(yōu)化全解析大麥自動搶票工具實操手冊部署、配置與成功率優(yōu)化全解析 ticket purchase 是一個針對大麥網(wǎng)的自動搶票開源工具基于 Python 實現(xiàn)網(wǎng)頁端與移動開發(fā)工具使用 wasm-bindgen 與 web-sys 在 Rust 中繪制 2D Canvas從零實現(xiàn)一個笑臉示例使用 wasm bindgen 與 web sys 在 Rust 中繪制 2D Canvas從零實現(xiàn)一個笑臉示例 本文以 wasm bindgen 倉庫自帶的開發(fā)工具上一篇Miniflux 2 域名管理子域名與路徑路由配置下一篇攝影愛好者的福音Album AI如何幫你管理幾T照片庫創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考