Canvas 动态高度文本图片生成器
先看效果图
上代码:
<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Canvas 动态高度文本图片生成器</title><style>body {font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;margin: 0;padding: 20px;background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);min-height: 100vh;display: flex;flex-direction: column;align-items: center;}.container {background: white;border-radius: 15px;padding: 30px;box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);max-width: 800px;width: 100%;margin-bottom: 30px;}h1 {text-align: center;color: #333;margin-bottom: 30px;font-weight: 300;}.input-area {margin-bottom: 20px;}textarea {width: 100%;height: 120px;padding: 15px;border: 2px solid #e2e8f0;border-radius: 8px;font-size: 16px;resize: vertical;font-family: inherit;box-sizing: border-box;}textarea:focus {outline: none;border-color: #667eea;box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);}.controls {display: flex;gap: 15px;margin-bottom: 20px;flex-wrap: wrap;}button {padding: 12px 24px;border: none;border-radius: 8px;background: #667eea;color: white;cursor: pointer;transition: all 0.3s ease;font-size: 14px;font-weight: 500;}button:hover {background: #5a67d8;transform: translateY(-1px);}button:active {transform: translateY(0);}.preview-area {text-align: center;margin-top: 30px;}.preview-container {display: inline-block;border: 2px dashed #cbd5e0;border-radius: 10px;padding: 20px;background: #f8fafc;margin-bottom: 20px;}.info {margin-top: 15px;padding: 15px;background: #edf2f7;border-radius: 8px;font-size: 14px;color: #4a5568;}.download-btn {background: #48bb78;margin-top: 15px;}.download-btn:hover {background: #38a169;}</style></head><body><div class="container"><h1>Canvas 动态高度文本图片生成器</h1><div class="input-area"><textareaid="textInput"placeholder="请输入要生成图片的文本内容..."oninput="debouncedCalculateAndPreview()">
这是一个示例文本,用于演示Canvas动态高度文本渲染功能。当文本内容超过固定宽度时,会自动换行并计算所需高度。</textarea></div><div class="controls"><button onclick="calculateAndPreview()">立即生成</button><button onclick="changeFontSize('increase')">增大字体</button><button onclick="changeFontSize('decrease')">减小字体</button><button onclick="changeLineHeight('increase')">增加行距</button><button onclick="changeLineHeight('decrease')">减少行距</button><button onclick="toggleTheme()">切换主题</button></div><div class="preview-area"><div class="preview-container"><canvas id="previewCanvas"></canvas></div><div class="info" id="sizeInfo">图片尺寸: 0px × 0px | 行数: 0</div><button class="download-btn" onclick="downloadImage()">下载图片</button></div></div><script>// 配置参数const config = {fixedWidth: 400, // 固定宽度fontSize: 16, // 字体大小lineHeight: 1.5, // 行高倍数padding: 20, // 内边距fontFamily: "'Segoe UI', Tahoma, Geneva, Verdana, sans-serif",textColor: "#2d3748", // 文字颜色backgroundColor: "#ffffff", // 背景颜色borderColor: "#e2e8f0", // 边框颜色};// Canvas 相关变量let canvas = document.getElementById("previewCanvas");let ctx = canvas.getContext("2d");let currentText = "";// 防抖函数function debounce(func, wait) {let timeout;return function executedFunction(...args) {const later = () => {clearTimeout(timeout);func(...args);};clearTimeout(timeout);timeout = setTimeout(later, wait);};}// 设置防抖的预览函数const debouncedCalculateAndPreview = debounce(calculateAndPreview, 300);// 计算文本换行和高度function calculateTextDimensions(text, maxWidth) {// 设置字体以测量文本ctx.font = `${config.fontSize}px ${config.fontFamily}`;const words = text.split("");const lines = [];let currentLine = words[0];let maxLineWidth = 0;// 文本换行算法for (let i = 1; i < words.length; i++) {const testLine = currentLine + words[i];const metrics = ctx.measureText(testLine);const testWidth = metrics.width;if (testWidth > maxWidth && currentLine !== "") {// 当前行已满,添加到行数组lines.push(currentLine);maxLineWidth = Math.max(maxLineWidth,ctx.measureText(currentLine).width);currentLine = words[i];} else {currentLine = testLine;}}// 添加最后一行lines.push(currentLine);maxLineWidth = Math.max(maxLineWidth,ctx.measureText(currentLine).width);console.log("maxLineWidth",maxLineWidth,ctx.measureText(currentLine).width);// 计算总高度const lineHeight = config.fontSize * config.lineHeight;const totalHeight = lines.length * lineHeight + config.padding * 2;return {lines: lines,lineCount: lines.length,totalHeight: totalHeight,maxLineWidth: maxLineWidth,lineHeight: lineHeight,};}// 计算并预览图片function calculateAndPreview() {const text = document.getElementById("textInput").value.trim();currentText = text;if (!text) {canvas.width = config.fixedWidth;canvas.height = 100;ctx.clearRect(0, 0, canvas.width, canvas.height);ctx.fillStyle = config.backgroundColor;ctx.fillRect(0, 0, canvas.width, canvas.height);ctx.fillStyle = "#a0aec0";ctx.font = `${config.fontSize}px ${config.fontFamily}`;ctx.textAlign = "center";ctx.fillText("请输入文本内容", canvas.width / 2, 50);updateSizeInfo(0, 0, 0);return;}// 计算文本尺寸const dimensions = calculateTextDimensions(text,config.fixedWidth - config.padding * 2);// 设置Canvas尺寸canvas.width = config.fixedWidth;canvas.height = dimensions.totalHeight;// 绘制背景ctx.fillStyle = config.backgroundColor;ctx.fillRect(0, 0, canvas.width, canvas.height);// 绘制边框ctx.strokeStyle = config.borderColor;ctx.lineWidth = 1;ctx.strokeRect(0, 0, canvas.width, canvas.height);// 绘制文本ctx.fillStyle = config.textColor;ctx.font = `${config.fontSize}px ${config.fontFamily}`;ctx.textBaseline = "middle";ctx.textAlign = "left";const startY = config.padding + dimensions.lineHeight / 2;dimensions.lines.forEach((line, index) => {const y = startY + index * dimensions.lineHeight;console.log(line, config.padding, y);ctx.fillText(line, config.padding, y);});// 更新尺寸信息updateSizeInfo(canvas.width, canvas.height, dimensions.lineCount);}// 更新尺寸信息显示function updateSizeInfo(width, height, lineCount) {document.getElementById("sizeInfo").textContent = `图片尺寸: ${width}px × ${height}px | 行数: ${lineCount} | 字体: ${config.fontSize}px`;}// 改变字体大小function changeFontSize(action) {if (action === "increase") {config.fontSize = Math.min(config.fontSize + 2, 32);} else {config.fontSize = Math.max(config.fontSize - 2, 12);}calculateAndPreview();}// 改变行高function changeLineHeight(action) {if (action === "increase") {config.lineHeight = Math.min(config.lineHeight + 0.1, 2.5);} else {config.lineHeight = Math.max(config.lineHeight - 0.1, 1.2);}calculateAndPreview();}// 切换主题function toggleTheme() {if (config.backgroundColor === "#ffffff") {// 切换到暗色主题config.backgroundColor = "#2d3748";config.textColor = "#e2e8f0";config.borderColor = "#4a5568";} else {// 切换到亮色主题config.backgroundColor = "#ffffff";config.textColor = "#2d3748";config.borderColor = "#e2e8f0";}calculateAndPreview();}// 下载图片function downloadImage() {if (!currentText) {alert("请先输入文本内容");return;}const link = document.createElement("a");link.download = `text-image-${new Date().getTime()}.png`;link.href = canvas.toDataURL("image/png");link.click();}// 初始化function init() {// 设置初始字体ctx.font = `${config.fontSize}px ${config.fontFamily}`;// 初始渲染calculateAndPreview();// 添加键盘快捷键document.addEventListener("keydown", (e) => {if (e.ctrlKey && e.key === "Enter") {calculateAndPreview();}});}// 页面加载完成后初始化window.addEventListener("load", init);</script></body>
</html>