Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
Expand Down
11 changes: 9 additions & 2 deletions web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,21 @@ <h3 id="preview-heading" class="section-title mb-0">再生・保存</h3>
<label for="t_bar" class="visually-hidden" id="sliderLabel">ターン</label>
<input type="range" id="t_bar" min="1" max="1" value="1" class="form-range flex-grow-1" aria-labelledby="sliderLabel" />

<div class="input-group input-group-sm" style="width: 160px;">
<div class="input-group input-group-sm" style="width: 200px;">
<label class="input-group-text" for="turn">turn</label>
<input type="number" id="turn" min="1" max="1" value="1" class="form-control text-center" inputmode="numeric" />
</div>
<div class="input-group input-group-sm" style="width: 150px;">
<div class="input-group input-group-sm" style="width: 180px;">
<label class="input-group-text" for="speed">fps</label>
<input type="number" id="speed" value="100" min="1" max="240" class="form-control text-center" inputmode="numeric" />
</div>
<div class="vr d-none d-lg-block mx-2"></div>
<div class="input-group input-group-sm" style="width: 190px;">
<label class="input-group-text text-nowrap" id="strokeWidthLabel" for="strokeWidth" title="長方形の枠線の太さ">
枠線
</label>
<input type="number" id="strokeWidth" class="form-control text-center" value="0.5" min="0" max="10" step="0.1" inputmode="decimal" aria-labelledby="strokeWidthLabel" aria-describedby="strokeHelp"/>
</div>
</fieldset>

<!-- Stage -->
Expand Down
17 changes: 11 additions & 6 deletions web/js/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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);
Expand Down Expand Up @@ -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, Number(els.strokeWidth.value));
els.stage.innerHTML = svg;
}

Expand Down Expand Up @@ -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, Number(els.strokeWidth.value));

const { canvas } = await svgToCanvas(svg, { scale: 1, background: "#ffffff" });
const blob = await canvasToBlob(canvas, "image/png");
Expand Down Expand Up @@ -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, Number(els.strokeWidth.value));
const { canvas } = await svgToCanvas(firstSvg, { background: "#ffffff" });

const gif = new GIF({
Expand All @@ -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, Number(els.strokeWidth.value));
await drawSvgOntoCanvas(svg, canvas, { background: "#ffffff" });
gif.addFrame(canvas, { copy: true, delay: d });
};
Expand Down Expand Up @@ -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, Number(els.strokeWidth.value));
const { canvas } = await svgToCanvas(firstSvg, { background: "#fff" });
const ctx = canvas.getContext("2d", { willReadFrequently: true });
ctx.imageSmoothingEnabled = false;
Expand All @@ -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, 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`;
Expand Down