Canvas学习:绘制文本

发布于 大漠

前面的课程我们学习的都是如何在Canvas中绘制图形,但很多时候,除了绘制图形之外还有很多情景要同文本打交道。不过在Canvas中到目前为止只提供了一些必备的基本功能,例如文本的描边与填充,向Canvas之中放置文本,以及用像素为单位来计算任意字符串的宽度等。接下来的内容,我们就来了解在Canvas中怎么绘制文本以及一些基本的操作。

Canvas的绘图环境对象提供了两种方法来渲染文本:

  • fillText(text, x, y, [maxWidth]):绘制填充文本
  • strokeText(text, x, y, [maxWidth]):绘制描边文本

在Canvas中同样提供了类似CSS的一些font属性,用来修饰在Canvas中绘制的文本,比如:

  • font = value:用来修饰绘制文本的样式,类似于CSS中的font
  • textAlign = value:文本对齐设置
  • textBaseline = value: 文本基线对齐设置
  • direction = value: 文本方向的设置

除此之外,很多时候我们还需要对已绘制文本宽度的度量,在Canvas中提供了一个measureText(text)方法,该方法所返回的对象中,包含一个名为width的属性,它的值代表你传递给该方法的文本所占据的宽度。

在接下来的内容,我们将详细的针对上述内容做相关的阐述。

文本的填充和描边

在Canvas中的CanvasRenderingContext2D对象提供了两个方法来绘制文本:fillText()strokeText()方法。先来看fillText()方法。

fillText()方法用来绘制填充文本,其语法如下:

ctx.fillText(text, x, y, [maxWidth])

fillText()方法接受四个参数:

  • text:需要绘制的文本内容
  • x:指定绘制文本在Canvas画布中起始位置的x轴坐标点
  • y:指定绘制文本在Canvas画布中起始位置的y轴坐标点
  • maxWidth:指定绘制文本的最大宽度

来看个简单的示例:

var text = 'Hello! W3cplus.com !'; // 需要绘制的文本内容
ctx.fillStyle = '#f90'; // 文本颜色
ctx.textAlign = 'center'; // 文本对齐方式  
ctx.font = '48px Airal'; // 文本字号、字体

ctx.fillText(text, w / 2, h / 2);

效果如下:

可以点击这里在CodePen上查看Demo效果

在绘制文本时,同样可以使用fillStyle属性来设置填充文本的颜色。另外,上面的示例,我们只用了其中三个参数,并没有使用maxWidth参数。在fillText()方法中,这个参数是可选参数。这个参数的主要功能是用来控制绘制文本的最大宽度。如果绘制的文本内容超过了maxWidth的宽度的时候,将会压缩文本,让绘制的文本宽度和maxWidth值一样。

来个小示例:

var text = 'Hello! W3cplus.com !';
ctx.fillStyle = '#f90';
ctx.textAlign = 'center';

ctx.font = '48px Airal';

ctx.fillText(text, w / 2, h / 2 - 50, 200);
ctx.fillText(text, w / 2, h / 2 + 50);

效果如下:

可以点击这里在CodePen上查看Demo效果

上示中,绘制的第一个文本,设置了maxWidth,而第二个文本并未设置maxWidth。通过ctx.measureText(text).width)可以得出未设置maxWidth的值是474,也就是说文本Hello! W3cplus.com!474压缩到了200

上面介绍的是fillText()绘制填充文本,在Canvas中还可以通过strokeText()方法来绘制描边文本。该方法具有的参数和使用方法与fillText()一样,只是最终在Canvas中渲染的效果不一样。比如将上示中的fillText()换成strokeText(),同时将fillStyle换成strokeStyle

var text = 'Hello! W3cplus.com !';
ctx.strokeStyle = '#f90';
ctx.textAlign = 'center';

ctx.font = '48px Airal';

ctx.strokeText(text, w / 2, h / 2 - 50, 200);
ctx.strokeText(text, w / 2, h / 2 + 50);

最终效果如下:

可以点击这里在CodePen上查看Demo效果

在实际中,我们可以同时使用fillText()strokeText()方法绘制一个具有填充的边框的文本,这也是我们最常见的描边文本,但它们的起始位置应该在同一个点:

var text = 'Hello! W3cplus.com !';
ctx.strokeStyle = 'blue';
ctx.fillStyle = 'red';
ctx.lineWidth = 2;
ctx.textAlign = 'center';    
ctx.font = '48px Airal';

ctx.fillText(text, w / 2, h / 2);
ctx.strokeText(text, w / 2, h / 2);

最终效果如下:

可以点击这里在CodePen上查看Demo效果

为了便于更灵活的使用fillText()strokeText()绘制文本,可以将其封装成一个函数,比如drawText()

// @param {Object} ctx - CanvasRenderingContext2D
// @param {String} text - 绘制文本的内容
// @param {Number} x - 绘制文本起始点x轴坐标
// @param {Number} y - 绘制文本起始点y轴坐标
// @param {Boolean} isFill - 是否填充,true绘制填充文本,false绘制描边文本
// @param {Boolean} isMaxWidth - 是否设置文本最大宽度, true设置最大宽度, false不设置最大宽度
// @param {String} color - 绘制文本颜色
// @param {Number} maxWidth - 文本最大宽度
function drawText(ctx, text, x, y, isFill, isMaxWidth, color, maxWidth) {
    if (isFill) {
    ctx.fillStyle = color;
        if (isMaxWidth) {
        ctx.fillText(text, x, y, maxWidth);
        } else {
        ctx.fillText(text, x, y);
        }
    } else {
    ctx.strokeStyle = color;
    if (isMaxWidth) {
        ctx.strokeText(text, x, y, maxWidth);
    } else {
        ctx.strokeText(text, x, y);
    }
    }
}

使用的时候,像下面这样调用即可:

drawText(ctx, 'W3cplus.com', w / 2, 40, true, false, 'blue');
drawText(ctx, 'W3cplus.com', w / 2, 80, true, true, 'blue', 100);

设置文本样式

在上面的示例中,我们看到了代码中有ctx.font属性的使用。其实在Canvas中,可以通过绘图环境对象的font属性,来设置绘制在Canvas之中的文本所采用的字型。该属性是一个CSS3格式的字型字符串,它和CSS font属性相同的语法,默认的字型是10px sans-serif

Canvas中font属性的各个分量如下表所示:

字型属性分量 描述
font-style 可以取如下三个值:normalitalicoblique
font-variant 可以取这两个值:normalsmall-caps
font-weight 决定该字型的字符笔画粗细,可取如下值:normallighterboldbolder,也可以采用对应的数值,比如normal对应的是400
font-size 字型的大小,可以使用关键词xx-smallx-smallsmallmediumlargex-largexx-largesmallerlarger;也可以使用具体的带有长度单位的数值,比如20px
line-height 浏览器会将该属性强制设置为其默认值normal,如果你设置了该值,浏览器会忽略你所设定的值
font-family 设置字体

**通过CSS3与Canvas来指定字型属性时的区别:**绘制环境对象的font属性也支持CSS3格式的字型语法,除了样式语法所特有的属性,例如inheritinitial等。如果你不巧刚好用到了inheritinitial的话,那么浏览器在执行到那行代码时会悄然地失败,并不抛出任何异常,同时也不会将该值设定给font属性。通过Canvas来设置字型属性与通过CSS3来设置相比,还有一个区别:在Canvas中设置line-height属性时,浏览器将忽略其值,因为规范要求浏览器必须将该值设置为normal

文本定位(对齐方式)

这里所说的文本定位,其实指的是对齐方式,在CSS中我们可以使用text-align设置文本水平对齐方式,vertical-align设置文本垂直方向的对齐方式。在Canvas中也有对应的属性。

通过前面的知识我们得知,在Canvas中使用fillText()strokeText()绘制文本时,需要指定所绘文本的xy坐标,然而,浏览器具体会将文本绘制在何处(文本定位),则要看textAligntextBaseline这两个绘图环境对象的属性。

textAlign

**CanvasRenderingContext2D.textAlign**是Canvas中绘制文本时文本的对齐方式的属性。其对齐是基于fillText()strokeText()方法的x值。textAlign属性有点类似于CSS中的text-align,用来设置文本水平对齐方式。其主要包括:

ctx.textAlign = "left" || "right" || "center" || "start" || "end";
  • left:文本左对齐
  • right:文本右对齐
  • center:文本居中对齐
  • start:文本对齐界线开始的地方(左对齐指本地从左向右,右对齐指本地从右向左)
  • end:文本对齐界线结束的地方(左对齐指本地从左向右,右对齐指本地从右向左)

默认值是start。在Canvas中使用textAlign时同样会受direction属性值的影响。当Canvas的direction的值为ltr时,也就是说浏览器是按照由左至右的方向来显示文本时,textAlignleft的效果与start相同,而right的效果则与end相同。同理,如果direction的值为rtl时,也就是说浏览器是从右至左来显示文本的,那么textAlignright的效果则与start一致,而left则与end一致。下例演示了textAlign每个值在浏览器中渲染的效果(其中direction的值为默认值ltr):

var text = ['left', 'right', 'center', 'start', 'end'];
ctx.fillStyle = '#f36';

ctx.font = '32px Airal';

for (var i = 0; i < text.length; i++) {
  ctx.textAlign = text[i];
  ctx.fillText('textAlign:' + text[i], w / 2, (i + 1) * 50);
}

效果如下:

可以点击这里在CodePen上查看Demo效果

示例中绘制的文本,其起始点是画布的中心位置w / 2,也就是上图中的黑色竖线。left则让文本左侧在x点处(在竖线右侧),right则文本右侧在x点处(在竖线左侧),center则文本中间点在x点处(在竖线中间)。startleft等同,endright等同。

这里的textAlign='center'比较特殊。textAlign的值为center时候文本的居中是基于你在fillText的时候所给的x的值,也就是说文本一半在x的左边,一半在x的右边(上图展示看得更清楚些)。所以,如果你想让文本在整个Canvas居中,就需要将fillTextx值设置成canvas的宽度的一半。

textBaseline

CanvasRenderingContext2D.textBaseline是Canvas中描述绘制文本时,当前文本基线的属性,类似CSS中的vertical-align属性,在介绍textBaseline属性之前,先来下图,下图展示了textBaseline属性支持的不同的基线情况

上图是不是非常熟悉呀,那我们回到Canvas的textBaseline属性的使用:

ctx.textBaseline = "top" || "hanging" || "middle" || "alphabetic" || "ideographic" || "bottom";
  • top:文本基线在文本块的顶部
  • hanging:文本基线是悬挂基线
  • middle:文本基线在文本块的中间
  • alphabetic:文本基线是标准的字母基线
  • ideographic:文字基线是表意字基线,如果字符本身超出了alphabetic基线,那么ideograhpic基线位置在字符本身的底部
  • bottom:文本基线在文本块的底部,与ideographic基线的区别在于ideographic基线不需要考虑下行字母

textBaseline的默认值是alphabetic,该值用于绘制由基于拉丁字母的语言所组成的字符串,ideographic值则用于绘制日文或中文字符串,hanging值用于绘制各种印度语字符串,topbottommiddle这三个值与特定的语言不相关,它们代表文本周围的边界框之内的某个位置,这个边界框也叫做“字符方框”。

同样来看一个示例,用效果来帮助我们理解textBaseline各个值的效果。

var text = ['top', 'hanging', 'middle', 'alphabetic', 'ideographic', 'bottom'];
ctx.fillStyle = '#f36';

ctx.font = '24px Airal';

for (var i = 0; i < text.length; i++) {
  ctx.textBaseline = text[i];
  ctx.fillText(text[i], (i + 1) * 150, h / 2);
}

效果如下:

可以点击这里在CodePen上查看Demo效果

文本度量

只要你做的事情与文本有关,你就得设法获取某个字符串的像素宽度和高度。在Canvas中提供了measureText()方法,这个方法返回一个TextMetrics对象,这个对象中包含了一个名为width的属性,这个属性就是字符串的宽度。

var text = ctx.measureText("foo"); // TextMetrics object
text.width; // 16;

在使用measureText()方法时,常见的错误就是在调用完该方法之后,才去设置字型。请注意:measureText()方法是根据当前的字型来计算字符串宽度的,因此,如果你在调用measureText()方法之后才去改变字型,那么该方法所返回的宽度并不能反映出以那种字型来度量的实际文本宽度。

上述的内容就是有关于Canvas中绘制文本所涉及到的一些方法和属性。接下来我们来看两个简单的示例。

示例

前面的示例,我们看到的是通过fillText()strokeText()方法绘制填充文本或描边文本,可以说是最简单的绘制文本效果,但实际当中,我们要的效果不仅仅是这些普通的效果。比如我们需要一个圆形的文本效果,3D的文本效果等。那咱们就借助以前所学的一些基础知识来实现这两种效果。

环形文本

直接上代码,这里封装了一个简单的函数,比如drawCircleText(),并且根据绘制弧形文本所需要的参数传给这个函数,具体代码如下:

// @param {Object} ctx - CanvasRenderingContext2D
// @param {String} text - 需要绘制制的文本
// @param {Number} x - 绘制文本起始点x轴坐标点
// @param {Number} y - 绘制文本起始点y轴坐标点
// @param {Number} radius - 圆形半径, 弧度值
// @param {Number} startAngle - 开始的角度值
// @param {Boolean} isFill - 是否填充,true填充,false描边
// @param {String} color - 文本颜色
function drawCircleText(ctx, text, x, y, radius, startAngle, isFill, color) {
    // 将startAngle角度转换成弧度
    var startRad = Math.PI * startAngle / 180;
    // 所绘文本的长度
    var len = text.length;
    // 每个字符所对应的弧度
    var numRadsPerLetter = Math.PI * 2 / len;
    
    ctx.save();
    // 将坐标原点移到(x, y)
    ctx.translate(x, y);
    // 将坐标旋转startRad
    ctx.rotate(startRad);
    
    // 对字符做操作
    for (var i = 0; i < len; i++) {
    ctx.save();
    // 每个字符旋转
    ctx.rotate(i * numRadsPerLetter);
    
    // isFill为true填充, false为描边
    if (isFill) {
        ctx.fillStyle = color;
        ctx.fillText(text[i], 0, -radius);
    } else {
        ctx.strokeStyle = color;
        ctx.strokeText(text[i], 0, -radius);
    }
    ctx.restore();
    }
    ctx.restore();
}

在使用的时候,只需要调用这个函数,并传入相应的值:

ctx.font = 'bold 40px Arial';
drawCircleText(ctx, 'W3CPLUC.COM!', w / 2, h / 2, 100, 0, true, 'orange');

效果如下:

3D文本效果

3D文本效果在CSS中通过text-shadow来实现,具体怎么实现这里就不多说了,必竟我们这里是聊Canvas。其实在Canvas中实现的原理也有点类似,将会使用到shadowBluershadowColor等阴影属性(这个我们前面没有接触过,但后面我们也会深入学习),和前面的圆形文本一样,同样将绘制3D文本的效果封装成一个简单的函数,比如draw3DText(),具体的代码如下:

// @param {Object} ctx - CanvasRenderingContext2D
// @param {String} text - 绘制的文本内容
// @param {Number} x - 文本起始点x轴坐标
// @param {Number} y - 文本起始点y轴坐标
// @param {Number} textDepth - 阴影的深度
// @param {Boolean} isFill - 文本是否填充,true为填充,false为描边
// @param {String} color - 文本颜色
// @param {String} shadowColor - 阴影颜色
// @param {Number} shadowBlur - 阴影距离
// @param {Number} interval - 间距
function draw3DText(ctx, text, x, y, textDepth,isFill, color, shadowColor, shadowBlur, interval) {
    var i;
    for (i = 0; i < textDepth; i++) {
    if(isFill) {
        ctx.fillText(text, x - i, y - i);
    } else {
        ctx.strokeText(text, x - i, y - i);
    }
    }
    ctx.shadowColor = shadowColor;
    ctx.shadowBlur = shadowBlur;
    ctx.shadowOffsetX = textDepth + interval;
    ctx.shadowOffsetY = textDepth + interval;
    
    if (isFill) {
    ctx.fillStyle = color;
    ctx.fillText(text, x - i, y - i);
    } else {
    ctx.strokeStyle = color;
    ctx.strokeText(text, x - i, y - i);
    }
    
}

调用也非常的简单:

ctx.font = '60px Verdana';
ctx.textAlign = 'center';
ctx.textBaseline= 'middle';
    
draw3DText(ctx, 'W3cplus', w / 2, h / 2, 6, true, '#0094ed', '#000', 12, 2);

得到的效果如下:

这个示例通过将fillText()或者strokeText()(x, y)设置为canvas宽度和高度的一半(w / 2, h / 2)再配合ctx.textAlign = 'center'ctx.textBaseline= 'middle'可以实现所绘文本在画布的中间,也就是实现了我们常说的水平垂直居中的效果。

另外,把上面两外效果结合起来,就可以实现圆形的3D文本效果,要是再添加一点动画功能,效果就不一样了:

总结

这篇文章我们主要介绍了在Canvas中绘制文本的一些基础知识,在Canvas中可以通过ctx.fillText()绘制填充文本,ctx.strokeText()可以绘制描边文本,另外通过ctx.textAlignctx.textBaseline设置所绘制文本的位置,并且使用ctx.measureText('text').width可以得到所绘制文本text的宽度值,虽然这个值并不精确,但在Canvas中有方法可以解决,至于怎么解决我们后续的内容将会介绍。

虽然这些功能是绘制文本的基本功能,但结合Canvas其他的功能,我们可以绘制出很多不同的文本效果,至于绘制出什么样的效果,则需要大家去思考,因为创意是自己的,有了创意,然后结合自己所掌握的Canvas知识,能做的事情就更多了。如果你有更好的创意,希望在下面的评论中与我们一起分享。

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。

如需转载,烦请注明出处:https://www.fedev.cn/canvas/drawing-text.htmlnike air max 90 amazon