mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-08-30 12:12:40 +08:00
+104
-27
@@ -61,7 +61,9 @@ const state = {
|
|||||||
fitMode: "width",
|
fitMode: "width",
|
||||||
renderAnnotations: true,
|
renderAnnotations: true,
|
||||||
pageCache: new Map(),
|
pageCache: new Map(),
|
||||||
pageInFlight: new Set(),
|
pageInFlight: new Map(),
|
||||||
|
pageRenderQueue: [],
|
||||||
|
pageRenderRunning: false,
|
||||||
pageObserver: null,
|
pageObserver: null,
|
||||||
scrollFrame: 0,
|
scrollFrame: 0,
|
||||||
thumbnailCache: new Map(),
|
thumbnailCache: new Map(),
|
||||||
@@ -102,6 +104,7 @@ const el = {
|
|||||||
svgHost: document.querySelector("#svgHost"),
|
svgHost: document.querySelector("#svgHost"),
|
||||||
pageListPanel: document.querySelector(".page-list-panel"),
|
pageListPanel: document.querySelector(".page-list-panel"),
|
||||||
pageList: document.querySelector("#pageList"),
|
pageList: document.querySelector("#pageList"),
|
||||||
|
metaPanel: document.querySelector(".meta-panel"),
|
||||||
metaTitle: document.querySelector("#metaTitle"),
|
metaTitle: document.querySelector("#metaTitle"),
|
||||||
metaAuthor: document.querySelector("#metaAuthor"),
|
metaAuthor: document.querySelector("#metaAuthor"),
|
||||||
metaVersion: document.querySelector("#metaVersion"),
|
metaVersion: document.querySelector("#metaVersion"),
|
||||||
@@ -894,6 +897,10 @@ async function openDocument(options = {}) {
|
|||||||
if (options.resetScroll) {
|
if (options.resetScroll) {
|
||||||
el.viewerPanel.scrollLeft = 0;
|
el.viewerPanel.scrollLeft = 0;
|
||||||
el.viewerPanel.scrollTop = 0;
|
el.viewerPanel.scrollTop = 0;
|
||||||
|
el.pageListPanel.scrollLeft = 0;
|
||||||
|
el.pageListPanel.scrollTop = 0;
|
||||||
|
el.metaPanel.scrollLeft = 0;
|
||||||
|
el.metaPanel.scrollTop = 0;
|
||||||
}
|
}
|
||||||
applyFit(false);
|
applyFit(false);
|
||||||
await nextFrame();
|
await nextFrame();
|
||||||
@@ -936,7 +943,7 @@ async function renderPage(index, options = {}) {
|
|||||||
if (options.scroll !== false) {
|
if (options.scroll !== false) {
|
||||||
scrollToPage(index);
|
scrollToPage(index);
|
||||||
}
|
}
|
||||||
await renderFlowPage(index, { throwError: true, openSeq });
|
await renderFlowPage(index, { throwError: true, openSeq, priority: 0 });
|
||||||
if (openSeq !== state.openSeq) {
|
if (openSeq !== state.openSeq) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1075,6 +1082,10 @@ function renderPageFlow() {
|
|||||||
|
|
||||||
function resetPageFlow() {
|
function resetPageFlow() {
|
||||||
state.pageCache.clear();
|
state.pageCache.clear();
|
||||||
|
for (const task of state.pageInFlight.values()) {
|
||||||
|
task.resolve(null);
|
||||||
|
}
|
||||||
|
state.pageRenderQueue = [];
|
||||||
state.pageInFlight.clear();
|
state.pageInFlight.clear();
|
||||||
resetThumbnails();
|
resetThumbnails();
|
||||||
if (state.pageObserver) {
|
if (state.pageObserver) {
|
||||||
@@ -1086,7 +1097,7 @@ function resetPageFlow() {
|
|||||||
function observeFlowPage(shell, index) {
|
function observeFlowPage(shell, index) {
|
||||||
const openSeq = state.openSeq;
|
const openSeq = state.openSeq;
|
||||||
if (index < 4 && index !== state.pageIndex) {
|
if (index < 4 && index !== state.pageIndex) {
|
||||||
renderFlowPage(index, { openSeq });
|
renderFlowPage(index, { openSeq, priority: 3 });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const observer = flowPageObserver();
|
const observer = flowPageObserver();
|
||||||
@@ -1108,7 +1119,7 @@ function flowPageObserver() {
|
|||||||
}
|
}
|
||||||
const index = Number.parseInt(entry.target.dataset.pageIndex, 10);
|
const index = Number.parseInt(entry.target.dataset.pageIndex, 10);
|
||||||
state.pageObserver.unobserve(entry.target);
|
state.pageObserver.unobserve(entry.target);
|
||||||
renderFlowPage(index, { openSeq });
|
renderFlowPage(index, { openSeq, priority: 4 });
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
root: el.viewerPanel,
|
root: el.viewerPanel,
|
||||||
@@ -1123,27 +1134,18 @@ async function renderFlowPage(index, options = {}) {
|
|||||||
if (openSeq !== state.openSeq) {
|
if (openSeq !== state.openSeq) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (state.pageCache.has(index)) {
|
|
||||||
return state.pageCache.get(index);
|
|
||||||
}
|
|
||||||
const key = `${openSeq}:${index}`;
|
|
||||||
if (state.pageInFlight.has(key)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
state.pageInFlight.add(key);
|
|
||||||
try {
|
try {
|
||||||
await nextFrame();
|
const page = await loadPageData(index, { openSeq, priority: options.priority || 2 });
|
||||||
if (openSeq !== state.openSeq) {
|
if (openSeq !== state.openSeq) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const page = callWASM("ofdgoRenderPage", index);
|
if (page) {
|
||||||
if (openSeq !== state.openSeq) {
|
const shell = pageShell(index);
|
||||||
return null;
|
if (!shell?.classList.contains("rendered")) {
|
||||||
|
mountPageSVG(index, page, openSeq);
|
||||||
|
}
|
||||||
|
updateThumbnail(index, openSeq);
|
||||||
}
|
}
|
||||||
state.pageCache.set(index, page);
|
|
||||||
cacheThumbnail(index, page.svg);
|
|
||||||
mountPageSVG(index, page, openSeq);
|
|
||||||
updateThumbnail(index, openSeq);
|
|
||||||
return page;
|
return page;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (openSeq === state.openSeq) {
|
if (openSeq === state.openSeq) {
|
||||||
@@ -1153,11 +1155,88 @@ async function renderFlowPage(index, options = {}) {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
|
||||||
state.pageInFlight.delete(key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadPageData(index, options = {}) {
|
||||||
|
const openSeq = options.openSeq || state.openSeq;
|
||||||
|
if (openSeq !== state.openSeq) {
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
if (state.pageCache.has(index)) {
|
||||||
|
return Promise.resolve(state.pageCache.get(index));
|
||||||
|
}
|
||||||
|
const key = `${openSeq}:${index}`;
|
||||||
|
const priority = options.priority || 3;
|
||||||
|
const current = state.pageInFlight.get(key);
|
||||||
|
if (current) {
|
||||||
|
current.priority = Math.min(current.priority, priority);
|
||||||
|
return current.promise;
|
||||||
|
}
|
||||||
|
let resolve;
|
||||||
|
let reject;
|
||||||
|
const promise = new Promise((done, fail) => {
|
||||||
|
resolve = done;
|
||||||
|
reject = fail;
|
||||||
|
});
|
||||||
|
const task = { key, index, openSeq, priority, resolve, reject, promise };
|
||||||
|
state.pageInFlight.set(key, task);
|
||||||
|
state.pageRenderQueue.push(task);
|
||||||
|
schedulePageRender();
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function schedulePageRender() {
|
||||||
|
if (state.pageRenderRunning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.pageRenderRunning = true;
|
||||||
|
requestAnimationFrame(processPageRenderQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function processPageRenderQueue() {
|
||||||
|
try {
|
||||||
|
while (state.pageRenderQueue.length) {
|
||||||
|
state.pageRenderQueue.sort(comparePageRenderTask);
|
||||||
|
const task = state.pageRenderQueue.shift();
|
||||||
|
try {
|
||||||
|
if (task.openSeq !== state.openSeq) {
|
||||||
|
task.resolve(null);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (state.pageCache.has(task.index)) {
|
||||||
|
task.resolve(state.pageCache.get(task.index));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
await nextFrame();
|
||||||
|
if (task.openSeq !== state.openSeq) {
|
||||||
|
task.resolve(null);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const page = callWASM("ofdgoRenderPage", task.index);
|
||||||
|
if (task.openSeq === state.openSeq) {
|
||||||
|
state.pageCache.set(task.index, page);
|
||||||
|
cacheThumbnail(task.index, page.svg);
|
||||||
|
}
|
||||||
|
task.resolve(page);
|
||||||
|
} catch (err) {
|
||||||
|
task.reject(err);
|
||||||
|
} finally {
|
||||||
|
state.pageInFlight.delete(task.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
state.pageRenderRunning = false;
|
||||||
|
if (state.pageRenderQueue.length) {
|
||||||
|
schedulePageRender();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function comparePageRenderTask(a, b) {
|
||||||
|
return a.priority - b.priority || Math.abs(a.index - state.pageIndex) - Math.abs(b.index - state.pageIndex);
|
||||||
|
}
|
||||||
|
|
||||||
function mountPageSVG(index, page, openSeq = state.openSeq) {
|
function mountPageSVG(index, page, openSeq = state.openSeq) {
|
||||||
if (openSeq !== state.openSeq) {
|
if (openSeq !== state.openSeq) {
|
||||||
return;
|
return;
|
||||||
@@ -1190,7 +1269,7 @@ function markFlowPageError(index, err) {
|
|||||||
|
|
||||||
function queueNearbyPages(index, openSeq = state.openSeq) {
|
function queueNearbyPages(index, openSeq = state.openSeq) {
|
||||||
for (let i = Math.max(0, index - 1); i <= Math.min((state.doc?.pageCount || 1) - 1, index + 2); i += 1) {
|
for (let i = Math.max(0, index - 1); i <= Math.min((state.doc?.pageCount || 1) - 1, index + 2); i += 1) {
|
||||||
renderFlowPage(i, { openSeq });
|
renderFlowPage(i, { openSeq, priority: 2 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1473,15 +1552,13 @@ async function renderThumbnail(index, openSeq = state.openSeq) {
|
|||||||
}
|
}
|
||||||
state.thumbnailInFlight.add(key);
|
state.thumbnailInFlight.add(key);
|
||||||
try {
|
try {
|
||||||
await nextFrame();
|
const page = await loadPageData(index, { openSeq, priority: 5 });
|
||||||
if (openSeq !== state.openSeq) {
|
if (openSeq !== state.openSeq) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const page = callWASM("ofdgoRenderPage", index);
|
if (!page) {
|
||||||
if (openSeq !== state.openSeq) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cacheThumbnail(index, page.svg);
|
|
||||||
updateThumbnail(index, openSeq);
|
updateThumbnail(index, openSeq);
|
||||||
} catch {
|
} catch {
|
||||||
if (openSeq === state.openSeq) {
|
if (openSeq === state.openSeq) {
|
||||||
|
|||||||
Reference in New Issue
Block a user