【问题标题】:how do I draw a rectangle around a text in html canvas?如何在 html 画布中围绕文本绘制矩形?
【发布时间】:2021-06-16 19:46:41
【问题描述】:

我知道我在画布中阅读了一些关于上升和字体高度的内容,但我就是不明白。

首先为什么文本是从右到上而不是像矩形那样从右到下绘制的。我在文档中的任何地方都找不到。然后,如果我想在一个字母周围画一个矩形,尤其是 'y' 或 'p' 那些低于基线的矩形,我该怎么办。

我有一个canvas with text

 ctx.beginPath();
  ctx.fillText('Hello yyyqqqppp', 50, 50);
  ctx.fillStyle = 'red';
  ctx.fill();
  ctx.closePath();

我该怎么做才能在它周围绘制矩形?

提前致谢!

【问题讨论】:

    标签: javascript html canvas


    【解决方案1】:

    首先,一些误解:

    • ctx.closePath() 方法确实关闭了仍然打开的 Path2D:即 ctx.moveTo(10,10); ctx.lineTo(50, 50); ctx.lineTo(30, 50); 将留下未关闭的路径。调用 closePath() 将为您创建最后一个 ctx.lineTo(10,10)
      ctx.rect() 因此始终是封闭路径,无需调用此方法。
      ctx.fill() 将为您关闭路径。
      ctx.fillText() 没有生成路径,已经包含了fill() 方法,无需再次调用。

    现在,为什么要指定基线而不是文本的顶部,这是因为默认情况下ctx.textBaseline 属性设置为"bottom"。如果需要,可以将其设置为"top"

    要获取文本中字母的位置和大小,可以使用ctx.measureText() 方法。

    因此,对于您的示例,您可以以 :

    结尾

    const canvas = document.querySelector( "canvas" );
    const ctx = canvas.getContext( "2d" );
    
    ctx.font = "50px Arial";
    // the text position
    const x = 50, y = 50;
    // the text to draw
    const str = "Hello yyyqqqppp";
    // the characters to find
    const chars_to_find = [ "o", "y", "p" ];
    
    ctx.strokeStyle = "red";
    
    // get the index of every characters we're interested in
    const chars_indice = [];
    for ( let i = 0; i < str.length; i++ ) {
      if ( chars_to_find.includes( str[ i ] ) ) {
        chars_indice.push( i );
      }
    }
    //iterate through the characters list
    for ( let i = 0; i < chars_indice.length; i++ ) {
      const previous_text = str.substring( 0, chars_indice[ i ] );
      const previous_text_width = ctx.measureText( previous_text ).width;
      const char = str.substring( chars_indice[ i ], chars_indice[ i ] + 1 );
      const char_bbox = getTextBBox( ctx, char );
    
      const left = previous_text_width + char_bbox.left;
      const { top, width, height } = char_bbox;
      const half_line = ctx.lineWidth / 2;
      // draw the rect
      ctx.strokeRect( left + x - half_line, top + y - half_line, width + ctx.lineWidth, height + ctx.lineWidth);
    }
    // draw our text
    ctx.fillText( str, x, y );
    
    function getTextBBox( ctx, text ) {
      const metrics = ctx.measureText( text );
      const left = metrics.actualBoundingBoxLeft * -1;
      const top = metrics.actualBoundingBoxAscent * -1;
      const right = metrics.actualBoundingBoxRight;
      const bottom = metrics.actualBoundingBoxDescent;
      // actualBoundinBox... excludes white spaces
      const width = text.trim() === text ? right - left : metrics.width;
      const height = bottom - top;
      return { left, top, right, bottom, width, height };
    }
    &lt;canvas width="500"&gt;&lt;/canvas&gt;

    【讨论】:

    • 天哪,非常感谢!我想我正在慢慢理解它是如何工作的,非常感谢!
    【解决方案2】:

    以下是在文本周围绘制矩形的一些关键

    如果您希望文本从左上角对齐,那么您可以设置以下上下文对齐属性:

    context.textAlign='left';  // this is the default to align horizontally to the left
    context.textBaseline='top';  // text will be aligned vertically to the top
    

    您可以使用此上下文方法测量文本的水平宽度:

    // set the font size and font face before measuring
    context.font='14px verdana';
    var textWidth=context.measureText('Hello').width;
    

    没有原生的画布方法来测量文本高度,但对于我使用过的大多数字体和非极端字体大小,您可以得到一个很好的高度近似值,如下所示:

    var lineHeight=fontsizeInPixels * 1.286;
    

    示例代码和演示:

    // get references to canvas and context
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    
    var fontsize = 14;
    var fontface = 'verdana';
    var lineHeight = fontsize * 1.286;
    var text = 'Draw a rectangle around me.';
    
    ctx.font = fontsize + 'px ' + fontface;
    var textWidth = ctx.measureText(text).width;
    
    ctx.textAlign = 'left';
    ctx.textBaseline = 'top';
    
    ctx.fillText(text, 20, 50);
    ctx.strokeRect(20, 50, textWidth, lineHeight);
    canvas {
      border: 1px solid red;
    }
    &lt;canvas id=canvas width=300 height=300&gt;&lt;/canvas&gt;

    【讨论】:

    • 韩,我开始沿着这个写一些东西......我会发布它,因为我经历了拆分每个字符的步骤,但是 +1 因为我使用了你在另一个答案中给我的近似值。
    • @Kaiido。我现在注意到提问者想在单个字母周围加上矩形——我错过了。我赞成您的回答,因为它是单个字母的矩形。干杯!
    • 谢谢,但两个答案基本相同,所以我觉得有点奇怪。
    • 谢谢谢谢谢谢,第5次帮了我很大的忙!只是出于好奇,看看我是否理解这个概念。所以有了lineHeight,我得到了文本的lineHeight。还有height = ascent + descent,所以ascent 是像素字体,所以14 对吗? descent 将是 lineHeight - 14
    • 是的,我想应该是这样,非常感谢! jsfiddle.net/5pbko680/8
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-14
    • 1970-01-01
    • 2019-11-18
    相关资源
    最近更新 更多