/**
* PhoneImageEngine - 手机图片排版引擎
*
* 将文章HTML内容自动分页并渲染为手机尺寸图片
* 依赖: jQuery, html2canvas (项目已有)
*
* 渲染宽度540px, html2canvas scale=2 输出1080px
* 小红书: 540x720 (输出1080x1440)
* 抖音: 540x960 (输出1080x1920)
*/
var PhoneImageEngine = (function () {
// ===== 配置 =====
var config = {
size: 'xiaohongshu',
watermark: '',
pageAlignments: {}, // key=页码(1-based), value='top'|'center'
sizes: {
xiaohongshu: { width: 540, height: 720 },
douyin: { width: 540, height: 960 }
},
contentPadding: 20
};
// ===== 文章数据 =====
var postData = {
id: 0,
title: '',
desc: '',
coverText: '', // 封面文案
content_html: '',
poster: '',
author_name: '',
create_time: '',
category_name: ''
};
// ===== 分页结果 =====
var pages = [];
/**
* 确保隐藏渲染区域存在
*/
function ensureStaging() {
if ($('#render-staging').length === 0) {
$('
').css({
position: 'fixed',
left: '-9999px',
top: 0,
visibility: 'hidden'
}).appendTo('body');
}
}
/**
* 初始化引擎
* @param {Object} options - {postId, title, desc, coverText, contentHtml, poster, authorName, createTime, categoryName}
* @param {Object} userConfig - {size, fontSize, watermark}
*/
function init(options, userConfig) {
postData.id = options.postId || 0;
postData.title = options.title || '';
postData.desc = options.desc || '';
postData.coverText = options.coverText || '';
postData.content_html = options.contentHtml || '';
postData.poster = options.poster || '';
postData.author_name = options.authorName || '';
postData.create_time = options.createTime || '';
postData.category_name = options.categoryName || '';
if (userConfig) {
$.extend(config, userConfig);
}
}
/**
* 渲染排版预览 - 生成分页后的HTML
* 管线: editorHtml -> parseHtmlToBlocks -> captureEditorBlocks -> generatePages -> renderThumbnails
* 带并发锁:防止多次 render 同时执行造成数据混乱
* @returns {jQuery Deferred} resolves with pages array
*/
function render() {
var deferred = $.Deferred();
// 防止并发渲染
if (render._locked) {
// 标记有待处理的渲染请求,当前渲染完成后会自动重试
render._pending = true;
return deferred.reject('rendering').promise();
}
render._locked = true;
pages = [];
var sizeConfig = config.sizes[config.size] || config.sizes.xiaohongshu;
var pageHeight = sizeConfig.height;
var contentAreaHeight = pageHeight - (config.contentPadding * 2);
// 从 wangeditor 读取内容
var editorHtml = window.phoneImageEditor ? window.phoneImageEditor.getHtml() : postData.content_html;
var cleanHtml = preprocessContent(editorHtml);
var blocks = parseHtmlToBlocks(cleanHtml);
// 空内容检测
if (blocks.length === 0) {
$('#paginated-preview').html('
文章正文内容为空
');
render._locked = false;
return deferred.resolve([]).promise();
}
// 封面页
pages.push(generateCoverPage(sizeConfig));
// 内容分页(使用 captureEditorBlocks,T3 会完善截图逻辑)
captureEditorBlocks(editorHtml, 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 + ''
);
}
}
// 渲染缩略图
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);
});
return deferred.promise();
}
/**
* 预处理HTML内容
*/
function preprocessContent(html) {
if (!html) return '';
html = html.replace(/