canvas绘制文字段落二
在上一篇文章中,没有实现段落的纵向偏移和首行缩进,本文把两项功能加上。
方式一:
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<title>Document</title>
<style>
canvas {
border: 1px solid #000;
}
</style>
</head>
<body>
<canvas id=”mycanvas” width=”240″ height=”630″></canvas>
<script>
let canvas = document.getElementById(“mycanvas”)
let ctx = canvas.getContext(“2d”)
ctx.font = “20px sans-serif”
ctx.fillStyle = “#E992B9”
ctx.textBaseline = “top” // 基线对齐字体的顶部
let rows = 0
let lineWidth = canvas.width – 40
let lineHeight = 20 // 每行高度
let str = “这几天心里颇不宁静。今晚在院子里坐着乘凉,忽然想起日日走过的荷塘,在这满月的光里,总该另有一番样子吧。”
let str2 = “这几天”
/**
@method drawTextParagraph
@params {string} str 要绘制的字符串
@params {number} lineWidth 每行的宽度
@params {number} lineHeight 每行的行高
@params {number} rows 从第多少行开始绘制
@params {number} offsetX 横向偏移距离
@params {number} offsetY 纵向偏移距离
@params {number} indent 段首缩进距离
@return {number} 返回该段落渲染了多少行
*/
const drawTextParagraph = ({ctx, str, lineWidth, lineHeight, rows = 0, offsetX = 0, offsetY =0, indent = 0}) => {
let line= 0
let delta = 1
while(ctx.measureText(str).width + indent > lineWidth){
let lineStr = str.substring(0, delta)
if(ctx.measureText(lineStr).width + indent <= lineWidth){
delta ++
continue
}
// 当字符串宽度大于允许行宽且delta大于1时,说明两个或两个以上的字符才会大于允许行宽
// 这时需要往回退一个字符,如果delta就是1的话,那就不退了
if(delta > 1){
delta —
lineStr = str.substring(0, delta)
}
ctx.fillText(lineStr, offsetX + indent, (rows + line) * lineHeight + offsetY)
str = str.substring(delta) // 把渲染过得字符串去掉,保留剩余的字符串
line ++ // 行数加1
delta = 1 // 索引重置为1
indent = 0 // 缩进只在首行起作用,第一次用完后就设置为0
}
// 经过上面的处理,str可能有剩于内容,或者被切成了空字符串。如果有剩余就再绘制一下
if(str) {
ctx.fillText(str, offsetX + indent, (rows + line) * lineHeight + offsetY)
line ++
}
return line
}
rows += drawTextParagraph({ctx, str, lineWidth, lineHeight, rows, offsetX:20, offsetY: 10 * 1, indent: 40})
rows += drawTextParagraph({ctx, str: str2, lineWidth, lineHeight, rows, offsetX:20, offsetY: 10 * 2, indent: 40})
console.log(rows)
</script>
</body>
</html>
这个方式,第一次用完indent后,就indent设置为0了。代码是短了,但是不直观,过了第一行后,每次循环还要加上indent,对应计算机底层,估计会加一些无用的加法指令。于是我把第一行单独处理。就有了方式二:
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<title>Document</title>
<style>
canvas {
border: 1px solid #000;
}
</style>
</head>
<body>
<canvas id=”mycanvas” width=”240″ height=”630″></canvas>
<script>
let canvas = document.getElementById(“mycanvas”)
let ctx = canvas.getContext(“2d”)
ctx.font = “20px sans-serif”
ctx.fillStyle = “#E992B9”
ctx.textBaseline = “top” // 基线对齐字体的顶部
let rows = 0
let lineWidth = canvas.width – 40
let lineHeight = 20 // 每行高度
let str = “这几天心里颇不宁静。今晚在院子里坐着乘凉,忽然想起日日走过的荷塘,在这满月的光里,总该另有一番样子吧。”
let str2 = “这几天”
// canvas绘制文字6.html图省事儿,第一次用完indent,就把indent设置为0了,这次把第一行单独拿出来处理试试
/**
@method drawTextParagraph
@params {string} str 要绘制的字符串
@params {number} lineWidth 每行的宽度
@params {number} lineHeight 每行的行高
@params {number} rows 从第多少行开始绘制
@params {number} offsetX 横向偏移距离
@params {number} offsetY 纵向偏移距离
@params {number} indent 段首缩进距离
@return {number} 返回该段落渲染了多少行
*/
const drawTextParagraph = ({ctx, str, lineWidth, lineHeight, rows = 0, offsetX = 0, offsetY =0, indent = 0}) => {
let line= 0
let delta = 1, len=str.length
let firtLine = ”
// 找出第一行,绘制第一行
while(delta<=len) {
firstLine = str.substring(0, delta)
if(ctx.measureText(firstLine).width + indent > lineWidth) {
delta —
firstLine = str.substring(0, delta)
break
}
delta ++
}
ctx.fillText(firstLine, offsetX + indent, (rows + line) * lineHeight + offsetY)
line = 1 // 绘制了一行后,行数变为1
// 绘制剩余行
str = str.substring(delta) // 提取剩余字符串
delta = 1 // 把索引拨回1
while(ctx.measureText(str).width > lineWidth){
let lineStr = str.substring(0, delta)
if(ctx.measureText(lineStr).width <= lineWidth){
delta ++
continue
}
// 当字符串宽度大于允许行宽且delta大于1时,说明两个或两个以上的字符才会大于允许行宽
// 这时需要往回退一个字符,如果delta就是1的话,那就不退了
if(delta > 1){
delta —
lineStr = str.substring(0, delta)
}
ctx.fillText(lineStr, offsetX, (rows + line) * lineHeight + offsetY)
str = str.substring(delta) // 把渲染过得字符串去掉,保留剩余的字符串
line ++ // 行数加1
delta = 1 // 索引重置为1
}
// 经过上面的处理,str可能有剩于内容,或者被切成了空字符串。如果有剩余就再绘制一下
if(str) {
ctx.fillText(str, offsetX, (rows + line) * lineHeight + offsetY)
line ++
}
return line
}
rows += drawTextParagraph({ctx, str, lineWidth, lineHeight, rows, offsetX:20, offsetY: 10 * 1, indent: -40})
rows += drawTextParagraph({ctx, str: str2, lineWidth, lineHeight, rows, offsetX:20, offsetY: 10 * 2, indent: 40})
console.log(rows)
</script>
</body>
</html>
版权声明:本文为zehnYCookie原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。