我在 Canvas 上混合了setLineDash
和strokeText
时发现了一个有趣的效果
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 贪婪的,并不是真正的性能优化,而是一种概念证明。