diff --git a/public/static/js/phone-image.js b/public/static/js/phone-image.js
index a9a97f9..4fc9b09 100644
--- a/public/static/js/phone-image.js
+++ b/public/static/js/phone-image.js
@@ -116,37 +116,43 @@ var PhoneImageEngine = (function () {
// 先渲染内容流(DOM) - 用于实测高度
renderContentFlow(blocks);
- // DOM实测高度 - 等待一帧确保渲染完成
+ // 等待一帧确保中间栏渲染完成
requestAnimationFrame(function () {
- measureBlockHeights(blocks);
+ // 在中间栏中将表格和代码块截图转为图片
+ convertFlowBlocksToImages(blocks).then(function () {
+ // 再等一帧让替换后的img渲染
+ requestAnimationFrame(function () {
+ measureBlockHeights(blocks);
- // 封面页
- pages.push(generateCoverPage(sizeConfig));
+ // 封面页
+ pages.push(generateCoverPage(sizeConfig));
- // 内容分页(使用实测高度)
- var contentPages = paginateContent(blocks, contentAreaHeight, sizeConfig);
- pages = pages.concat(contentPages);
+ // 内容分页(使用实测高度)
+ var contentPages = paginateContent(blocks, contentAreaHeight, sizeConfig);
+ pages = pages.concat(contentPages);
- // 尾页
- pages.push(generateSummaryPage(sizeConfig, pages.length));
+ // 尾页
+ 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') {
- // 在页脚中添加 N/M 格式页码
- pages[i].html = pages[i].html.replace(
- '' + pages[i].pageNum + '',
- '' + (i + 1) + '/' + totalPages + ''
- );
- }
- }
+ // 两遍遍历: 现在知道了总页数, 给每页补上 N/M 页码
+ var totalPages = pages.length;
+ for (var i = 0; i < pages.length; i++) {
+ if (pages[i].type === 'content') {
+ // 在页脚中添加 N/M 格式页码
+ pages[i].html = pages[i].html.replace(
+ '' + pages[i].pageNum + '',
+ '' + (i + 1) + '/' + totalPages + ''
+ );
+ }
+ }
- // 渲染缩略图(右侧)- 异步截图
- renderThumbnails(sizeConfig).then(function () {
- deferred.resolve(pages);
- }).catch(function (err) {
- deferred.reject(err);
+ // 渲染缩略图(右侧)- 异步截图
+ renderThumbnails(sizeConfig).then(function () {
+ deferred.resolve(pages);
+ }).catch(function (err) {
+ deferred.reject(err);
+ });
+ });
});
});
@@ -273,6 +279,12 @@ var PhoneImageEngine = (function () {
block.estimatedHeight = 0;
block.needsMeasurement = true;
break;
+ case 'pre':
+ block.type = 'pre';
+ block.html = $el[0].outerHTML;
+ block.estimatedHeight = 0;
+ block.needsMeasurement = true;
+ break;
case 'img':
// 尝试从data属性计算比例高度, 否则标记需要DOM测量
var imgW = parseInt($el.attr('data-original-width'), 10);
@@ -483,6 +495,11 @@ var PhoneImageEngine = (function () {
return [block];
}
+ // 代码块不拆分,保留原样
+ if (block.type === 'pre') {
+ return [block];
+ }
+
// 文本类块:按句子拆分
var text = '';
var wrapperTag = 'p';
@@ -666,112 +683,6 @@ var PhoneImageEngine = (function () {
return { type: 'summary', html: html };
}
- // ===== DOM渲染 =====
-
- /**
- * 将 #render-staging 中的代码块转为图片
- * 使用 prismjs 高亮 + html2canvas 截图替换
- * @returns {jQuery Deferred} resolves when all code blocks are converted
- */
- function convertCodeBlocks() {
- var deferred = $.Deferred();
- var $staging = $('#render-staging');
-
- // 检查 Prism 是否可用
- if (typeof Prism === 'undefined') {
- deferred.resolve();
- return deferred.promise();
- }
-
- // 给没有语言类的代码块添加默认语言
- $staging.find('pre > code:not([class*="language-"])').addClass('language-plaintext');
-
- // 高亮所有代码块
- $staging.find('pre code').each(function () {
- Prism.highlightElement(this);
- });
-
- // 逐个截图替换,每次重新查找避免 replaceWith 后索引失效
- function convertNext() {
- var $pre = $staging.find('pre').first();
- if ($pre.length === 0) {
- deferred.resolve();
- return;
- }
-
- html2canvas($pre[0], {
- scale: 2,
- useCORS: true,
- backgroundColor: '#f5f5f5',
- logging: false
- }).then(function (canvas) {
- var imgData = canvas.toDataURL('image/jpeg', 0.92);
- var $img = $('
');
- $img.attr('src', imgData);
- $img.css({
- maxWidth: '100%',
- height: 'auto',
- display: 'block',
- margin: '10px 0'
- });
- $pre.replaceWith($img);
- convertNext();
- }).catch(function () {
- // 截图失败则跳过当前元素继续处理下一个
- $pre.remove();
- convertNext();
- });
- }
-
- convertNext();
- return deferred.promise();
- }
-
- /**
- * 将 #render-staging 中的表格转为图片
- * 使用 html2canvas 截图替换(与 convertCodeBlocks 模式相同)
- * @returns {jQuery Deferred} resolves when all tables are converted
- */
- function convertTables() {
- var deferred = $.Deferred();
- var $staging = $('#render-staging');
-
- // 逐个截图替换,每次重新查找避免 replaceWith 后索引失效
- function convertNext() {
- var $table = $staging.find('.page-content table').first();
- if ($table.length === 0) {
- deferred.resolve();
- return;
- }
-
- html2canvas($table[0], {
- scale: 2,
- useCORS: true,
- backgroundColor: '#ffffff',
- logging: false
- }).then(function (canvas) {
- var imgData = canvas.toDataURL('image/jpeg', 0.92);
- var $img = $('
');
- $img.attr('src', imgData);
- $img.css({
- maxWidth: '100%',
- height: 'auto',
- display: 'block',
- margin: '10px 0'
- });
- $table.replaceWith($img);
- convertNext();
- }).catch(function () {
- // 截图失败则跳过当前元素继续处理下一个
- $table.remove();
- convertNext();
- });
- }
-
- convertNext();
- return deferred.promise();
- }
-
// ===== 纯图片页优化 =====
/**
@@ -794,6 +705,9 @@ var PhoneImageEngine = (function () {
var $imgs = $content.find('img');
if ($imgs.length !== 1) return false;
+ // 跳过由表格/代码块转换而来的图片(不应被当作纯图片页处理)
+ if ($imgs.first().attr('data-converted') === 'true') return false;
+
// img必须有有效src
var src = $imgs.first().attr('src');
return src && src.length > 0;
@@ -876,17 +790,8 @@ var PhoneImageEngine = (function () {
requestAnimationFrame(function () {
// html2canvas会跳过visibility:hidden的元素,临时切换为可见
$staging.css({ visibility: 'visible' });
- // 先转换代码块为图片
- convertCodeBlocks().then(function () {
- return convertTables();
- }).then(function () {
- runCaptureLoop($staging, opts, deferred);
- }).catch(function () {
- // 代码块转换失败,继续转换表格并截图流程
- convertTables().then(function () {
- runCaptureLoop($staging, opts, deferred);
- });
- });
+ // 表格和代码块已在中间栏中转为图片,无需再次转换
+ runCaptureLoop($staging, opts, deferred);
});
});
@@ -1062,6 +967,68 @@ var PhoneImageEngine = (function () {
});
}
+ /**
+ * 在中间栏 #content-flow 中将表格和代码块截图转为图片
+ * 在 renderContentFlow(blocks) 之后、measureBlockHeights(blocks) 之前调用
+ * @param {Array} blocks - parseHtmlToBlocks 返回的块数组
+ * @returns {jQuery Deferred} resolves with blocks array
+ */
+ function convertFlowBlocksToImages(blocks) {
+ var deferred = $.Deferred();
+ var $flow = $('#content-flow');
+
+ function convertNextBlock(blockIdx) {
+ // 找到下一个需要转换的 block
+ while (blockIdx < blocks.length) {
+ if (blocks[blockIdx].type === 'table' || blocks[blockIdx].type === 'pre') break;
+ blockIdx++;
+ }
+ if (blockIdx >= blocks.length) {
+ deferred.resolve(blocks);
+ return;
+ }
+
+ var block = blocks[blockIdx];
+ // 在 content-flow 中找到对应的 .content-flow-block
+ var $blockEl = $flow.find('.content-flow-block[data-index="' + blockIdx + '"]');
+ if (!$blockEl.length) {
+ convertNextBlock(blockIdx + 1);
+ return;
+ }
+
+ // 对代码块:先应用 Prism 高亮
+ if (block.type === 'pre' && typeof Prism !== 'undefined') {
+ $blockEl.find('pre > code:not([class*="language-"])').addClass('language-plaintext');
+ $blockEl.find('pre code').each(function () { Prism.highlightElement(this); });
+ }
+
+ // html2canvas 截图
+ var targetEl = $blockEl[0];
+ html2canvas(targetEl, {
+ scale: 2,
+ useCORS: true,
+ backgroundColor: block.type === 'table' ? '#ffffff' : '#f5f5f5',
+ logging: false
+ }).then(function (canvas) {
+ var imgData = canvas.toDataURL('image/jpeg', 0.92);
+ // 替换 block 的 html 和 type
+ block.html = '
';
+ block.type = 'img';
+
+ // 更新中间栏中的显示
+ $blockEl.html(block.html);
+
+ convertNextBlock(blockIdx + 1);
+ }).catch(function () {
+ // 截图失败,保留原样
+ convertNextBlock(blockIdx + 1);
+ });
+ }
+
+ convertNextBlock(0);
+ return deferred.promise();
+ }
+
/**
* DOM实测高度 - 从#content-flow中读取每个块的实际渲染高度
* @param {Array} blocks - parseHtmlToBlocks 返回的块数组