feat: 增加图片合并功能
This commit is contained in:
+57
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 图片纵向合成 —— 把多张图表 PNG 顺序拼接为一张竖版长图。
|
||||
* 公众号文章在手机上以竖版窗口预览,横版单图显示过小,
|
||||
* 两张上下拼接后(2400×~2800)接近满屏,阅读体验明显更好。
|
||||
* 纯浏览器 Canvas API,无依赖。
|
||||
*/
|
||||
|
||||
const DEFAULTS = { gap: 96, background: '#fff' }; // gap 为设备像素:@2x 导出下 96 = 视觉 48px
|
||||
|
||||
function loadImage(src) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve(img);
|
||||
img.onerror = () => reject(new Error('图片解码失败'));
|
||||
img.src = src;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 按顺序纵向拼接一组图片 dataURL,返回合成后的 canvas。
|
||||
* 宽度取各图最大值;宽度不一致的图等比缩放对齐(正常流程导出的图宽度一致,不会触发缩放)。
|
||||
*/
|
||||
export async function mergeImages(sources, options = {}) {
|
||||
const { gap, background } = { ...DEFAULTS, ...options };
|
||||
if (!sources.length) throw new Error('没有可合并的图片');
|
||||
|
||||
const images = await Promise.all(sources.map(loadImage));
|
||||
const width = Math.max(...images.map((img) => img.width));
|
||||
const heights = images.map((img) => Math.round(img.height * width / img.width));
|
||||
const height = heights.reduce((sum, h) => sum + h, 0) + gap * (images.length - 1);
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = background;
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
let y = 0;
|
||||
images.forEach((img, index) => {
|
||||
ctx.drawImage(img, 0, y, width, heights[index]);
|
||||
y += heights[index] + gap;
|
||||
});
|
||||
return canvas;
|
||||
}
|
||||
|
||||
/** canvas → PNG Blob 触发下载(Blob URL 比超长 dataURL 下载更稳) */
|
||||
export function downloadCanvas(canvas, filename) {
|
||||
canvas.toBlob((blob) => {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const anchor = document.createElement('a');
|
||||
anchor.href = url;
|
||||
anchor.download = filename.endsWith('.png') ? filename : `${filename}.png`;
|
||||
anchor.click();
|
||||
setTimeout(() => URL.revokeObjectURL(url), 10_000);
|
||||
}, 'image/png');
|
||||
}
|
||||
Reference in New Issue
Block a user