textarea 到 canvas 多行文本方案

实现效果

输入:使用组件设置文案,要求canvas 渲染效果与textarea内显示格式一致,文本溢出自动删除、有字符输入限制,支持文本对齐功能。
输出:canvas绘制

实现方式

功能点主要有

  1. 文案换行判断
  2. 文本对齐绘制
  3. 文本溢出截断
文本换行判断与绘制

换行的情况主要分为三种:回车换行、文本过长自动换行。对于回车换行,采用字符串查询即可确定换行位置;主要难点在文本过长自动换行,如何判断文本在canvas绘制会在哪一行,需要利用canvas的测量api来模拟换行。
ctx.measureText(text).width即可计算出文本绘制的像素长度。若溢出,择换行渲染(即直接filltext然后改变positionY += lineHeight)

文本对齐绘制

根据text-align属性判断对齐方式,对于不同的对齐方式,只需要计算开始绘制canvas的positionX即可
左对齐,直接偏移量置0,
右对齐,偏移量=外宽度-文本绘制长度
居中对齐,偏移量=(外宽度-文本绘制长度)/2

文本溢出截断

这里需要有行数计算,如何计算文本占用的行高,可以将文本内容绑定到 pre 标签,然后计算pre标签的高度,通过高度/行高确定行数,如果超过就将输入的字符通过watch功能,不断除去最后一个字母,直到满足文本内容在设定行数以内,即满足文本截断功能。

// 具体的watch实现根据对应框架应用即可
function watch(newVal) {
  // 回车异常锁定
  if (
    newVal.slice(newVal.length - 1).charCodeAt() === 10 &&
    this.getlines() + 1 > this.lines
  ) {
    this.inputValue = newVal.slice(0, newVal.length - 1);
    return;
  }
  // 越界逻辑
  if (this.lines < this.getlines()) {
    newVal = newVal.split('');
    newVal.pop();
    this.inputValue = newVal.join('');
    return;
  }
  this.inputValue = (newVal + '').replace(/["*/\\?<>:|]/g, '');
}

canvas绘制示例

let drawMultipleText = function (ctx, styles, text) {
  // 属性获取
  const {
    textAlign,
    lineHeight,
    height,
    width,
    left,
    top,
    fontSize
  } = styles
  ctx.fillStyle = '#999';
  // 计算X偏移量函数
  function calPositionX(text) {
    let positionX;
    switch (textAlign) {
        // 右对齐
      case 2:
        positionX = left + width - ctx.measureText(text).width;
        break;
        // 居中对齐
      case 1:
        positionX = left + (width - ctx.measureText(text).width) / 2;
        break;
        // 左对齐 
      case 0:
      default:
        positionX = left;
    }
    return positionX;
  }
  let positionY = top + fontSize
  let lines = Math.floor(height / lineHeight);
  // 多行绘制计算
  let str = text.split('');
  let lineText = '';
  let renderLine = 0;
  for (let i = 0; i < str.length; i++) {
    if (renderLine > lines) {
      break;
    }
    // measureText 可计算绘制内容的宽度
    let metric = ctx.measureText(lineText + str[i]);
    if (metric.width < width) {
      if (str[i].charCodeAt() === 10) {
        // 回车换行
        positionX = calPositionX(lineText);
        ctx.fillText(lineText, positionX, positionY);
        positionY = positionY + lineHeight;
        lineText = '';
      } else {
        lineText += str[i];
      }
      if (i === str.length - 1) {
        // 到达行尾,绘制结束的文本
        positionX = calPositionX(lineText);
        ctx.fillText(lineText, positionX, positionY);
      }
    } else {
      // 绘制整行内容
      positionX = calPositionX(lineText);
      ctx.fillText(lineText, positionX, positionY);
      renderLine++;
      lineText = str[i];
      positionY = positionY + lineHeight;
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值