mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 14:53:44 +08:00
perf(性能提升): 优化快速翻页与分帧呈现
This commit is contained in:
1 parent
51ab8846e3
commit
01e29618f4
2 files changed
+80
-17
No files matched your search
@@ -160,6 +160,8 @@ function createDocumentState() {
|
|||||||
pagePrefetchDirection: 1,
|
pagePrefetchDirection: 1,
|
||||||
pageRenderRunning: false,
|
pageRenderRunning: false,
|
||||||
pageRenderWorkers: 0,
|
pageRenderWorkers: 0,
|
||||||
|
pagePresentQueue: new Map(),
|
||||||
|
pagePresentFrame: 0,
|
||||||
pageObserver: null,
|
pageObserver: null,
|
||||||
visiblePages: new Set(),
|
visiblePages: new Set(),
|
||||||
scrollFrame: 0,
|
scrollFrame: 0,
|
||||||
@@ -12369,7 +12371,7 @@ async function callTabWASM(tab, name, ...args) {
|
|||||||
}
|
}
|
||||||
if (name === "ofdgoLoadImport" && ArrayBuffer.isView(args[0]) && !nativeHost) transfer.push(args[0].buffer);
|
if (name === "ofdgoLoadImport" && ArrayBuffer.isView(args[0]) && !nativeHost) transfer.push(args[0].buffer);
|
||||||
const message = {id, name, args};
|
const message = {id, name, args};
|
||||||
if (name === "ofdgoRenderPage" && [...state.pageInFlight.values()].some(task => task.index === args[0] && task.priority >= 6)) message.preload = true;
|
if (name === "ofdgoRenderPage" && [...state.pageInFlight.values()].some(task => task.index === args[0] && pageView.pageRenderPriority(task, state) >= 6)) message.preload = true;
|
||||||
tab.worker.postMessage(message, transfer);
|
tab.worker.postMessage(message, transfer);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
wasmRequests.delete(id);
|
wasmRequests.delete(id);
|
||||||
|
|||||||
+77
-16
@@ -47,6 +47,7 @@ export function createPageView(context) {
|
|||||||
} else if (state.pageWindow && (!pageView.pageShell(index) || pageView.pageShell(index).style.visibility === "hidden")) {
|
} else if (state.pageWindow && (!pageView.pageShell(index) || pageView.pageShell(index).style.visibility === "hidden")) {
|
||||||
pageView.scrollToPage(index);
|
pageView.scrollToPage(index);
|
||||||
}
|
}
|
||||||
|
pageView.prunePageRenderQueue();
|
||||||
const page = await pageView.renderFlowPage(index, { throwError: true, openSeq, priority: 0 });
|
const page = await pageView.renderFlowPage(index, { throwError: true, openSeq, priority: 0 });
|
||||||
if (openSeq !== state.openSeq || index !== state.pageIndex) {
|
if (openSeq !== state.openSeq || index !== state.pageIndex) {
|
||||||
return null;
|
return null;
|
||||||
@@ -222,6 +223,10 @@ export function createPageView(context) {
|
|||||||
|
|
||||||
resetPageLoading() {
|
resetPageLoading() {
|
||||||
context.releaseMediaPlayers();
|
context.releaseMediaPlayers();
|
||||||
|
if (context.state.pagePresentFrame) cancelAnimationFrame(context.state.pagePresentFrame);
|
||||||
|
context.state.pagePresentFrame = 0;
|
||||||
|
for (const task of context.state.pagePresentQueue.values()) task.resolve(null);
|
||||||
|
context.state.pagePresentQueue.clear();
|
||||||
for (const [id, request] of context.wasmRequests) {
|
for (const [id, request] of context.wasmRequests) {
|
||||||
if (request.tab === context.documentTab() && request.name === "ofdgoRenderPage") context.wasmWorker?.postMessage({ type: "cancel", id });
|
if (request.tab === context.documentTab() && request.name === "ofdgoRenderPage") context.wasmWorker?.postMessage({ type: "cancel", id });
|
||||||
}
|
}
|
||||||
@@ -305,18 +310,15 @@ export function createPageView(context) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const page = await pageView.loadPageData(index, { openSeq, priority: options.priority ?? 2,
|
const page = await pageView.loadPageData(index, { openSeq, priority: options.priority ?? 2, view: true,
|
||||||
refresh: context.state.editing && context.state.pageCache.get(index)?.previewOnly });
|
refresh: context.state.editing && context.state.pageCache.get(index)?.previewOnly });
|
||||||
if (openSeq !== context.state.openSeq) {
|
if (openSeq !== context.state.openSeq) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (page) {
|
if (page) {
|
||||||
const shell = pageView.pageShell(index);
|
await pageView.queuePagePresentation(index, page, openSeq, true);
|
||||||
if (shell && !shell.classList.contains("rendered") && (!context.state.pageObserver || context.state.visiblePages.has(index) || index === context.state.pageIndex)) {
|
|
||||||
pageView.mountPageSVG(index, page, openSeq);
|
|
||||||
}
|
|
||||||
pageView.updateThumbnail(index, openSeq);
|
|
||||||
}
|
}
|
||||||
|
if (openSeq !== context.state.openSeq) return null;
|
||||||
pageView.trimPageCache();
|
pageView.trimPageCache();
|
||||||
return page;
|
return page;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -340,7 +342,8 @@ export function createPageView(context) {
|
|||||||
const current = context.state.pageInFlight.get(key);
|
const current = context.state.pageInFlight.get(key);
|
||||||
if (current) {
|
if (current) {
|
||||||
current.priority = Math.min(current.priority, priority);
|
current.priority = Math.min(current.priority, priority);
|
||||||
if (current.preview && !options.preview) return current.promise.then(() => openSeq === context.state.openSeq ? pageView.loadPageData(index, options) : null);
|
current.view &&= !!options.view;
|
||||||
|
if (current.preview && !options.preview) return current.promise.then(page => page && openSeq === context.state.openSeq ? pageView.loadPageData(index, options) : null);
|
||||||
return current.promise;
|
return current.promise;
|
||||||
}
|
}
|
||||||
if (!options.refresh && context.state.pageCache.has(index) && (options.preview || !context.state.pageCache.get(index).previewOnly)) {
|
if (!options.refresh && context.state.pageCache.has(index) && (options.preview || !context.state.pageCache.get(index).previewOnly)) {
|
||||||
@@ -355,7 +358,7 @@ export function createPageView(context) {
|
|||||||
resolve = done;
|
resolve = done;
|
||||||
reject = fail;
|
reject = fail;
|
||||||
});
|
});
|
||||||
const task = { key, index, openSeq, priority, preview: !!options.preview, refresh: options.refresh, resolve, reject, promise };
|
const task = { key, index, openSeq, priority, view: !!options.view, preview: !!options.preview, refresh: options.refresh, resolve, reject, promise };
|
||||||
context.state.pageInFlight.set(key, task);
|
context.state.pageInFlight.set(key, task);
|
||||||
context.state.pageRenderQueue.push(task);
|
context.state.pageRenderQueue.push(task);
|
||||||
pageView.schedulePageRender();
|
pageView.schedulePageRender();
|
||||||
@@ -460,6 +463,7 @@ export function createPageView(context) {
|
|||||||
|
|
||||||
trimPageCache() {
|
trimPageCache() {
|
||||||
const protectedPages = new Set([context.state.pageIndex, ...context.state.visiblePages, ...context.state.visibleThumbnails, ...context.state.selectedPages]);
|
const protectedPages = new Set([context.state.pageIndex, ...context.state.visiblePages, ...context.state.visibleThumbnails, ...context.state.selectedPages]);
|
||||||
|
for (const index of context.state.pagePresentQueue.keys()) protectedPages.add(index);
|
||||||
for (const view of [context.state.pageWindow, context.state.thumbnailWindow]) {
|
for (const view of [context.state.pageWindow, context.state.thumbnailWindow]) {
|
||||||
for (const index of view?.nodes.keys() || []) protectedPages.add(index);
|
for (const index of view?.nodes.keys() || []) protectedPages.add(index);
|
||||||
}
|
}
|
||||||
@@ -556,7 +560,66 @@ export function createPageView(context) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
comparePageRenderTask(a, b) {
|
comparePageRenderTask(a, b) {
|
||||||
return a.priority - b.priority || Math.abs(a.index - context.state.pageIndex) - Math.abs(b.index - context.state.pageIndex);
|
return pageView.pageRenderPriority(a) - pageView.pageRenderPriority(b) || Math.abs(a.index - context.state.pageIndex) - Math.abs(b.index - context.state.pageIndex);
|
||||||
|
},
|
||||||
|
|
||||||
|
pageRenderPriority(task, state = context.state) {
|
||||||
|
if (!task.view || task.refresh) return task.priority;
|
||||||
|
if (task.index === state.pageIndex) return 0;
|
||||||
|
if (Math.floor(task.index / state.pageLayout) === Math.floor(state.pageIndex / state.pageLayout)) return 1;
|
||||||
|
if (!task.preview) {
|
||||||
|
if (state.visiblePages.has(task.index)) return 2;
|
||||||
|
if (state.pageWindow?.nodes.has(task.index)) return 3;
|
||||||
|
}
|
||||||
|
if (state.visibleThumbnails.has(task.index) || state.thumbnailWindow?.nodes.has(task.index)) return 5;
|
||||||
|
return 6;
|
||||||
|
},
|
||||||
|
|
||||||
|
queuePagePresentation(index, page, openSeq, flow = false) {
|
||||||
|
const state = context.state;
|
||||||
|
if (openSeq !== state.openSeq || state.pageCache.get(index) !== page) return Promise.resolve(null);
|
||||||
|
let shell = flow ? pageView.pageShell(index) : null;
|
||||||
|
let thumb = context.el.pageList.querySelector(`[data-page-index="${index}"] .thumb-paper`);
|
||||||
|
if (shell?.classList.contains("rendered") || state.pageObserver && !state.visiblePages.has(index) && index !== state.pageIndex) shell = null;
|
||||||
|
if (thumb?.dataset.renderKey === `${openSeq}:${index}` || state.thumbnailObserver && !state.visibleThumbnails.has(index) && index !== state.pageIndex) thumb = null;
|
||||||
|
if (!shell && !thumb) return Promise.resolve(null);
|
||||||
|
const queue = state.pagePresentQueue, current = queue.get(index);
|
||||||
|
if (current?.page === page) {
|
||||||
|
if (shell) { current.shell = shell; current.preview = false; }
|
||||||
|
current.thumb = thumb;
|
||||||
|
return current.promise;
|
||||||
|
}
|
||||||
|
current?.resolve(null);
|
||||||
|
let resolve, reject;
|
||||||
|
const promise = new Promise((done, fail) => { resolve = done; reject = fail; });
|
||||||
|
queue.set(index, { index, page, openSeq, shell, thumb, view: true, preview: !shell, resolve, reject, promise });
|
||||||
|
pageView.schedulePagePresentation();
|
||||||
|
return promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
schedulePagePresentation() {
|
||||||
|
const tab = context.documentTab(), state = tab.state, openSeq = state.openSeq;
|
||||||
|
if (state.pagePresentFrame || !state.pagePresentQueue.size) return;
|
||||||
|
state.pagePresentFrame = requestAnimationFrame(() => {
|
||||||
|
if (context.activeTab !== tab || openSeq !== state.openSeq) return;
|
||||||
|
state.pagePresentFrame = 0;
|
||||||
|
const started = performance.now();
|
||||||
|
do {
|
||||||
|
const task = [...state.pagePresentQueue.values()].sort(pageView.comparePageRenderTask)[0];
|
||||||
|
state.pagePresentQueue.delete(task.index);
|
||||||
|
try {
|
||||||
|
if (state.pageCache.get(task.index) === task.page) {
|
||||||
|
if (task.shell && pageView.pageShell(task.index) === task.shell && !task.shell.classList.contains("rendered")
|
||||||
|
&& (!state.pageObserver || state.visiblePages.has(task.index) || task.index === state.pageIndex)) {
|
||||||
|
pageView.mountPageSVG(task.index, task.page, task.openSeq);
|
||||||
|
}
|
||||||
|
if (task.thumb && context.el.pageList.querySelector(`[data-page-index="${task.index}"] .thumb-paper`) === task.thumb) pageView.updateThumbnail(task.index, task.openSeq);
|
||||||
|
}
|
||||||
|
task.resolve(null);
|
||||||
|
} catch (err) { task.reject(err); }
|
||||||
|
} while (state.pagePresentQueue.size && performance.now() - started < 6);
|
||||||
|
pageView.schedulePagePresentation();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
mountPageSVG(index, page, openSeq = context.state.openSeq) {
|
mountPageSVG(index, page, openSeq = context.state.openSeq) {
|
||||||
@@ -698,7 +761,7 @@ export function createPageView(context) {
|
|||||||
|
|
||||||
prunePageRenderQueue() {
|
prunePageRenderQueue() {
|
||||||
context.state.pageRenderQueue = context.state.pageRenderQueue.filter(task => {
|
context.state.pageRenderQueue = context.state.pageRenderQueue.filter(task => {
|
||||||
if (task.priority < 2 || Math.abs(task.index - context.state.pageIndex) <= 2
|
if (!task.view || task.refresh || Math.abs(task.index - context.state.pageIndex) <= 2
|
||||||
|| context.state.pageWindow?.nodes.has(task.index) || context.state.thumbnailWindow?.nodes.has(task.index)) return true;
|
|| context.state.pageWindow?.nodes.has(task.index) || context.state.thumbnailWindow?.nodes.has(task.index)) return true;
|
||||||
context.state.pageInFlight.delete(task.key);
|
context.state.pageInFlight.delete(task.key);
|
||||||
task.resolve(null);
|
task.resolve(null);
|
||||||
@@ -744,6 +807,7 @@ export function createPageView(context) {
|
|||||||
if (index !== context.state.pageIndex && !context.state.openActionTask?.navigating) context.state.openActionTask = null;
|
if (index !== context.state.pageIndex && !context.state.openActionTask?.navigating) context.state.openActionTask = null;
|
||||||
if (context.state.composite && context.state.composite.index !== index) context.resetCompositeScope();
|
if (context.state.composite && context.state.composite.index !== index) context.resetCompositeScope();
|
||||||
context.state.pageIndex = index;
|
context.state.pageIndex = index;
|
||||||
|
pageView.prunePageRenderQueue();
|
||||||
if (context.state.editing) context.state.openActionPage = context.state.doc?.pages[index]?.id;
|
if (context.state.editing) context.state.openActionPage = context.state.doc?.pages[index]?.id;
|
||||||
if (context.state.fitMode === "width" && context.state.pageLayout === 1) {
|
if (context.state.fitMode === "width" && context.state.pageLayout === 1) {
|
||||||
context.state.scale = pageView.fitWidthScale(pageView.currentPageInfo());
|
context.state.scale = pageView.fitWidthScale(pageView.currentPageInfo());
|
||||||
@@ -1159,24 +1223,21 @@ export function createPageView(context) {
|
|||||||
if (openSeq !== context.state.openSeq || !context.state.doc) {
|
if (openSeq !== context.state.openSeq || !context.state.doc) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (context.state.pageCache.has(index)) {
|
|
||||||
pageView.updateThumbnail(index, openSeq);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const key = `${openSeq}:${index}`;
|
const key = `${openSeq}:${index}`;
|
||||||
if (context.state.thumbnailInFlight.has(key)) {
|
if (context.state.thumbnailInFlight.has(key)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
context.state.thumbnailInFlight.add(key);
|
context.state.thumbnailInFlight.add(key);
|
||||||
try {
|
try {
|
||||||
const page = await pageView.loadPageData(index, { openSeq, priority: 5, preview: true });
|
const page = await pageView.loadPageData(index, { openSeq, priority: 5, preview: true, view: true });
|
||||||
if (openSeq !== context.state.openSeq) {
|
if (openSeq !== context.state.openSeq) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!page) {
|
if (!page) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pageView.updateThumbnail(index, openSeq);
|
await pageView.queuePagePresentation(index, page, openSeq);
|
||||||
|
if (openSeq !== context.state.openSeq) return;
|
||||||
pageView.trimPageCache();
|
pageView.trimPageCache();
|
||||||
} catch {
|
} catch {
|
||||||
if (openSeq === context.state.openSeq) {
|
if (openSeq === context.state.openSeq) {
|
||||||
|
|||||||
Reference in new issue
Block a user