Canvas 文本绘制
本文档详细介绍 Canvas 的文本绘制功能,包括文本渲染、样式设置、文本测量等内容。
基础文本绘制
Canvas 提供了两个基本的文本绘制方法和一个测量方法。
方法概览
| 方法 | 说明 |
|---|---|
fillText(text, x, y, maxWidth) | 绘制填充文本 |
strokeText(text, x, y, maxWidth) | 绘制描边文本 |
measureText(text) | 测量文本宽度 |
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
text | String | 要绘制的文本 |
x | Number | 文本起始 X 坐标 |
y | Number | 文本起始 Y 坐标(基线位置) |
maxWidth | Number | 可选,文本最大宽度 |
基本用法
javascript
const canvas = document.getElementById('myCanvas')
const ctx = canvas.getContext('2d')
// 绘制填充文本
ctx.font = '30px Arial'
ctx.fillStyle = '#333'
ctx.fillText('Hello Canvas', 50, 50)
// 绘制描边文本
ctx.strokeStyle = '#e74c3c'
ctx.lineWidth = 2
ctx.strokeText('描边文本', 50, 100)限制文本宽度
javascript
// 设置最大宽度,文本会自动缩放
ctx.fillText('这是一段很长的文本会被限制宽度', 50, 150, 200)文本样式
font 属性
font 属性设置文本的字体样式,语法与 CSS font 属性相同。
javascript
ctx.font = 'italic bold 24px Arial, sans-serif'语法格式: font-style font-weight font-size font-family
font-style(字体样式)
javascript
ctx.font = 'normal 20px Arial' // 正常(默认)
ctx.font = 'italic 20px Arial' // 斜体
ctx.font = 'oblique 20px Arial' // 倾斜font-weight(字体粗细)
javascript
ctx.font = 'normal 20px Arial' // 正常(默认)
ctx.font = 'bold 20px Arial' // 粗体
ctx.font = 'lighter 20px Arial' // 更细
ctx.font = 'bolder 20px Arial' // 更粗
ctx.font = '100 20px Arial' // 数字值(100-900)
ctx.font = '400 20px Arial' // 正常
ctx.font = '700 20px Arial' // 粗体font-size(字体大小)
javascript
ctx.font = '16px Arial' // 像素
ctx.font = '1em Arial' // em 单位
ctx.font = '1rem Arial' // rem 单位
ctx.font = '12pt Arial' // 点
ctx.font = '100% Arial' // 百分比font-family(字体族)
javascript
ctx.font = '20px Arial' // 单一字体系列
ctx.font = '20px "Courier New", monospace' // 多个备选字体
ctx.font = '20px Georgia, serif'
ctx.font = '20px "Microsoft YaHei", sans-serif'颜色与样式
javascript
// 纯色文本
ctx.fillStyle = '#3498db'
ctx.fillText('蓝色文本', 50, 50)
// 渐变文本
const gradient = ctx.createLinearGradient(50, 0, 300, 0)
gradient.addColorStop(0, '#e74c3c')
gradient.addColorStop(1, '#f39c12')
ctx.fillStyle = gradient
ctx.fillText('渐变文本', 50, 100)
// 图案文本
const pattern = ctx.createPattern(img, 'repeat')
ctx.fillStyle = pattern
ctx.fillText('图案文本', 50, 150)文本对齐
水平对齐 (textAlign)
textAlign 属性设置文本的水平对齐方式。
javascript
ctx.textAlign = 'start' | 'end' | 'left' | 'right' | 'center'对齐方式说明
code
left: 文本左对齐
center: 文本居中对齐
right: 文本右对齐
start: 根据文本方向对齐(LTR 时等同于 left)
end: 根据文本方向对齐(LTR 时等同于 right)可视化示例
javascript
const canvas = document.getElementById('myCanvas')
const ctx = canvas.getContext('2d')
// 绘制中心线
ctx.strokeStyle = '#ccc'
ctx.moveTo(200, 0)
ctx.lineTo(200, 250)
ctx.stroke()
// 不同对齐方式
const alignments = ['left', 'center', 'right']
ctx.font = '20px Arial'
ctx.fillStyle = '#333'
alignments.forEach((align, index) => {
ctx.textAlign = align
ctx.fillText(`textAlign: ${align}`, 200, 50 + index * 50)
})垂直对齐 (textBaseline)
textBaseline 属性设置文本的垂直对齐方式。
javascript
ctx.textBaseline = 'top' | 'hanging' | 'middle' | 'alphabetic' | 'ideographic' | 'bottom'基线说明
code
top: 文本顶端对齐 Y 坐标
hanging: 悬挂基线(主要用于印度文字)
middle: 文本垂直居中对齐 Y 坐标
alphabetic: 字母基线(默认)
ideographic: 表意文字基线
bottom: 文本底端对齐 Y 坐标可视化示例
javascript
// 绘制基线
ctx.strokeStyle = '#ccc'
ctx.moveTo(0, 100)
ctx.lineTo(400, 100)
ctx.stroke()
const baselines = ['top', 'middle', 'alphabetic', 'bottom']
ctx.font = '20px Arial'
ctx.fillStyle = '#333'
ctx.textAlign = 'left'
baselines.forEach((baseline, index) => {
ctx.textBaseline = baseline
ctx.fillText(`baseline: ${baseline}`, 20 + index * 100, 100)
})组合使用
javascript
// 文本居中(水平 + 垂直)
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillText('居中文本', canvas.width / 2, canvas.height / 2)文本测量
measureText() 方法返回一个 TextMetrics 对象,包含文本的测量信息。
基本用法
javascript
const text = 'Hello Canvas'
const metrics = ctx.measureText(text)
console.log('文本宽度:', metrics.width)TextMetrics 属性
| 属性 | 说明 |
|---|---|
width | 文本的宽度 |
actualBoundingBoxLeft | 从 textAlign 确定的对齐点到文本矩形左边的距离 |
actualBoundingBoxRight | 从 textAlign 确定的对齐点到文本矩形右边的距离 |
actualBoundingBoxAscent | 从 textBaseline 确定的基线到文本矩形顶部的距离 |
actualBoundingBoxDescent | 从 textBaseline 确定的基线到文本矩形底部的距离 |
计算文本高度
javascript
function getTextHeight(text, font) {
ctx.font = font
const metrics = ctx.measureText(text)
return metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent
}
const height = getTextHeight('Hello', '30px Arial')
console.log('文本高度:', height)获取文本边界框
javascript
function getTextBoundingBox(text, x, y) {
const metrics = ctx.measureText(text)
return {
x: x - metrics.actualBoundingBoxLeft,
y: y - metrics.actualBoundingBoxAscent,
width: metrics.actualBoundingBoxLeft + metrics.actualBoundingBoxRight,
height: metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent
}
}
// 使用示例
const text = 'Hello Canvas'
ctx.font = '30px Arial'
const box = getTextBoundingBox(text, 100, 100)
// 绘制文本和边界框
ctx.fillText(text, 100, 100)
ctx.strokeStyle = 'red'
ctx.strokeRect(box.x, box.y, box.width, box.height)文本自适应宽度
javascript
function fitTextToWidth(text, maxWidth, fontSize, fontFamily) {
ctx.font = `${fontSize}px ${fontFamily}`
let width = ctx.measureText(text).width
// 如果宽度超过最大宽度,缩小字体
while (width > maxWidth && fontSize > 10) {
fontSize--
ctx.font = `${fontSize}px ${fontFamily}`
width = ctx.measureText(text).width
}
return fontSize
}
const text = '这是一段需要自适应的文本内容'
const fontSize = fitTextToWidth(text, 300, 30, 'Arial')
ctx.font = `${fontSize}px Arial`
ctx.fillText(text, 50, 100)文本效果
文本阴影
javascript
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)'
ctx.shadowBlur = 5
ctx.shadowOffsetX = 2
ctx.shadowOffsetY = 2
ctx.font = '40px Arial'
ctx.fillStyle = '#333'
ctx.fillText('阴影文本', 50, 100)发光文本
javascript
ctx.shadowColor = '#f39c12'
ctx.shadowBlur = 20
ctx.shadowOffsetX = 0
ctx.shadowOffsetY = 0
ctx.font = 'bold 40px Arial'
ctx.fillStyle = '#f39c12'
ctx.fillText('发光文本', 50, 100)描边文本
javascript
ctx.font = 'bold 40px Arial'
ctx.strokeStyle = '#3498db'
ctx.lineWidth = 3
ctx.strokeText('描边文本', 50, 100)
// 填充 + 描边组合
ctx.fillStyle = '#fff'
ctx.fillText('组合文本', 50, 150)
ctx.strokeText('组合文本', 50, 150)渐变文本
javascript
const gradient = ctx.createLinearGradient(50, 0, 350, 0)
gradient.addColorStop(0, '#e74c3c')
gradient.addColorStop(0.5, '#f39c12')
gradient.addColorStop(1, '#9b59b6')
ctx.font = 'bold 40px Arial'
ctx.fillStyle = gradient
ctx.fillText('渐变文本', 50, 100)3D 文本效果
javascript
function draw3DText(text, x, y, depth, color) {
ctx.font = 'bold 40px Arial'
// 绘制阴影层
for (let i = depth; i > 0; i--) {
ctx.fillStyle = `rgba(0, 0, 0, ${0.1 * (depth - i + 1)})`
ctx.fillText(text, x + i, y + i)
}
// 绘制主文本
ctx.fillStyle = color
ctx.fillText(text, x, y)
}
draw3DText('3D 文本', 50, 100, 5, '#3498db')文本沿路径排列
javascript
function drawTextOnPath(text, pathPoints) {
ctx.font = '20px Arial'
let charIndex = 0
let totalLength = 0
// 计算路径总长度
for (let i = 1; i < pathPoints.length; i++) {
const dx = pathPoints[i].x - pathPoints[i-1].x
const dy = pathPoints[i].y - pathPoints[i-1].y
totalLength += Math.sqrt(dx * dx + dy * dy)
}
const charSpacing = totalLength / text.length
// 沿路径绘制每个字符
let currentPoint = 0
let traveledDistance = 0
for (let i = 0; i < text.length; i++) {
const charWidth = ctx.measureText(text[i]).width / 2
const targetDistance = i * charSpacing + charWidth
// 找到目标距离对应的点
while (currentPoint < pathPoints.length - 1) {
const dx = pathPoints[currentPoint + 1].x - pathPoints[currentPoint].x
const dy = pathPoints[currentPoint + 1].y - pathPoints[currentPoint].y
const segmentLength = Math.sqrt(dx * dx + dy * dy)
if (traveledDistance + segmentLength >= targetDistance) {
const t = (targetDistance - traveledDistance) / segmentLength
const x = pathPoints[currentPoint].x + dx * t
const y = pathPoints[currentPoint].y + dy * t
const angle = Math.atan2(dy, dx)
ctx.save()
ctx.translate(x, y)
ctx.rotate(angle)
ctx.fillText(text[i], -charWidth, 0)
ctx.restore()
break
}
traveledDistance += segmentLength
currentPoint++
}
}
}
// 使用示例
const path = [
{ x: 50, y: 100 },
{ x: 150, y: 50 },
{ x: 250, y: 100 },
{ x: 350, y: 50 }
]
drawTextOnPath('沿路径排列的文本', path)综合示例
文本按钮
javascript
function drawTextButton(text, x, y, width, height) {
// 按钮背景
const gradient = ctx.createLinearGradient(x, y, x, y + height)
gradient.addColorStop(0, '#667eea')
gradient.addColorStop(1, '#764ba2')
ctx.fillStyle = gradient
ctx.beginPath()
ctx.roundRect(x, y, width, height, 8)
ctx.fill()
// 按钮阴影
ctx.shadowColor = 'rgba(0, 0, 0, 0.3)'
ctx.shadowBlur = 10
ctx.shadowOffsetY = 4
// 文本
ctx.font = 'bold 16px Arial'
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillStyle = 'white'
ctx.fillText(text, x + width / 2, y + height / 2)
}
drawTextButton('点击按钮', 100, 100, 150, 50)文本动画(打字机效果)
javascript
let text = '这是打字机效果示例文本'
let currentIndex = 0
function typeWriter() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.font = '30px Arial'
ctx.fillStyle = '#333'
const displayText = text.substring(0, currentIndex)
ctx.fillText(displayText, 50, 100)
// 光标
const metrics = ctx.measureText(displayText)
ctx.fillRect(50 + metrics.width, 70, 2, 30)
if (currentIndex < text.length) {
currentIndex++
setTimeout(typeWriter, 100)
}
}
typeWriter()多行文本
javascript
function drawMultiLineText(text, x, y, maxWidth, lineHeight) {
const words = text.split('')
let line = ''
let lineCount = 0
ctx.font = '20px Arial'
ctx.textBaseline = 'top'
for (let i = 0; i < words.length; i++) {
const testLine = line + words[i]
const metrics = ctx.measureText(testLine)
if (metrics.width > maxWidth && line !== '') {
ctx.fillText(line, x, y + lineCount * lineHeight)
line = words[i]
lineCount++
} else {
line = testLine
}
}
// 绘制最后一行
ctx.fillText(line, x, y + lineCount * lineHeight)
}
const text = '这是一段很长的文本,需要自动换行显示。Canvas 不支持自动换行,需要手动实现换行逻辑。'
drawMultiLineText(text, 50, 50, 300, 30)常见问题
1. 中文字体问题
问题: 中文字体显示不正常。
解决方案: 确保指定了支持中文的字体。
javascript
ctx.font = '20px "Microsoft YaHei", "SimHei", sans-serif'2. 文本模糊
问题: 文本在高 DPI 屏幕上模糊。
解决方案: 根据 devicePixelRatio 调整 Canvas 尺寸。
javascript
const dpr = window.devicePixelRatio || 1
canvas.width = width * dpr
canvas.height = height * dpr
canvas.style.width = width + 'px'
canvas.style.height = height + 'px'
ctx.scale(dpr, dpr)3. 文本换行
问题: Canvas 不支持自动换行。
解决方案: 手动实现换行逻辑(参见多行文本示例)。
下一步学习
返回:Canvas 教程目录 | 上一篇:样式与效果