Meme 生成器背后的技术架构
Source: Dev.to
Canvas 渲染管线
从本质上讲,表情包生成器是一个两层合成系统:背景图像和文字覆盖层。HTML5 Canvas API 提供了所需的一切。
async function generateMeme(imageUrl, topText, bottomText) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.crossOrigin = 'anonymous';
return new Promise((resolve) => {
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;
// Draw background image
ctx.drawImage(img, 0, 0);
// Configure text style
ctx.fillStyle = 'white';
ctx.strokeStyle = 'black';
ctx.lineWidth = img.width / 200;
ctx.textAlign = 'center';
ctx.font = `bold ${img.width / 12}px Impact`;
// Top text
ctx.strokeText(topText, canvas.width / 2, img.width / 8);
ctx.fillText(topText, canvas.width / 2, img.width / 8);
// Bottom text
ctx.strokeText(bottomText, canvas.width / 2, img.height - img.width / 20);
ctx.fillText(bottomText, canvas.width / 2, img.height - img.width / 20);
resolve(canvas.toDataURL('image/png'));
};
img.src = imageUrl;
});
}为什么 Impact 字体是标准
Impact 字体之所以成为表情包的标准,并非出于设计选择,而是因为其可用性。自 Windows 98 起,它随每个 Windows 安装一起提供,成为最普遍可用的粗体紧凑无衬线字体。其粗重的笔画和紧凑的间距使其在小尺寸、覆盖在繁杂图像上时仍易读。
白色文字加黑色描边的技巧确保在任何背景上都具可读性。先使用 strokeText 绘制描边,再用 fillText 绘制填充。这种两遍绘制的方法产生了典型的表情包文字外观。
Canvas 上的文字换行
Canvas 的 fillText 不会自动换行。如果文字宽度超过图像,会溢出。需要手动进行换行:
function wrapText(ctx, text, maxWidth) {
const words = text.split(' ');
const lines = [];
let currentLine = words[0];
for (let i = 1; i < words.length; i++) {
const testLine = currentLine + ' ' + words[i];
if (ctx.measureText(testLine).width > maxWidth) {
lines.push(currentLine);
currentLine = words[i];
} else {
currentLine = testLine;
}
}
lines.push(currentLine);
return lines;
}measureText 方法返回当前字体上下文中字符串的像素宽度。这是确定换行的唯一可靠方式,因为字符宽度随字体而异。
动态字体大小
对于一个精致的表情包生成器,字体大小应随文字长度而自适应。短文字使用大号字体,长文字则使用较小的字体以适应图像。
function fitFontSize(ctx, text, maxWidth, maxSize, minSize) {
for (let size = maxSize; size >= minSize; size -= 2) {
ctx.font = `bold ${size}px Impact`;
if (ctx.measureText(text).width <= maxWidth) {
return size;
}
}
return minSize;
}它处理文字换行、动态尺寸,并使用上述 Canvas 管道导出干净的 PNG。