我在 Canvas 上混合了setLineDashstrokeText时发现了一个有趣的效果

ctx = document.querySelector("canvas").getContext("2d")
ctx.font = "110px Arial";
i = speed = 3

function loop() {
  ctx.clearRect(0, 0, 600, 160)
  ctx.beginPath()
  ctx.setLineDash([i, 600]);
  ctx.strokeText("WORLD ふ", 10, 100);
  i += speed
  if (i > 600 || i < 2)
    speed *= -1
}

setInterval(loop, 50)
<canvas id="c" width=600 height=160></canvas>


如您所见,绘制此W所需的时间比绘制此片段中的O所需的时间更长。
是否有任何属性可以检索字母最长行的像素数(长度)?

最佳答案

您可以在屏幕外绘制每个字符,并“计数”像素的出现次数(非零值):

function getAmount (char, { width, height, font, color }) {
  // create temporary offscreen canvas
  const canvas = document.createElement('canvas')
  canvas.width = width
  canvas.height = height

  // draw the character
  const ctx = canvas.getContext("2d")
  ctx.font = font
  ctx.strokeText(char, 0, 90)

  // get the pixels data
  const imageData = ctx.getImageData(0, 0, width, height)
  let sum = 0
  imageData.data.forEach(point => {
    if (point > 0) sum++
  })
  return sum
}

const width = 90
const height = 90
const font = "90px Arial"

getAmount('W', { font, width, height }) // 940
getAmount('O', { font, width, height }) // 660
getAmount('R', { font, width, height }) // 673
getAmount('L', { font, width, height }) // 296
getAmount('D', { font, width, height }) // 613

您可以粗略地使用这些值来加权速度并分别绘制每个字符,但请记住,您还必须另外管理位置等。同样,这仅检测任何非零值。在为笔划使用渐变时,您将必须检测渐变范围内的图像数据。

编辑:

由于没有找到真相的来源,我们可以使用另一个技巧:

查找可创建屏幕外图像的i数目,该图像的像素数量与全破折号字符相同。

/**
 * draws a stroked text by given params to a context
 **/

function draw (char, ctx, minValue, maxValue) {
  ctx.clearRect(0, 0, 600, 160)
  ctx.beginPath()
  if (minValue && maxValue) {
    ctx.setLineDash([minValue, maxValue])
  }
  ctx.strokeText(char, 10, 100);
}

/**
 * Returns the amount of pixels for a given character
 */
const offscreenCanvas = document.createElement('canvas')
function getAmount (char, { value, max,  width, height, font }) {
  // draw offscreen, then detect border pixels
  offscreenCanvas.width = width
  offscreenCanvas.height = height

  // draw the character
  const ctx = offscreenCanvas.getContext("2d")
  ctx.font = font
  draw(char, ctx, value, max)

  // get the pixels data
  const imageData = ctx.getImageData(0, 0, width, height)
  let sum = 0
  imageData.data.forEach(point => {
    if (point > 0) sum++
  })

  return sum
}

/**
 * Returns the number of iterations required to complete a character
 **/

function getIterations (char, { font, width, height }) {
  // get amount when word is fully drawn
  const fullAmount = getAmount(char, { value: undefined, max: undefined, width, height, font })

  let iterations = 1
  let amount = 0
  do {
    amount = getAmount(char, { value: iterations, max: 1000, width, height, font })
    iterations++
  } while ((amount - fullAmount < -3) && iterations < 2000);

  return iterations
}

从这里我们可以确定i参数的setLineDash值:

const font = "110px Arial";
const width = 110
const height = 110

const check = char => {
  const amount = getIterations(char, { font, width, height })
  console.log(char, amount)
}


check('W')  // 620
check('O')  // 243
check('R')  // 331
check('L')  // 248
check('D')  // 248
check('ふ') // 185

使用这些值,您可以创建一个相对的speed参数,该参数允许您同时完成笔画。

请注意,这种方法是 super 贪婪的,并不是真正的性能优化,而是一种概念证明。

10-08 12:39