From f274691ba7fd3b02f5e05f4d3290bb911785af28 Mon Sep 17 00:00:00 2001 From: augushong Date: Tue, 19 May 2026 00:09:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(phone-image):=20codeFontScale+=E4=BB=A3?= =?UTF-8?q?=E7=A2=BC=E5=A1=8A=E9=A0=90=E6=B8=B2=E6=9F=93+=E7=B4=94?= =?UTF-8?q?=E5=9C=96=E7=89=87=E9=A0=81HTML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - config新增codeFontScale:1, applyFontScale新增--pi-code-font-scale CSS變量 - 新增async preRenderCodeBlocks(): 超長代碼塊(>contentAreaHeight)用SnapDOM截圖替換為img - render()在parseHtmlToBlocks之前await preRenderCodeBlocks - saveConfig payload新增codeFontScale - renderPureImageToCanvas標記@deprecated - 純圖片頁在renderDomPages中檢測並添加flex居中樣式 --- public/static/js/phone-image.js | 97 +++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 5 deletions(-) diff --git a/public/static/js/phone-image.js b/public/static/js/phone-image.js index 1d2cc2c..c968e1d 100644 --- a/public/static/js/phone-image.js +++ b/public/static/js/phone-image.js @@ -101,6 +101,7 @@ var PhoneImageEngine = (function () { contentPadding: 20, fontScale: 1, tableFontScale: 1, + codeFontScale: 1, useDomPreview: true }; @@ -172,15 +173,18 @@ var PhoneImageEngine = (function () { function applyFontScale() { var scale = config.fontScale || 1; var tableScale = config.tableFontScale || 1; + var codeScale = config.codeFontScale || 1; var preview = document.getElementById('render-preview'); if (preview) { preview.style.setProperty('--pi-font-scale', scale); preview.style.setProperty('--pi-table-font-scale', tableScale); + preview.style.setProperty('--pi-code-font-scale', codeScale); } var staging = document.getElementById('render-staging'); if (staging) { staging.style.setProperty('--pi-font-scale', scale); staging.style.setProperty('--pi-table-font-scale', tableScale); + staging.style.setProperty('--pi-code-font-scale', codeScale); } } @@ -217,7 +221,10 @@ var PhoneImageEngine = (function () { // 等待预览区内所有图片加载完成,否则 getBoundingClientRect 测高为0 PhoneImageLogPanel.log('等待图片加载...', 'info'); - waitForImages(previewEl).then(function () { + waitForImages(previewEl).then(async function () { + // 预渲染超长代码块为图片 + await preRenderCodeBlocks(contentAreaHeight); + var cleanHtml = previewEl ? previewEl.innerHTML : ''; var blocks = parseHtmlToBlocks(cleanHtml); PhoneImageLogPanel.log('解析内容: ' + blocks.length + ' 个块', 'info'); @@ -1154,7 +1161,9 @@ var PhoneImageEngine = (function () { } /** - * @deprecated 纯图片页已统一走SnapDOM截图流程,此函数不再被调用 + * @deprecated DOM预览模式下不再使用Canvas绘制纯图片页。 + * 纯图片页已统一使用HTML结构(品牌header+居中图片+页脚)渲染。 + * 保留函数供JPEG截图备用路径使用。 * 将原始图片绘制到canvas(用于高质量保存路径) * @param {string} src - 图片src * @param {number} width - 目标宽度 @@ -1328,6 +1337,49 @@ var PhoneImageEngine = (function () { return deferred.promise(); } + /** + * 代码块预渲染:将超长代码块(>contentAreaHeight)用SnapDOM截图替换为img + * 短代码块保持HTML渲染 + */ + async function preRenderCodeBlocks(contentAreaHeight) { + var previewEl = document.getElementById('render-preview'); + if (!previewEl) return; + + var preElements = previewEl.querySelectorAll('pre'); + + for (var i = 0; i < preElements.length; i++) { + var preEl = preElements[i]; + var rect = preEl.getBoundingClientRect(); + + // 只对超出一页高度的代码块预渲染 + if (rect.height > contentAreaHeight) { + try { + var canvas = await snapdom.toCanvas(preEl, { + scale: 2, + backgroundColor: '#f6f8fa' + }); + var dataUrl = canvas.toDataURL('image/png'); + + // 创建替换img元素 + var imgEl = document.createElement('img'); + imgEl.setAttribute('data-converted', 'true'); + imgEl.setAttribute('data-original-width', Math.round(rect.width)); + imgEl.setAttribute('data-original-height', Math.round(rect.height)); + imgEl.setAttribute('src', dataUrl); + imgEl.style.maxWidth = '100%'; + imgEl.style.display = 'block'; + imgEl.style.margin = '10px 0'; + + // 替换原pre元素 + preEl.parentNode.replaceChild(imgEl, preEl); + } catch(e) { + // SnapDOM截图失败时保留原pre + console.warn('Code block pre-render failed:', e); + } + } + } + } + /** * DOM分页预览 - 将pages[]转换为domPages格式 * 替代canvas截图方案,直接操作DOM渲染 @@ -1346,12 +1398,46 @@ var PhoneImageEngine = (function () { // 遍历pages,创建domPage对象 for (var i = 0; i < pages.length; i++) { var page = pages[i]; + var pageHtml = page.html || ''; + var pageType = page.type || 'content'; + + // 检测纯图片页:内容页中 .page-content 内仅有1个img且无文字 + var isPureImage = false; + if (pageType === 'content') { + var $tempDiv = $('
').html(pageHtml); + var $pageContent = $tempDiv.find('.page-content'); + if ($pageContent.length > 0) { + var contentText = $pageContent.text().replace(/\s/g, ''); + if (contentText.length === 0) { + var $imgs = $pageContent.find('img'); + if ($imgs.length === 1) { + var imgSrc = $imgs.first().attr('src'); + if (imgSrc && imgSrc.length > 0 && $imgs.first().attr('data-converted') !== 'true') { + isPureImage = true; + // 为纯图片页调整 .page-content 样式:居中显示图片,保持原始宽高比 + $pageContent.css({ + 'display': 'flex', + 'align-items': 'center', + 'justify-content': 'center' + }); + $imgs.first().css({ + 'max-width': '100%', + 'max-height': '100%', + 'object-fit': 'contain' + }); + pageHtml = $tempDiv.html(); + } + } + } + } + } + var domPage = { - html: page.html || '', - type: page.type || 'content', + html: pageHtml, + type: pageType, pageNum: page.pageNum || (i + 1), valign: (config.pageAlignments && config.pageAlignments[i]) || 'top', - isPureImage: page.type === 'pure-image' + isPureImage: isPureImage }; domPages.push(domPage); } @@ -1678,6 +1764,7 @@ var PhoneImageEngine = (function () { watermark: saveConfig.watermark || config.watermark, fontScale: config.fontScale || 1, tableFontScale: config.tableFontScale || 1, + codeFontScale: config.codeFontScale || 1, pageAlignments: config.pageAlignments || {} }, content_html: saveConfig.content_html || postData.content_html