feat(界面设计): 优化图片操作布局与建页预览
build.yaml / build (push) Successful in 45s

This commit is contained in:
xiaoqidun committed 2026-10-31 14:22:43 +08:00
1 parent 22c8a95e7c
commit 171f118a46
5 files changed
+82 -21

No files matched your search

+1 -1
View File
@@ -356,7 +356,7 @@
</div>
<div id="createImagesRow" class="create-images-row" hidden>
<div class="create-image-actions"><button id="createImageAdd" class="button" type="button">添加图片</button><div id="createImageOrder" class="form-options" hidden><label><input type="radio" name="imageOrder" value="name-asc" checked><span>名称升序</span></label><label><input type="radio" name="imageOrder" value="name-desc"><span>名称降序</span></label><label><input type="radio" name="imageOrder" value="time-asc"><span>时间升序</span></label><label><input type="radio" name="imageOrder" value="time-desc"><span>时间降序</span></label><label><input type="radio" name="imageOrder" value="added"><span>添加顺序</span></label></div></div>
<span id="createImageCount">0</span><input id="createImages" type="file" accept=".png,.jpg,.jpeg,.bmp,.tif,.tiff,.jb2,.jbig2" multiple hidden disabled>
<input id="createImages" type="file" accept=".png,.jpg,.jpeg,.bmp,.tif,.tiff,.jb2,.jbig2" multiple hidden disabled>
</div>
<div id="createImageQueue" hidden>
<div id="createImageList" class="create-image-list"></div>
+15 -14
View File
@@ -1666,10 +1666,6 @@ button:not(:disabled):hover {
}
.create-images-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: 4px;
margin-bottom: 12px;
}
@@ -1685,12 +1681,6 @@ button:not(:disabled):hover {
}
}
#createImageCount {
align-self: start;
line-height: var(--control-height);
color: var(--text-muted);
}
#createImageQueue {
margin-bottom: 12px;
}
@@ -1765,11 +1755,22 @@ button:not(:disabled):hover {
font-size: 11px;
}
.create-image-preview img {
display: block;
max-width: 100%;
max-height: 100%;
.create-image-paper {
display: grid;
place-items: center;
box-sizing: border-box;
background: #fff;
box-shadow: 0 0 0 1px var(--line);
}
.create-image-paper img {
display: block;
width: 100%;
height: 100%;
}
.create-image-paper[hidden] {
display: none;
}
.create-image-details {
+38 -4
View File
@@ -508,7 +508,6 @@ const el = {
createImageQueue: document.querySelector("#createImageQueue"),
createImageList: document.querySelector("#createImageList"),
createImageAdd: document.querySelector("#createImageAdd"),
createImageCount: document.querySelector("#createImageCount"),
createImageOrder: document.querySelector("#createImageOrder"),
createPortrait: document.querySelector("#createPortrait"),
createLandscape: document.querySelector("#createLandscape"),
@@ -1681,6 +1680,7 @@ el.createMode.addEventListener("change", () => {
});
el.createSize.addEventListener("change", applyCreatePaper);
for (const input of [el.createWidth, el.createHeight]) input.addEventListener("input", () => updateCreateDirection(true));
el.createMargin.addEventListener("input", updateCreateImagePreviews);
for (const radio of [el.createPortrait, el.createLandscape]) radio.addEventListener("change", () => {
[el.createWidth.value, el.createHeight.value] = [el.createHeight.value, el.createWidth.value];
updateCreateDirection();
@@ -3886,6 +3886,7 @@ function updateCreateDirection(matchPaper = false) {
el.createLandscape.checked = valid && width > height;
el.createPortrait.disabled = el.createLandscape.disabled = !valid || width === height || el.createWidth.disabled;
if (matchPaper) el.createSize.value = Object.entries(createPaperSizes).find(([, [w, h]]) => Math.min(width, height) === w && Math.max(width, height) === h)?.[0] || "custom";
updateCreateImagePreviews();
}
function releaseCreateImages(tab) {
@@ -3963,7 +3964,6 @@ function renderCreateImages() {
const focused = el.createImageList.contains(document.activeElement);
el.createImageList.replaceChildren();
for (const input of el.createImageOrder.querySelectorAll("input")) input.checked = input.value === queue.order;
el.createImageCount.textContent = String(queue.entries.length);
if (!queue.entries.some(entry => entry.id === queue.selected)) queue.selected = queue.entries[0]?.id ?? null;
queue.observer = new IntersectionObserver(items => {
for (const item of items) if (item.isIntersecting && isForegroundDocument(tab)) {
@@ -4000,10 +4000,39 @@ function renderCreateImages() {
}
function showCreateImagePreview(entry, row) {
const image = document.createElement("img");
const paper = document.createElement("span"), image = document.createElement("img");
paper.className = "create-image-paper";
image.src = entry.url;
image.draggable = false;
row.querySelector(".create-image-preview").replaceChildren(image);
paper.append(image);
row.querySelector(".create-image-preview").replaceChildren(paper);
layoutCreateImagePreview(entry, row);
}
function updateCreateImagePreviews() {
const queue = state.createImageQueue;
if (!queue) return;
const entries = new Map(queue.entries.map(entry => [entry.id, entry]));
for (const row of el.createImageList.children) {
const entry = entries.get(Number(row.dataset.imageId));
if (entry?.url && row.querySelector(".create-image-paper")) layoutCreateImagePreview(entry, row);
}
}
function layoutCreateImagePreview(entry, row) {
const paper = row.querySelector(".create-image-paper"), image = paper.firstElementChild;
const margin = Number(el.createMargin.value), follow = el.createSize.value === "image";
const width = follow ? entry.width + 2 * margin : Number(el.createWidth.value);
const height = follow ? entry.height + 2 * margin : Number(el.createHeight.value);
const valid = Number.isFinite(width + height + margin) && margin >= 0 && width > 2 * margin && height > 2 * margin;
paper.hidden = !valid;
if (!valid) return;
const scale = 46 / Math.max(width, height);
const fit = follow ? 1 : Math.min((width - 2 * margin) / entry.width, (height - 2 * margin) / entry.height);
paper.style.width = `${width * scale}px`;
paper.style.height = `${height * scale}px`;
image.style.width = `${entry.width * fit * scale}px`;
image.style.height = `${entry.height * fit * scale}px`;
}
async function loadCreateImagePreview(tab, queue, entry) {
@@ -4014,6 +4043,11 @@ async function loadCreateImagePreview(tab, queue, entry) {
const source = entry.file instanceof Blob ? entry.file : new Blob([await entry.file.arrayBuffer()]);
if (tab.state.createImageQueue !== queue || !queue.entries.includes(entry)) return;
bitmap = await createImageBitmap(source, { resizeWidth: 64 });
if (tab.closed || tab.state.createImageQueue !== queue || !queue.entries.includes(entry)) return;
const size = await callTabWASM(tab, "ofdgoImagePageSize", new Uint8Array(await source.arrayBuffer()));
if (tab.state.createImageQueue !== queue || !queue.entries.includes(entry)) return;
entry.width = size.width;
entry.height = size.height;
const canvas = document.createElement("canvas"), scale = Math.min(64 / bitmap.width, 64 / bitmap.height, 1);
canvas.width = Math.max(1, Math.round(bitmap.width * scale));
canvas.height = Math.max(1, Math.round(bitmap.height * scale));
+1 -1
View File
@@ -478,7 +478,7 @@ class BrowserEngine {
"ofdgoLoadPDF", "ofdgoDocumentInfo", "ofdgoDocumentVersions", "ofdgoDocumentNavigation", "ofdgoOpenLinks", "ofdgoOutlineLinks", "ofdgoRenderBackends", "ofdgoSVGFontData",
"ofdgoSearchPage", "ofdgoPageTextString", "ofdgoResources", "ofdgoResourceReferences", "ofdgoResourceTargets",
"ofdgoResourceLocations", "ofdgoFontPreview", "ofdgoFontFaces", "ofdgoFontFace", "ofdgoMatchFontFiles",
"ofdgoImages", "ofdgoStructureTargets", "ofdgoStructureObjects", "ofdgoPackageDocuments", "ofdgoVerifySignatures",
"ofdgoImages", "ofdgoImagePageSize", "ofdgoStructureTargets", "ofdgoStructureObjects", "ofdgoPackageDocuments", "ofdgoVerifySignatures",
"ofdgoAnnotationInfos", "ofdgoInvoices", "ofdgoInvoice", "ofdgoExportInvoice", "ofdgoValidateInvoice",
"ofdgoActionList", "ofdgoPageSettings", "ofdgoReadAnnotation", "ofdgoPreviewSignatureSeal",
"ofdgoExportFormats", "ofdgoOutputFormats", "ofdgoParsePageRange", "ofdgoExportPage", "ofdgoExportDocument",