From 37af14ff8e78dba1344806d6002f6dd07987f1ef Mon Sep 17 00:00:00 2001 From: Shimo-1999 <63487964+Shimo-1999@users.noreply.github.com> Date: Wed, 22 Oct 2025 06:56:13 +0900 Subject: [PATCH 1/3] feat: make SVG stroke width adjustable (0px disables stroke) --- wasm/src/lib.rs | 7 +++++-- web/index.html | 11 +++++++++-- web/js/script.js | 17 +++++++++++------ 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 44a40d0..436619c 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -57,7 +57,7 @@ impl Store { } #[wasm_bindgen(js_name = visSvg)] - pub fn vis_svg(&self, step: u32) -> String { + pub fn vis_svg(&self, step: u32, stroke_width: f32) -> String { use svg::node::element::{Group, Rectangle as SvgRect}; use svg::Document; @@ -76,12 +76,15 @@ impl Store { let mut g = Group::new().set("shape-rendering", "crispEdges"); for r in frame { - let rect = SvgRect::new() + let mut rect = SvgRect::new() .set("x", r.x1 as i32) .set("y", r.y1 as i32) .set("width", (r.x2 - r.x1) as i32) .set("height", (r.y2 - r.y1) as i32) .set("fill", format!("#{:06X}", r.color & 0x00_FF_FF_FF)); + if stroke_width > 0.0 { + rect = rect.set("stroke", "#000000").set("stroke-width", stroke_width); + } g = g.add(rect); } doc = doc.add(g); diff --git a/web/index.html b/web/index.html index f7431e8..dae4a49 100644 --- a/web/index.html +++ b/web/index.html @@ -105,6 +105,9 @@

実行

実行後、右のツールバーで再生・保存ができます。
+
+ stroke で枠線の幅(px)を調整できます。0 で非表示。 +
@@ -139,14 +142,18 @@

再生・保存

-
+
-
+
+
+ + +
diff --git a/web/js/script.js b/web/js/script.js index 0c4c4d0..c54dd2f 100644 --- a/web/js/script.js +++ b/web/js/script.js @@ -43,6 +43,7 @@ window.addEventListener("DOMContentLoaded", async () => { tBar: document.getElementById("t_bar"), turn: document.getElementById("turn"), speed: document.getElementById("speed"), + strokeWidth: document.getElementById("strokeWidth"), }; els.numRects?.setAttribute("max", String(LIMITS.RECT_MAX_INPUT)); @@ -58,6 +59,10 @@ window.addEventListener("DOMContentLoaded", async () => { els.playBtn.addEventListener("click", togglePlay); els.tBar.addEventListener("input", () => seek(Number(els.tBar.value))); els.turn.addEventListener("input", () => seek(Number(els.turn.value))); + els.strokeWidth.addEventListener("input", () => { + const step = Number(els.turn.value) || 1; + renderStep(step); + }); // save handlers els.savePng.addEventListener("click", onSavePng); @@ -290,7 +295,7 @@ function seek(step) { function renderStep(step) { if (!state.store) return; - const svg = state.store.visSvg(step); + const svg = state.store.visSvg(step, els.strokeWidth.value); els.stage.innerHTML = svg; } @@ -340,7 +345,7 @@ async function onSavePng() { if (!state.store || !state.hasResult) return; try { const step = Number(els.turn.value) || 0; - const svg = state.store.visSvg(step); + const svg = state.store.visSvg(step, els.strokeWidth.value); const { canvas } = await svgToCanvas(svg, { scale: 1, background: "#ffffff" }); const blob = await canvasToBlob(canvas, "image/png"); @@ -371,7 +376,7 @@ async function onSaveGif() { const delay = Math.round((step * 2000) / speedValue); const lastDelay = 3000; - const firstSvg = state.store.visSvg(1); + const firstSvg = state.store.visSvg(1, els.strokeWidth.value); const { canvas } = await svgToCanvas(firstSvg, { background: "#ffffff" }); const gif = new GIF({ @@ -385,7 +390,7 @@ async function onSaveGif() { gif.on("progress", p => { btn.innerHTML = `GIF ${Math.round(p * 100)}%`; }); const addFrame = async (s, d) => { - const svg = state.store.visSvg(s); + const svg = state.store.visSvg(s, els.strokeWidth.value); await drawSvgOntoCanvas(svg, canvas, { background: "#ffffff" }); gif.addFrame(canvas, { copy: true, delay: d }); }; @@ -508,7 +513,7 @@ async function onSaveFramesZip() { try { const max = Number(els.turn.max) || 1; - const firstSvg = state.store.visSvg(1); + const firstSvg = state.store.visSvg(1, els.strokeWidth.value); const { canvas } = await svgToCanvas(firstSvg, { background: "#fff" }); const ctx = canvas.getContext("2d", { willReadFrequently: true }); ctx.imageSmoothingEnabled = false; @@ -520,7 +525,7 @@ async function onSaveFramesZip() { const pad = String(max).length; for (let s = 1; s <= max; s++) { - const svg = state.store.visSvg(s); + const svg = state.store.visSvg(s, els.strokeWidth.value); await drawSvgOntoCanvas(svg, canvas, { background: "#fff" }); const blob = await canvasToBlob(canvas, "image/png"); const filename = `frame_${String(s).padStart(pad, "0")}.png`; From 635714474900de2ade57c3bbd4a6f2c1e247abe1 Mon Sep 17 00:00:00 2001 From: Shimo-1999 <63487964+Shimo-1999@users.noreply.github.com> Date: Wed, 22 Oct 2025 07:56:09 +0900 Subject: [PATCH 2/3] fix(web): pass numeric strokeWidth to WASM (use Number()) --- web/js/script.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web/js/script.js b/web/js/script.js index c54dd2f..95d2bb0 100644 --- a/web/js/script.js +++ b/web/js/script.js @@ -295,7 +295,7 @@ function seek(step) { function renderStep(step) { if (!state.store) return; - const svg = state.store.visSvg(step, els.strokeWidth.value); + const svg = state.store.visSvg(step, Number(els.strokeWidth.value)); els.stage.innerHTML = svg; } @@ -345,7 +345,7 @@ async function onSavePng() { if (!state.store || !state.hasResult) return; try { const step = Number(els.turn.value) || 0; - const svg = state.store.visSvg(step, els.strokeWidth.value); + const svg = state.store.visSvg(step, Number(els.strokeWidth.value)); const { canvas } = await svgToCanvas(svg, { scale: 1, background: "#ffffff" }); const blob = await canvasToBlob(canvas, "image/png"); @@ -376,7 +376,7 @@ async function onSaveGif() { const delay = Math.round((step * 2000) / speedValue); const lastDelay = 3000; - const firstSvg = state.store.visSvg(1, els.strokeWidth.value); + const firstSvg = state.store.visSvg(1, Number(els.strokeWidth.value)); const { canvas } = await svgToCanvas(firstSvg, { background: "#ffffff" }); const gif = new GIF({ @@ -390,7 +390,7 @@ async function onSaveGif() { gif.on("progress", p => { btn.innerHTML = `GIF ${Math.round(p * 100)}%`; }); const addFrame = async (s, d) => { - const svg = state.store.visSvg(s, els.strokeWidth.value); + const svg = state.store.visSvg(s, Number(els.strokeWidth.value)); await drawSvgOntoCanvas(svg, canvas, { background: "#ffffff" }); gif.addFrame(canvas, { copy: true, delay: d }); }; @@ -513,7 +513,7 @@ async function onSaveFramesZip() { try { const max = Number(els.turn.max) || 1; - const firstSvg = state.store.visSvg(1, els.strokeWidth.value); + const firstSvg = state.store.visSvg(1, Number(els.strokeWidth.value)); const { canvas } = await svgToCanvas(firstSvg, { background: "#fff" }); const ctx = canvas.getContext("2d", { willReadFrequently: true }); ctx.imageSmoothingEnabled = false; @@ -525,7 +525,7 @@ async function onSaveFramesZip() { const pad = String(max).length; for (let s = 1; s <= max; s++) { - const svg = state.store.visSvg(s, els.strokeWidth.value); + const svg = state.store.visSvg(s, Number(els.strokeWidth.value)); await drawSvgOntoCanvas(svg, canvas, { background: "#fff" }); const blob = await canvasToBlob(canvas, "image/png"); const filename = `frame_${String(s).padStart(pad, "0")}.png`; From d144f8d0c36314750df4ed574c877f6eb2f1a063 Mon Sep 17 00:00:00 2001 From: Shimo-1999 <63487964+Shimo-1999@users.noreply.github.com> Date: Wed, 22 Oct 2025 20:06:54 +0900 Subject: [PATCH 3/3] =?UTF-8?q?add=20aria-labelledby;=20rename=20label=20t?= =?UTF-8?q?o=20=E6=9E=A0=E7=B7=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/index.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web/index.html b/web/index.html index dae4a49..f2b3f23 100644 --- a/web/index.html +++ b/web/index.html @@ -105,9 +105,6 @@

実行

実行後、右のツールバーで再生・保存ができます。
-
- stroke で枠線の幅(px)を調整できます。0 で非表示。 -
@@ -150,9 +147,12 @@

再生・保存

-
- - +
+
+ +