canvas绘制文字的实践记录
1 相关知识点
1.1 canvas绘制文本 api
绘制文字可以使用APIvoid ctx.fillText(text, x, y, [maxWidth])
、void ctx.strokeText(text, x, y [, maxWidth])
,两者的区别在于:前者是填充文本,后者是对文本进行描边。语法:
参数:
参数 | 说明 |
---|---|
text | 文本 |
x | 文本起始点的x轴坐标 |
y | 文本起始点的y轴坐标 |
maxWidth(可选) | 需要限制的最大宽度 |
1.2 计算文字位置
假设一个情境,将文本绘制画布中心。
目前我所知的有3种思路,对文字位置的计算较为方便:
1.设置textAlign为“center”
由上可知,canvas提供的绘制文本的api参数是从起始点坐标开始算的,所以,可直接设置ctx.textAlign = "center"
,textAlign的值为center的时候文本的居中是基于你在fillText的时候所给的值(即,文本一半在x的左边,一半在x的右边,或者说,fillText的参数x不再是从文本的左上角开始算,而是才能从文本水平方向上的中点开始算起)。
问题解决如下:
ctx.textAlign = "center";
ctx.fillText(text, canvas.width / 2, canvas.height / 2);
2.测量文字宽高
首先,canvas提供了一个API,ctx.measureText(text);
,浏览器兼容性很好,它将返回一个TextMetrics对象(表示文本的尺寸),提供了一个width属性可用。不过需要在测量之前先设置ctx.font = fontStyle
。
其次,若需要计算文字的高度,还可以通过dom元素的方法:
function measureText (text, textStyle) {
const d = document.createElement('span');
d.innerText = text;
d.style.font = textStyle;
d.style.visibility = 'hidden'; // 隐藏不可见
d.style.zIndex = -1; // 改变层叠顺序,减少对页面排版的影响
document.querySelector('body').appendChild(d);
return {
width: d.offsetWidth,
height: d.offsetHeight
}
}
问题解决如下:
const { width: textWidth, height: textHeight } = measureText(text, 'normal bolder 12px Microsoft YaHei');
ctx.fillText(text, canvas.width / 2 - textWidth / 2, canvas.height / 2 - textHeight / 2);
3.画布的平移
在已知textWidth、textHeight的前提下,ctx.translate(-textWidth / 2, -textHeight / 2);
问题解决如下:
ctx.translate(-textWidth / 2, -textHeight / 2);
ctx.fillText(text, canvas.width / 2, canvas.height / 2);
1.3 文字换行的实现
目前为止,canvas中并没有刻意让文本自动换行的现成的API。需要人为实现。实现思路如下:设限定宽为W,将文字处理生成多行不超过W的文字数组,然后for循环逐行绘制。
其中,怎么将文字处理生成多行不超过W的文字数组?可以使用二分查找,如果某一个位置pos1之前的文字宽度<=W,并且pos1之后一个字之前的文字宽度>W,那么这个位置就是文本的换行点。若只是找到一个换行点,对于输入的一段文本,需要循环查找,直到不存在这样的换行点为止。
2 附完整代码封装
export default class TextSprite {
constructor () {
this._instance = null
}
// 单例模式
static getInstance () {
if (!this._instance) {
this._instance = new TextSprite()
}
return this._instance
}
/**
* 绘制文字
* @param String data - 文字
* @param obj param1 - 文字样式
* @param {*} ctx - canvas context
*/
renderText (data, { pos, fontColor, textStyle }, ctx, maxWidth) {
ctx.beginPath()
ctx.fillStyle = fontColor
ctx.font = textStyle
if (maxWidth) {
ctx.fillText(data, pos.x, pos.y, maxWidth)
} else {
ctx.fillText(data, pos.x, pos.y)
}
ctx.closePath()
}
/**
* 文字测量
* @param String data - 文字
* @param String textStyle - 文字样式
* @param {*} ctx - canvas context
* @param Boolean isComplete - 是否需要完整信息(含高度)
*/
measureText (data, textStyle, ctx, isComplete = true) {
let obj = {}
if (isComplete) {
const d = document.createElement('span')
d.innerText = data
d.style.font = textStyle
d.style.visibility = 'hidden'
d.style.zIndex = -1;
document.querySelector('body').appendChild(d)
obj.width = d.offsetWidth
obj.height = d.offsetHeight
} else {
// 注意在使用前设置好对应的 font 属性才能准确获取文字的长度
ctx.font = textStyle
obj.width = ctx.measureText(data).width
}
return obj
}
/**
* 绘制多行文本
* @param {*} ctx - canvas context
* @param String data - 文字
* @param Number limitWidth - 限制的宽度
* @param Number lineHeight - 行高
* @param Object {pos, fontColor, textStyle} - {起始位置,字体颜色,文字样式}
*/
drawMultipleLinesOfText (ctx, data, limitWidth, lineHeight, { pos, fontColor, textStyle }) {
ctx.beginPath()
ctx.fillStyle = fontColor
ctx.font = textStyle
const res = breakLinesForCanvas(ctx, data, limitWidth, textStyle);
res.forEach((line, index) => {
ctx.fillText(line, pos.x, pos.y lineHeight * index)
})
}
}
/**
* 二分法查找文字断点
* @param String text - 文字
* @param Number width - 限制的宽度
* @param {*} ctx - canvas context
*/
function findBreakPoint(text, width, ctx) {
let min = 0
let max = text.length - 1
while (min <= max) {
let middle = Math.floor((min max) / 2)
let middleWidth = ctx.measureText(text.substr(0, middle)).width
const oneCharWiderThanMiddleWidth = ctx.measureText(text.substr(0, middle 1)).width
if (middleWidth <= width && oneCharWiderThanMiddleWidth > width) {
return middle
}
if (middleWidth < width) {
min = middle 1
} else {
max = middle - 1
}
}
return -1
}
/**
* 为限定宽度的文本绘制 生成多行文字
* @param {*} ctx - canvas context
* @param String text - 文字
* @param Number width - 限制的宽度
* @param String font - 文字样式
*/
function breakLinesForCanvas (ctx, text, width, font) {
const result = []
let breakPoint = 0
if (font) ctx.font = font
while ((breakPoint = findBreakPoint(text, width, context)) !== -1) {
result.push(text.substr(0, breakPoint))
text = text.substr(breakPoint)
}
if (text) result.push(text)
return result
}
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgbcehg
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01