feat(phone-image): code blocks converted to images via prismjs + html2canvas

This commit is contained in:
augushong
2026-05-03 09:19:31 +08:00
parent f3f209c403
commit 9dc37283a3

View File

@@ -621,6 +621,75 @@ var PhoneImageEngine = (function () {
// ===== DOM渲染 ===== // ===== 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);
});
// 找到所有代码块,逐个截图替换
var $preBlocks = $staging.find('pre');
if ($preBlocks.length === 0) {
deferred.resolve();
return deferred.promise();
}
var idx = 0;
var total = $preBlocks.length;
function convertNext() {
if (idx >= total) {
deferred.resolve();
return;
}
var $pre = $preBlocks.eq(idx);
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>');
$img.attr('src', imgData);
$img.css({
maxWidth: '100%',
height: 'auto',
display: 'block',
margin: '10px 0'
});
$pre.replaceWith($img);
idx++;
convertNext();
}).catch(function () {
// 截图失败则保留原始代码块
idx++;
convertNext();
});
}
convertNext();
return deferred.promise();
}
/** /**
* 渲染到隐藏区域 → 截图 → 显示缩略图 * 渲染到隐藏区域 → 截图 → 显示缩略图
* @param {Object} sizeConfig * @param {Object} sizeConfig
@@ -640,44 +709,86 @@ var PhoneImageEngine = (function () {
// 等待一帧确保 DOM 渲染完成 // 等待一帧确保 DOM 渲染完成
requestAnimationFrame(function () { requestAnimationFrame(function () {
var $pageElems = $staging.find('.phone-image-page'); // 先转换代码块为图片
if ($pageElems.length === 0) { convertCodeBlocks().then(function () {
displayThumbnails([], sizeConfig); var $pageElems = $staging.find('.phone-image-page');
deferred.resolve(pages); if ($pageElems.length === 0) {
return; displayThumbnails([], sizeConfig);
}
// 串行截图每一页
var thumbnailDataUrls = [];
var idx = 0;
var total = $pageElems.length;
function captureNext() {
if (idx >= total) {
$staging.empty();
displayThumbnails(thumbnailDataUrls, sizeConfig);
deferred.resolve(pages); deferred.resolve(pages);
return; return;
} }
var $elem = $pageElems.eq(idx); // 串行截图每一页
html2canvas($elem[0], { var thumbnailDataUrls = [];
scale: 1, var idx = 0;
useCORS: true, var total = $pageElems.length;
backgroundColor: '#ffffff',
width: $elem.outerWidth(),
height: $elem.outerHeight(),
logging: false
}).then(function (canvas) {
thumbnailDataUrls.push(canvas.toDataURL('image/jpeg', 0.85));
idx++;
captureNext();
}).catch(function (err) {
deferred.reject('截图失败(第' + (idx + 1) + '页): ' + err);
});
}
captureNext(); function captureNext() {
if (idx >= total) {
$staging.empty();
displayThumbnails(thumbnailDataUrls, sizeConfig);
deferred.resolve(pages);
return;
}
var $elem = $pageElems.eq(idx);
html2canvas($elem[0], {
scale: 1,
useCORS: true,
backgroundColor: '#ffffff',
width: $elem.outerWidth(),
height: $elem.outerHeight(),
logging: false
}).then(function (canvas) {
thumbnailDataUrls.push(canvas.toDataURL('image/jpeg', 0.85));
idx++;
captureNext();
}).catch(function (err) {
deferred.reject('截图失败(第' + (idx + 1) + '页): ' + err);
});
}
captureNext();
}).catch(function () {
// 代码块转换失败,继续截图流程
var $pageElems = $staging.find('.phone-image-page');
if ($pageElems.length === 0) {
displayThumbnails([], sizeConfig);
deferred.resolve(pages);
return;
}
var thumbnailDataUrls = [];
var idx = 0;
var total = $pageElems.length;
function captureNext() {
if (idx >= total) {
$staging.empty();
displayThumbnails(thumbnailDataUrls, sizeConfig);
deferred.resolve(pages);
return;
}
var $elem = $pageElems.eq(idx);
html2canvas($elem[0], {
scale: 1,
useCORS: true,
backgroundColor: '#ffffff',
width: $elem.outerWidth(),
height: $elem.outerHeight(),
logging: false
}).then(function (canvas) {
thumbnailDataUrls.push(canvas.toDataURL('image/jpeg', 0.85));
idx++;
captureNext();
}).catch(function (err) {
deferred.reject('截图失败(第' + (idx + 1) + '页): ' + err);
});
}
captureNext();
});
}); });
return deferred.promise(); return deferred.promise();
@@ -750,40 +861,79 @@ var PhoneImageEngine = (function () {
$staging.append($wrapper); $staging.append($wrapper);
requestAnimationFrame(function () { requestAnimationFrame(function () {
var $pageElems = $staging.find('.phone-image-page'); // 先转换代码块为图片
if ($pageElems.length === 0) { convertCodeBlocks().then(function () {
deferred.reject('没有可渲染的页面'); var $pageElems = $staging.find('.phone-image-page');
return; if ($pageElems.length === 0) {
} deferred.reject('没有可渲染的页面');
var canvases = [];
var idx = 0;
var total = $pageElems.length;
function captureNext() {
if (idx >= total) {
$staging.empty();
deferred.resolve(canvases);
return; return;
} }
html2canvas($pageElems.eq(idx)[0], { var canvases = [];
scale: 2, var idx = 0;
useCORS: true, var total = $pageElems.length;
backgroundColor: '#ffffff',
width: $pageElems.eq(idx).outerWidth(),
height: $pageElems.eq(idx).outerHeight(),
logging: false
}).then(function (canvas) {
canvases.push(canvas);
idx++;
captureNext();
}).catch(function (err) {
deferred.reject('截图失败(第' + (idx + 1) + '页): ' + err);
});
}
captureNext(); function captureNext() {
if (idx >= total) {
$staging.empty();
deferred.resolve(canvases);
return;
}
html2canvas($pageElems.eq(idx)[0], {
scale: 2,
useCORS: true,
backgroundColor: '#ffffff',
width: $pageElems.eq(idx).outerWidth(),
height: $pageElems.eq(idx).outerHeight(),
logging: false
}).then(function (canvas) {
canvases.push(canvas);
idx++;
captureNext();
}).catch(function (err) {
deferred.reject('截图失败(第' + (idx + 1) + '页): ' + err);
});
}
captureNext();
}).catch(function () {
// 代码块转换失败,继续截图流程
var $pageElems = $staging.find('.phone-image-page');
if ($pageElems.length === 0) {
deferred.reject('没有可渲染的页面');
return;
}
var canvases = [];
var idx = 0;
var total = $pageElems.length;
function captureNext() {
if (idx >= total) {
$staging.empty();
deferred.resolve(canvases);
return;
}
html2canvas($pageElems.eq(idx)[0], {
scale: 2,
useCORS: true,
backgroundColor: '#ffffff',
width: $pageElems.eq(idx).outerWidth(),
height: $pageElems.eq(idx).outerHeight(),
logging: false
}).then(function (canvas) {
canvases.push(canvas);
idx++;
captureNext();
}).catch(function (err) {
deferred.reject('截图失败(第' + (idx + 1) + '页): ' + err);
});
}
captureNext();
});
}); });
return deferred.promise(); return deferred.promise();