58 lines
2.1 KiB
JavaScript
58 lines
2.1 KiB
JavaScript
/**
|
|
* 图片纵向合成 —— 把多张图表 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');
|
|
}
|