HTML5 canvas ctx.fillText无法实现换行

1. HTML5 Canvas ctx.fillText介绍

HTML5中的Canvas api是一个用于动态生成图像的JavaScript API。在Canvas中,文字也可以被绘制到画布上。其中的ctx.fillText方法用于写出文本。Canvas内的文字和其他元素一样只有样式没有结构,这就意味着渲染途中将会忽略空格与连续空格。但是Canvas的文字渲染除此之外还有一个瑕疵,就是对于仅有一个ctx.fillText方法的情况下无法实现自动换行。

2. Canvas ctx.fillText无法实现换行的原因

Canvas的ctx.fillText()方法指定了在Canvas画布上绘制的文本。此方法可以接受四个参数:字符串的实际文本、所绘制文本的x坐标、y坐标和文本最大宽度。其中,字符串的实际文本是必须的,而另外三个参数是可选的。

ctx.fillText()方法虽然可以将文字绘制到画布上,但区别于其他DOM元素的文字,Canvas中的文字无法自动换行,这也是绘制文本时ctx.fillText()方法所固有的限制。

3. 如何实现Canvas中的文本自动换行

在实现Canvas中的文本自动换行时,所要考虑的最重要的因素是文字的宽度。Canvas不提供任何默认方法来计算一行文字的整体宽度,只能通过让文字设置成可绘制的元素,再通过调用 getWidth() 方法来获取其整体宽度。

3.1 开始实现自动换行的代码

为了实现自动换行效果,首先需要把要绘制的文本内容划分成若干行,这一步的思路是将一串文字分割成若干个可以被容纳的小段,从而使之塞进Canvas的边界内。

下面的代码展示了用ctx.fillText()方法将一行文本绘制到Canvas画布上的基本流程:

let text = "这是要绘制的文字";

let x = 0;

let y = 0;

let ctx = document.getElementById("canvas").getContext("2d");

ctx.font = "15px sans-serif";

ctx.fillText(text, x, y);

但是,上述代码绘制出来的文字是连续的,而不是分段的。需要用到split()方法将字符串文本分割成若干行文本。

3.2 使用split()方法进行文本分割

为了让文字能够实现自动换行,需要将大段的字符串文本分割成若干个小段,从而使之可以塞入Canvas画布内。split()方法是JavaScript原生提供的字符串分割函数,可以将一个字符串拆分成若干个不同的小段。

在使用该方法时,我们需要根据所规定的分割字符将一段字符串文本切分成一个文本数组。常见的字符串分割方法有以下两种:

1. 通过空格进行字符串分割

let splitText = text.split(' ');

使用一个空格作为分隔符,将一串文本分割成多个部分,每部分之间以单个空格进行分割,最终生成一个文本数组。

2. 通过字符数进行字符串分割

let limit = 10;

let splitTextArr = [];

while (text.length > limit) {

let chunk = text.slice(0, limit);

let lastSpace = chunk.lastIndexOf(' ');

if (lastSpace === -1) {

lastSpace = limit;

}

let splitPoint = lastSpace + 1;

let splitMsg = text.slice(0, splitPoint);

splitTextArr.push(splitMsg);

text = text.slice(splitPoint);

}

if (text.length) {

splitTextArr.push(text);

}

通过该方法需要定义一个字符数的上限,循环不断地对文本进行切割,找到每一次切割点后的小段文本,直到全部文本被切割为止。

3.3 实现 Canvas 中的文本自动换行

为了实现文字的自动换行效果,可以使用一个循环,将所有分割出的小段文本逐个绘制在 Canvas 上。

let text = "这是要绘制的文字";

let x = 0;

let y = 0;

let ctx = document.getElementById("canvas").getContext("2d");

ctx.font = "15px sans-serif";

let splitTextArr = text.split(' ');

for (let i = 0, lastY = 0; i < splitTextArr.length; i++) {

let line = splitTextArr[i];

let measuredWidth = ctx.measureText(line).width;

if (x != 0 && measuredWidth > 150) {

ctx.fillText(line, x, lastY + 20);

x = 0;

y += 20;

} else {

ctx.fillText(line, x, y);

x += measuredWidth;

}

lastY = y;

}

在上述代码中,首先利用 split() 方法将一整段文本切割成多行小段文本,然后通过一个 for 循环对每一行文本进行遍历。之后,再对每一行文本进行计算,找到文本的宽度,判断这行文本是否可以塞进 Canvas 内部。如果不能,则换行。若绘制下一行时文字的 x 坐标无法容纳该行内容,则将 x 置为 0,y 坐标向下移动,溢出的文本需要移动到下一行绘制。

4. 总结

Canvas 虽然是一个非常强大的工具,但是在使用时也存在很多限制。Canvas 中的文字无法自动换行是其中之一。但实际场景中,自动换行的要求还是比较常见的。通过自定义一些实用的函数,可以在 Canvas 中实现自动换行的效果。

文章中实现的效果代码可以参考以下代码:

let text = "这是要绘制的文字,这行文字可能非常长,所以需要进行换行,从而实现更好的展示效果。";

let x = 0;

let y = 0;

let ctx = document.getElementById("canvas").getContext("2d");

ctx.font = "15px sans-serif";

let splitTextArr = text.split(' ');

for (let i = 0, lastY = 0; i < splitTextArr.length; i++) {

let line = splitTextArr[i];

let measuredWidth = ctx.measureText(line).width;

if (x != 0 && measuredWidth > 150) {

ctx.fillText(line, x, lastY + 20);

x = 0;

y += 20;

} else {

ctx.fillText(line, x, y);

x += measuredWidth;

}

lastY = y;

}