update at 2025-10-09 14:46:24
This commit is contained in:
@@ -22,13 +22,14 @@ function parseAspectRatio(ratio: string): { width: number; height: number } {
|
||||
return { width: 3, height: 4 };
|
||||
}
|
||||
|
||||
const PAGE_PADDING = 40; // 与 renderPage 保持一致的页面内边距
|
||||
|
||||
/**
|
||||
* 计算目标页面高度
|
||||
*/
|
||||
function getTargetPageHeight(settings: NMPSettings): number {
|
||||
const ratio = parseAspectRatio(settings.sliceImageAspectRatio);
|
||||
const height = Math.round((settings.sliceImageWidth * ratio.height) / ratio.width);
|
||||
console.log(`[paginator] 计算页面高度: 宽度=${settings.sliceImageWidth}, 比例=${settings.sliceImageAspectRatio} (${ratio.width}:${ratio.height}), 高度=${height}`);
|
||||
return height;
|
||||
}
|
||||
|
||||
@@ -56,17 +57,38 @@ export async function paginateArticle(
|
||||
): Promise<PageInfo[]> {
|
||||
const pageHeight = getTargetPageHeight(settings);
|
||||
const pageWidth = settings.sliceImageWidth;
|
||||
|
||||
// 创建临时容器用于测量
|
||||
const measureContainer = document.createElement('div');
|
||||
measureContainer.style.cssText = `
|
||||
|
||||
// 创建临时测量容器:与实际页面一致的宽度与内边距
|
||||
const measureHost = document.createElement('div');
|
||||
measureHost.style.cssText = `
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
top: 0;
|
||||
width: ${pageWidth}px;
|
||||
visibility: hidden;
|
||||
box-sizing: border-box;
|
||||
`;
|
||||
document.body.appendChild(measureContainer);
|
||||
document.body.appendChild(measureHost);
|
||||
|
||||
const measurePage = document.createElement('div');
|
||||
measurePage.className = 'xhs-page';
|
||||
measurePage.style.boxSizing = 'border-box';
|
||||
measurePage.style.width = `${pageWidth}px`;
|
||||
measurePage.style.padding = `${PAGE_PADDING}px`;
|
||||
measurePage.style.background = 'white';
|
||||
measurePage.style.position = 'relative';
|
||||
measureHost.appendChild(measurePage);
|
||||
|
||||
const measureContent = document.createElement('div');
|
||||
measureContent.className = 'xhs-page-content';
|
||||
measurePage.appendChild(measureContent);
|
||||
if (articleElement.classList.length > 0) {
|
||||
measureContent.classList.add(...Array.from(articleElement.classList));
|
||||
}
|
||||
const measuredFontSize = window.getComputedStyle(articleElement).fontSize;
|
||||
if (measuredFontSize) {
|
||||
measureContent.style.fontSize = measuredFontSize;
|
||||
}
|
||||
|
||||
const pages: PageInfo[] = [];
|
||||
let currentPageContent: Element[] = [];
|
||||
@@ -79,51 +101,45 @@ export async function paginateArticle(
|
||||
|
||||
for (const child of children) {
|
||||
const childClone = child.cloneNode(true) as HTMLElement;
|
||||
measureContainer.innerHTML = '';
|
||||
measureContainer.appendChild(childClone);
|
||||
|
||||
// 等待浏览器完成渲染
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
|
||||
const childHeight = childClone.offsetHeight;
|
||||
const isIndivisible = isIndivisibleElement(child);
|
||||
measureContent.appendChild(childClone);
|
||||
|
||||
// 判断是否需要换页
|
||||
if (currentPageHeight + childHeight > pageHeight && currentPageContent.length > 0) {
|
||||
// 如果是不可分割元素且加入后会超出,先保存当前页
|
||||
if (isIndivisible) {
|
||||
pages.push({
|
||||
index: pageIndex++,
|
||||
content: wrapPageContent(currentPageContent),
|
||||
height: currentPageHeight
|
||||
});
|
||||
currentPageContent = [child];
|
||||
currentPageHeight = childHeight;
|
||||
} else {
|
||||
// 可分割元素(段落等),尝试加入当前页
|
||||
if (currentPageHeight + childHeight <= pageHeight * 1.1) {
|
||||
// 允许 10% 的溢出容差
|
||||
currentPageContent.push(child);
|
||||
currentPageHeight += childHeight;
|
||||
} else {
|
||||
// 超出太多,换页
|
||||
pages.push({
|
||||
index: pageIndex++,
|
||||
content: wrapPageContent(currentPageContent),
|
||||
height: currentPageHeight
|
||||
});
|
||||
currentPageContent = [child];
|
||||
currentPageHeight = childHeight;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 加入当前页
|
||||
await waitForLayout();
|
||||
|
||||
const totalHeight = measurePage.scrollHeight;
|
||||
const isIndivisible = isIndivisibleElement(child);
|
||||
const fitsCurrentPage =
|
||||
totalHeight <= pageHeight ||
|
||||
(!isIndivisible && totalHeight <= pageHeight * 1.1) ||
|
||||
currentPageContent.length === 0;
|
||||
|
||||
if (fitsCurrentPage) {
|
||||
currentPageContent.push(child);
|
||||
currentPageHeight += childHeight;
|
||||
currentPageHeight = totalHeight;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 当前页已放不下:移除刚刚加入的克隆节点
|
||||
measureContent.removeChild(childClone);
|
||||
await waitForLayout();
|
||||
|
||||
if (currentPageContent.length > 0) {
|
||||
pages.push({
|
||||
index: pageIndex++,
|
||||
content: wrapPageContent(currentPageContent),
|
||||
height: currentPageHeight
|
||||
});
|
||||
}
|
||||
|
||||
currentPageContent = [child];
|
||||
measureContent.innerHTML = '';
|
||||
const firstClone = child.cloneNode(true) as HTMLElement;
|
||||
measureContent.appendChild(firstClone);
|
||||
await waitForLayout();
|
||||
currentPageHeight = measurePage.scrollHeight;
|
||||
|
||||
// 不可分割元素即使超过高度也直接保留在新页
|
||||
}
|
||||
|
||||
// 保存最后一页
|
||||
if (currentPageContent.length > 0) {
|
||||
pages.push({
|
||||
index: pageIndex,
|
||||
@@ -132,12 +148,15 @@ export async function paginateArticle(
|
||||
});
|
||||
}
|
||||
|
||||
// 清理临时容器
|
||||
document.body.removeChild(measureContainer);
|
||||
document.body.removeChild(measureHost);
|
||||
|
||||
return pages;
|
||||
}
|
||||
|
||||
async function waitForLayout(): Promise<void> {
|
||||
await new Promise<void>(resolve => requestAnimationFrame(() => resolve()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装页面内容为完整的 HTML
|
||||
*/
|
||||
@@ -163,8 +182,6 @@ export function renderPage(
|
||||
const actualPageWidth = settings.sliceImageWidth;
|
||||
const actualPageHeight = getTargetPageHeight(settings);
|
||||
|
||||
console.log(`[renderPage] 渲染页面: 宽=${actualPageWidth}, 高=${actualPageHeight}`);
|
||||
|
||||
container.innerHTML = '';
|
||||
|
||||
// 直接设置为实际尺寸,用于切图
|
||||
@@ -174,7 +191,7 @@ export function renderPage(
|
||||
height: ${actualPageHeight}px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
padding: 40px;
|
||||
padding: ${PAGE_PADDING}px;
|
||||
background: white;
|
||||
`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user