在上一篇文章中,没有实现段落的纵向偏移和首行缩进,本文把两项功能加上。

方式一:

<!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 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/zehnYCookie/p/14704049.html