Fix crop range preview zoom and history saving

This commit is contained in:
wuyanwanwu
2026-08-14 01:37:19 +08:00
parent cf0f53d148
commit 1de77c904e
14 changed files with 42 additions and 33 deletions
+25 -16
View File
@@ -65,6 +65,11 @@ const HISTORY_STORAGE_KEY = "bead-pattern-history";
const HISTORY_DB_NAME = "pixel-bead-pattern-history";
const HISTORY_STORE_NAME = "patterns";
function createHistoryId() {
if (typeof globalThis.crypto?.randomUUID === "function") return globalThis.crypto.randomUUID();
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
}
function openHistoryDatabase() {
return new Promise<IDBDatabase>((resolve, reject) => {
const request = indexedDB.open(HISTORY_DB_NAME, 1);
@@ -483,10 +488,12 @@ function drawFittedImage(
}
drawWidth *= cropZoom;
drawHeight *= cropZoom;
const overflowX = Math.max(0, (drawWidth - width) / 2);
const overflowY = Math.max(0, (drawHeight - height) / 2);
const drawX = (width - drawWidth) / 2 + cropX * overflowX;
const drawY = (height - drawHeight) / 2 + cropY * overflowY;
// Allow an object at the source-image edge to be dragged into the crop
// center. The exposed area intentionally becomes transparent/no-bead.
const panRangeX = drawWidth / 2;
const panRangeY = drawHeight / 2;
const drawX = (width - drawWidth) / 2 + cropX * panRangeX;
const drawY = (height - drawHeight) / 2 + cropY * panRangeY;
ctx.drawImage(image, drawX, drawY, drawWidth, drawHeight);
}
@@ -921,18 +928,20 @@ export default function Home() {
const savePattern = async () => {
if (!pixels.length) return;
const entry: SavedPattern = {
id: crypto.randomUUID(),
name: sourceName.replace(/\.[^.]+$/, "") || "未命名图纸",
savedAt: new Date().toISOString(),
width: gridWidth,
height: gridHeight,
colorLimit,
codes: pixels.map((pixel) => pixel.color?.code ?? null),
};
try {
const entry: SavedPattern = {
id: createHistoryId(),
name: sourceName.replace(/\.[^.]+$/, "") || "未命名图纸",
savedAt: new Date().toISOString(),
width: gridWidth,
height: gridHeight,
colorLimit,
codes: pixels.map((pixel) => pixel.color?.code ?? null),
};
await persistHistory([entry, ...history].slice(0, HISTORY_LIMIT));
setHistoryQuery("");
setStatus("图纸已保存到本机历史记录");
requestAnimationFrame(() => document.getElementById("history-title")?.scrollIntoView({ behavior: "smooth", block: "start" }));
} catch {
setStatus("保存历史失败,请检查浏览器是否允许本地存储或剩余空间是否充足");
}
@@ -1029,9 +1038,9 @@ export default function Home() {
<div className="pattern-toolbar">
<div><p className="section-kicker">02 / </p><h2></h2></div>
<div className="zoom-control" aria-label="图纸缩放">
<button onClick={() => setZoom((z) => Math.max(8, z - 2))} aria-label="缩小"></button>
<input aria-label="缩放比例" type="range" min="8" max="42" value={zoom} onChange={(e) => setZoom(Number(e.target.value))} />
<button onClick={() => setZoom((z) => Math.min(42, z + 2))} aria-label="放大"></button>
<button onClick={() => setZoom((z) => Math.max(3, z - 1))} aria-label="缩小"></button>
<input aria-label="缩放比例" type="range" min="3" max="42" step="1" value={zoom} onChange={(e) => setZoom(Number(e.target.value))} />
<button onClick={() => setZoom((z) => Math.min(42, z + 1))} aria-label="放大"></button>
<output>{zoom}px</output>
</div>
</div>