| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| function textToImage(options) { |
| const { |
| text, |
| width = 200, |
| height = 200, |
| backgroundColor = 'transparent', |
| fontFamily = 'Arial', |
| fontSize = 16, |
| fontColor = 'black', |
| lineHeight = 1.2, |
| horizontalCenter = false, |
| verticalCenter = false, |
| padding = 10, |
| } = options; |
| |
| const dpr = devicePixelRatio || 1; |
| |
| |
| |
| const canvas = document.createElement('canvas'); |
| const ctx = canvas.getContext('2d'); |
| |
| canvas.width = width * dpr; |
| canvas.height = height * dpr; |
| canvas.style.width = `${width}px`; |
| canvas.style.height = `${height}px`; |
| |
| ctx.scale(dpr, dpr); |
| |
| |
| ctx.fillStyle = backgroundColor; |
| ctx.fillRect(0, 0, width, height); |
| |
| |
| ctx.font = `${fontSize}px ${fontFamily}`; |
| ctx.fillStyle = fontColor; |
| |
| const words = text.split(' '); |
| let line = ''; |
| const lines = []; |
| |
| |
| const maxWidth = width - padding * 2; |
| |
| for (const word of words) { |
| const testLine = line + word + ' '; |
| const metrics = ctx.measureText(testLine); |
| |
| |
| if (metrics.width > maxWidth) { |
| lines.push(line); |
| line = word + ' '; |
| } else { |
| line = testLine; |
| } |
| } |
| lines.push(line); |
| |
| |
| const drawableWidth = width - padding * 2; |
| const drawableHeight = height - padding * 2; |
| |
| |
| |
| const maxLines = Math.floor(drawableHeight / (fontSize * lineHeight)); |
| const displayLines = lines.slice(0, maxLines); |
| |
| let startX = horizontalCenter ? width / 2 : padding; |
| |
| let startY = verticalCenter |
| ? (height - displayLines.length * fontSize * lineHeight) / 2 + (fontSize * lineHeight) / 2 |
| : padding; |
| |
| |
| ctx.textAlign = horizontalCenter ? 'center' : 'left'; |
| ctx.textBaseline = verticalCenter ? 'middle' : 'top'; |
| |
| |
| displayLines.forEach((line, index) => { |
| let truncatedLine = line.trim(); |
| while (ctx.measureText(truncatedLine).width > drawableWidth && truncatedLine.length > 0) { |
| truncatedLine = truncatedLine.slice(0, -1); |
| } |
| |
| ctx.fillText(truncatedLine, startX, startY + index * fontSize * lineHeight); |
| }); |
| |
| queueMicrotask(() => { |
| canvas.remove(); |
| }); |
| |
| return canvas.toDataURL('image/png'); |
| |
| |
| |
| } |
| const base64 = textToImage({ |
| text: 'Hello, World! This is a best d |
| width: 200, |
| height: 200, |
| backgroundColor: 'pink', |
| fontSize: 24, |
| lineHeight: 1.25, |
| horizontalCenter: false, |
| verticalCenter: false, |
| padding: 10, |
| }); |
| let img = new Image(); |
| img.onload = () => { |
| document.body.appendChild(img); |
| }; |
| img.src = base64; |
| |
| <!DOCTYPE html> |
| <html lang="en"> |
| |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| <title>Text to Image</title> |
| <style> |
| img { |
| width: 100px; |
| height: 100px; |
| } |
| </style> |
| </head> |
| |
| <body> |
| <canvas id="canvas"></canvas> |
| <script> |
| function textToImage({ |
| text, |
| font = '14px Arial', // 默认字体 |
| width = 200, // 默认画布宽度 |
| height = 200, // 默认画布高度 |
| backgroundColor = '#FFFFFF', // 默认背景色 |
| textColor = '#000000', // 默认文字颜色 |
| padding = 10, // 默认内边距 |
| align = 'left', // 默认水平居中 |
| valign = 'top', // 默认垂直居中 |
| }) { |
| |
| const canvas = document.getElementById('canvas'); |
| const ctx = canvas.getContext('2d'); |
| |
| const maxWidth = width - 2 * padding; |
| const fontSize = parseInt(font.match(/\d+/)[0] ?? 14); |
| const dpr = window.devicePixelRatio || 1; |
| |
| |
| canvas.width = width * dpr; |
| canvas.height = height * dpr; |
| ctx.scale(dpr, dpr); |
| |
| |
| ctx.fillStyle = backgroundColor; |
| ctx.fillRect(0, 0, width, height); |
| |
| |
| ctx.font = font; |
| ctx.fillStyle = textColor; |
| ctx.textBaseline = 'top'; |
| |
| |
| let lineWidth = maxWidth || width - 2 * padding; |
| |
| |
| const wordCache = new Map(); |
| |
| |
| const lines = wrapText(ctx, text, lineWidth, wordCache); |
| |
| |
| let textHeight = lines.length * fontSize + (lines.length - 1) * 5; |
| if (textHeight > height - 2 * padding) { |
| |
| lines.length = Math.floor((height - 2 * padding) / (fontSize + 5)); |
| textHeight = lines.length * fontSize + (lines.length - 1) * 5; |
| } |
| |
| |
| const x = align === 'center' ? (width - lineWidth) / 2 : padding; |
| const y = valign === 'middle' ? (height - textHeight) / 2 : padding; |
| |
| |
| for (let i = 0; i < lines.length; i++) { |
| ctx.fillText(lines[i], x, y + i * (fontSize + 5)); |
| } |
| } |
| |
| function wrapText(ctx, text, maxWidth, wordCache) { |
| let lines = []; |
| let currentLine = ''; |
| let words = text.split(' '); |
| |
| |
| for (let i = 0; i < words.length; i++) { |
| const word = words[i]; |
| |
| |
| const isChinese = /[\u4e00-\u9fa5]/.test(word); |
| |
| |
| let wordWidth; |
| if (isChinese) { |
| |
| wordWidth = ctx.measureText(word).width; |
| } else { |
| |
| if (wordCache.has(word)) { |
| wordWidth = wordCache.get(word); |
| } else { |
| wordWidth = ctx.measureText(word).width; |
| wordCache.set(word, wordWidth); |
| } |
| } |
| |
| |
| if (ctx.measureText(currentLine).width + wordWidth > maxWidth && currentLine !== '') { |
| lines.push(currentLine); |
| currentLine = word; |
| } else { |
| currentLine += (currentLine ? ' ' : '') + word; |
| } |
| } |
| |
| |
| if (currentLine) { |
| lines.push(currentLine); |
| } |
| |
| return lines; |
| } |
| |
| |
| textToImage({ |
| text: 'This is a long text example that will wrap when necessary. 中文的文本也可以处理。 This is a long text example that will wrap when necessary. 中文的文本也可以处理。 This is a long text example that will wrap when necessary. 中文的文本也可以处理。 This is a long text example that will wrap when necessary. 中文的文本也可以处理。', |
| font: '14px Arial', |
| width: 100, |
| height: 100, |
| backgroundColor: '#e0e0e0', |
| textColor: '#333', |
| padding: 10, |
| |
| |
| }); |
| </script> |
| </body> |
| |
| </html> |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!