是否可以使用HTML5 Canvas Text API绘制文本修饰(下划线等)?

时间:2011-01-07 15:30:12

标签: html5 canvas

我正在使用HTML5 canvas API来显示一些字符串(canvas.fillText),我想知道是否可以使用canvas api进行文本修饰(如下划线,删除线等)。不幸的是我没有发现这一点。我找到的唯一解决方案是使用画布绘图api手动进行装饰(我的意思是,明确地绘制一条水平线,例如,模仿'下划线'装饰)。 是否可以使用canvas文本api?

由于 帕特里克

5 个答案:

答案 0 :(得分:8)

它不适用于内置方法,但这是一个简化的函数,我成功使用它基于读取它。 http://scriptstock.wordpress.com/2012/06/12/html5-canvas-text-underline-workaround/

var underline = function(ctx, text, x, y, size, color, thickness ,offset){
  var width = ctx.measureText(text).width;

  switch(ctx.textAlign){
    case "center":
    x -= (width/2); break;
    case "right":
    x -= width; break;
  }

  y += size+offset;

  ctx.beginPath();
  ctx.strokeStyle = color;
  ctx.lineWidth = thickness;
  ctx.moveTo(x,y);
  ctx.lineTo(x+width,y);
  ctx.stroke();

}

答案 1 :(得分:3)

帕特里克,您可以使用fillRect轻松完成此操作:

ctx.fillText("Hello World", 0, 0);
var text = ctx.measureText("Hello World");
ctx.fillRect(xPos, yPos, text.width, 2);

这种方法唯一困难的部分是无法获得高度使用measureText。否则,您可以在绘制fillRect时将其用作Y坐标。

您的Y位置仅取决于文字的高度以及您对下划线的接近程度。

答案 2 :(得分:2)

要为画布文本添加下划线,只需在与文本相同的 (x,y) 位置添加下划线字符即可。 例如你想在 abc 下划线

    context.fillText("abc",x,y);
    context.fillText ("___",x,y);

与删除线类似,您可以使用“-”字符而不是下划线。

答案 3 :(得分:1)

我很遗憾地说答案是'不'。 text methods of the HTML Canvas Context中没有“文字装饰”或类似的样式。

答案 4 :(得分:1)

我创建了Mulhoon代码的替代版本。我还考虑了文本基线。

const underline = (ctx, text, x, y) => {
  let metrics = measureText(ctx, text)
  let fontSize = Math.floor(metrics.actualHeight * 1.4) // 140% the height 
  switch (ctx.textAlign) {
    case "center" : x -= (metrics.width / 2) ; break
    case "right"  : x -= metrics.width       ; break
  }
  switch (ctx.textBaseline) {
    case "top"    : y += (fontSize)     ; break
    case "middle" : y += (fontSize / 2) ; break
  }
  ctx.save()
  ctx.beginPath()
  ctx.strokeStyle = ctx.fillStyle
  ctx.lineWidth = Math.ceil(fontSize * 0.08)
  ctx.moveTo(x, y)
  ctx.lineTo(x + metrics.width, y)
  ctx.stroke()
  ctx.restore()
}

完整示例

const triggerEvent = (el, eventName) => {
  var event = document.createEvent('HTMLEvents')
  event.initEvent(eventName, true, false)
  el.dispatchEvent(event)
}

const measureText = (ctx, text) => {
  let metrics = ctx.measureText(text)
  return {
    width: Math.floor(metrics.width),
    height: Math.floor(metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent),
    actualHeight: Math.floor(metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent)
  }
}

const underline = (ctx, text, x, y) => {
  let metrics = measureText(ctx, text)
  let fontSize = Math.floor(metrics.actualHeight * 1.4) // 140% the height 
  switch (ctx.textAlign) {
    case "center" : x -= (metrics.width / 2) ; break
    case "right"  : x -= metrics.width       ; break
  }
  switch (ctx.textBaseline) {
    case "top"    : y += (fontSize)     ; break
    case "middle" : y += (fontSize / 2) ; break
  }
  ctx.save()
  ctx.beginPath()
  ctx.strokeStyle = ctx.fillStyle
  ctx.lineWidth = Math.ceil(fontSize * 0.08)
  ctx.moveTo(x, y)
  ctx.lineTo(x + metrics.width, y)
  ctx.stroke()
  ctx.restore()
}

const getOrigin = (ctx) => ({
  x : Math.floor(ctx.canvas.width / 2),
  y : Math.floor(ctx.canvas.height / 2)
})

const redraw = (ctx, sampleText, fontSize) => {
  let origin = getOrigin(ctx)
  ctx.font = fontSize + 'px Arial'
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
  renderText(ctx, sampleText, origin.x, origin.y, 'Yellow', 'left', 'top')
  renderText(ctx, sampleText, origin.x, origin.y, 'SkyBlue ', 'right', 'bottom')
  renderText(ctx, sampleText, origin.x, origin.y, 'Tomato', 'left', 'bottom')
  renderText(ctx, sampleText, origin.x, origin.y, 'Chartreuse ', 'right', 'top')
  renderText(ctx, sampleText, origin.x, origin.y, 'Black', 'center', 'middle')
}

const renderText = (ctx, text, x, y, fillStyle, textAlign, textBaseLine) => {
  ctx.fillStyle = fillStyle
  ctx.textAlign = textAlign
  ctx.textBaseline = textBaseLine
  ctx.fillText(text, x, y)
  underline(ctx, text, x, y)
}

const sampleText = 'Hello World'
const fontSizes = [ 8, 12, 16, 24, 32 ]

document.addEventListener('DOMContentLoaded', () => {
  let ctx = document.querySelector('#demo').getContext('2d')
  let sel = document.querySelector('select[name="font-size"]')
  
  fontSizes.forEach(fontSize => sel.appendChild(new Option(fontSize, fontSize)))
  sel.addEventListener('change', (e) => redraw(ctx, sampleText, sel.value))
  sel.value = fontSizes[fontSizes.length - 1]
  triggerEvent(sel, 'change')
})
canvas { border: thin solid grey }
label { font-weight: bold }
label::after { content: ": " }
<canvas id="demo" width="360" height="120"></canvas>
<form>
  <label for="font-size-select">Font Size</label>
  <select id="font-size-select" name="font-size"></select>
</form>