小程序 画布 文本绘制

2020-09-07  本文已影响0人  IamaStupid
情景:有很多文本,有的是一行,有的是多行的段落,这么多文本,需要一个方法来处理。

代码:

onload () {
  ......
  // CanvasContext.setTextAlign() 是根据坐标来展示文字的,具体用法去官网
  this.drawText(ctx, {
      textAlign: 'right',
      fontColor: '#f00',
      fontSize: 30,
      text: 'Hello',
      x: 300,
      y: 30
    });
    this.drawText(ctx, {
      textAlign: 'left',
      text: 'Hello 中国!Hello word! Hello Everyone!',
      x: 20,
      y: 80,
      maxWidth: 100
    });
    this.drawText(ctx, {
      textAlign: 'center',
      text: 'Hello 中国! Hello word!  Hello Everyone! ',
      x: 20 + 50,
      y: 150,
      maxWidth: 100,
      isWrap: true,
      charDistace: 2,
      wrapLineHeight: 40
    });
    ......
},
......
// charDistace 这个属性的设置,主要是为了换行的时候,减少循环的次数
// 换行的原理:一个一个文字叠加,然后测量它们的宽度,然后和最大的宽度值进行对比
// 于是设置了charDistace 属性,自定义一次叠加的字数,默认一个,也可以设置为多个
drawText (ctx, options) {
    // textAlign type: string
    if (options.textAlign) {
      ctx.setTextAlign(options.textAlign)
    }
    //fontColor type: string
    if (options.fontColor) {
      ctx.setFillStyle(options.fontColor)
    }
    // fontSize type: number
    if (options.fontSize) {
      ctx.setFontSize(options.fontSize)
    }
    if (!options.isWrap) {
      ctx.fillText(options.text, options.x, options.y, options.maxWidth);
    } else {
      // options.isWrap 是否换行
      // options.charDistace 默认1,表示一次循环一个字符, 这个值在isWrap为true时才有效
      // options.wrapLineHeight 行的高度,这个值在isWrap为true时才有效
      let charArr = options.text.split('');
      let index = 0;
      let end = 0;
      let tempStr = '';
      let metrics = null;
      
      let d = options.charDistace || 1;
      let i = d;
      let tempI = 0;
      let gap1 = 2; // 表示 maxw -xw <= 2 都可以当做相等
      let gap2 = 2; // 如果xw - maxw < = 2 ,那么也当做相等
      let isTextBigger = false;
      let textBiggerIndex = 0;
      let wrapNum = 0;
      // 一次循环5个字符,并将循环的字符叠加,比如 i = 15, 测量它们的宽度xw15
      // 如果xw15比最大的maxw大,则退回到 i = 11的状态,即11个字符长度 xw11
      // 如果xw11 > maxw, 那么可以确定这行最多只能放10个字符,
      // 因为xw10的时候,发生的下一次循环,说明xw10 < maxw
      // 如果xw < maxw, 则开始一个一个字符循环,不再是5个5个字符循环
      while (i < charArr.length) {
        end = i;
        // 不包含end
        tempStr = charArr.slice(index, end).join('');
        metrics = ctx.measureText(tempStr);
        if ((options.maxWidth > metrics.width && options.maxWidth - metrics.width <= gap1) ||
          (metrics.width > options.maxWidth && metrics.width - options.maxWidth <= gap2) ||
          (metrics.width < options.maxWidth && isTextBigger && textBiggerIndex - i === 1 ) ||
          (metrics.width < options.maxWidth && i === (charArr.length - 1))) {
            // 字符宽度刚好符合最大宽度要求
            // 另一种情况:加一个字符又太宽 > gap2; 减一个字符又太小 < gap1
            // 另一种情况:最后一行
            wrapNum++;
            ctx.fillText(tempStr, options.x, options.y + (options.wrapLineHeight * wrapNum -1), options.maxWidth);
            tempI = end;
            index = end;
            i = i + d;
            isTextBigger = false;
        } else {
          // 字符宽度不符合最大宽度
          if (metrics.width < options.maxWidth) {
            // 字符宽度太小
            tempI = i;
            if (isTextBigger) {
              i++
            } else {
              if ((i + d) > charArr.length) {
                i++
              } else {
                i += d
              }
            }
          } else {
            // 字符宽度太大
            isTextBigger = true;
            textBiggerIndex = i;
            i = tempI;
          }
        }
        // console.log(tempStr, metrics)
      }
    }
    
  },
image.png
上一篇 下一篇

猜你喜欢

热点阅读