From 37259cfb4b215985b3f2ed55e4b708882ffceda2 Mon Sep 17 00:00:00 2001 From: augushong Date: Wed, 13 May 2026 12:35:10 +0800 Subject: [PATCH] =?UTF-8?q?fix(phoneimage):=20=E4=BF=AE=E5=A4=8D=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E5=88=86=E9=A1=B5=E6=88=AA=E6=96=AD=20-=20=E6=B5=8B?= =?UTF-8?q?=E9=AB=98=E5=8C=85=E5=90=ABmargin=E5=B9=B6=E7=AD=89=E5=BE=85?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/static/js/phone-image.js | 141 ++++++++++++++++++++------------ 1 file changed, 87 insertions(+), 54 deletions(-) diff --git a/public/static/js/phone-image.js b/public/static/js/phone-image.js index 657e09a..476337d 100644 --- a/public/static/js/phone-image.js +++ b/public/static/js/phone-image.js @@ -98,52 +98,56 @@ var PhoneImageEngine = (function () { // 从预览区读取已预处理的内容 syncPreview(); var previewEl = document.getElementById('render-preview'); - var cleanHtml = previewEl ? previewEl.innerHTML : ''; - var blocks = parseHtmlToBlocks(cleanHtml); - // 空内容检测 - if (blocks.length === 0) { - $('#paginated-preview').html('
文章正文内容为空
'); - render._locked = false; - return deferred.resolve([]).promise(); - } + // 等待预览区内所有图片加载完成,否则 getBoundingClientRect 测高为0 + waitForImages(previewEl).then(function () { + var cleanHtml = previewEl ? previewEl.innerHTML : ''; + var blocks = parseHtmlToBlocks(cleanHtml); - // 封面页 - pages.push(generateCoverPage(sizeConfig)); - - // 内容分页(使用 captureEditorBlocks,T3 会完善截图逻辑) - captureEditorBlocks(cleanHtml, blocks, contentAreaHeight, sizeConfig).then(function(contentPages) { - pages = pages.concat(contentPages); - - // 尾页 - pages.push(generateSummaryPage(sizeConfig, pages.length)); - - // 页码 N/M - var totalPages = pages.length; - for (var i = 0; i < pages.length; i++) { - if (pages[i].type === 'content') { - pages[i].html = pages[i].html.replace( - '' + pages[i].pageNum + '', - '' + (i + 1) + '/' + totalPages + '' - ); - } + // 空内容检测 + if (blocks.length === 0) { + $('#paginated-preview').html('
文章正文内容为空
'); + render._locked = false; + return deferred.resolve([]).promise(); } - // 渲染缩略图 - renderThumbnails(sizeConfig).then(function() { - render._locked = false; - if (render._pending) { - render._pending = false; - render().catch(function() {}); + // 封面页 + pages.push(generateCoverPage(sizeConfig)); + + // 内容分页 + captureEditorBlocks(cleanHtml, blocks, contentAreaHeight, sizeConfig).then(function(contentPages) { + pages = pages.concat(contentPages); + + // 尾页 + pages.push(generateSummaryPage(sizeConfig, pages.length)); + + // 页码 N/M + var totalPages = pages.length; + for (var i = 0; i < pages.length; i++) { + if (pages[i].type === 'content') { + pages[i].html = pages[i].html.replace( + '' + pages[i].pageNum + '', + '' + (i + 1) + '/' + totalPages + '' + ); + } } - deferred.resolve(pages); + + // 渲染缩略图 + renderThumbnails(sizeConfig).then(function() { + render._locked = false; + if (render._pending) { + render._pending = false; + render().catch(function() {}); + } + deferred.resolve(pages); + }).catch(function(err) { + render._locked = false; + deferred.reject(err); + }); }).catch(function(err) { render._locked = false; deferred.reject(err); }); - }).catch(function(err) { - render._locked = false; - deferred.reject(err); }); return deferred.promise(); @@ -204,6 +208,44 @@ var PhoneImageEngine = (function () { $('#render-preview').html(cleanHtml); } + /** + * 等待容器内所有图片加载完成 + * 图片未加载时 getBoundingClientRect() 高度为0,导致分页计算错误 + * @param {HTMLElement} containerEl + * @returns {jQuery Deferred} + */ + function waitForImages(containerEl) { + var deferred = $.Deferred(); + var imgs = containerEl ? containerEl.querySelectorAll('img') : []; + if (imgs.length === 0) { + return deferred.resolve().promise(); + } + var loaded = 0; + var total = imgs.length; + var timer = setTimeout(function () { + // 超时保底:3秒后强制继续 + deferred.resolve(); + }, 3000); + + function checkDone() { + loaded++; + if (loaded >= total) { + clearTimeout(timer); + deferred.resolve(); + } + } + + for (var i = 0; i < imgs.length; i++) { + if (imgs[i].complete && imgs[i].naturalHeight > 0) { + checkDone(); + } else { + imgs[i].onload = checkDone; + imgs[i].onerror = checkDone; + } + } + return deferred.promise(); + } + /** * 将HTML解析为块级元素数组 * 每个块: { type, html, estimatedHeight } @@ -392,30 +434,21 @@ var PhoneImageEngine = (function () { var previewChildren = Array.prototype.slice.call(previewEl.children); - // 逐组测高 + // 逐 block 精确测高(包含 margin,否则分页偏小导致内容溢出) for (var gi = 0; gi < groups.length; gi++) { var group = groups[gi]; - var groupHeight = 0; - for (var j = 0; j < group.domIndices.length; j++) { + for (var j = 0; j < group.blocks.length; j++) { var di = group.domIndices[j]; if (di < previewChildren.length) { - var rect = previewChildren[di].getBoundingClientRect(); - groupHeight += Math.round(rect.height); + var el = previewChildren[di]; + var rect = el.getBoundingClientRect(); + var cs = window.getComputedStyle(el); + var mt = parseFloat(cs.marginTop) || 0; + var mb = parseFloat(cs.marginBottom) || 0; + group.blocks[j].estimatedHeight = Math.round(rect.height + mt + mb); } } - - if (groupHeight === 0) groupHeight = group.blocks.length * 40; - - // 按比例分配测量的实际高度给组内各个 block - var totalEstimated = 0; - for (var k = 0; k < group.blocks.length; k++) { - totalEstimated += (group.blocks[k].estimatedHeight || 40); - } - for (var k = 0; k < group.blocks.length; k++) { - var ratio = (group.blocks[k].estimatedHeight || 40) / (totalEstimated || 1); - group.blocks[k].estimatedHeight = Math.round(groupHeight * ratio); - } } // 给尚未设置高度的 block 设默认值