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