之前总结了一些前端截图方案,其中有一个是手动调用canvas的api进行绘制,多次使用之后发现每次都要写很复杂的代码,所以就把主要逻辑封装了一下,改成了通过json配置文字和图片内容,虽然网上已经有一些了,但是都比较臃肿,有很多功能用不到,这里实现的是最轻量的。

// 兼容低版本浏览器
if (typeof Object.assign != 'function') {
  Object.assign = function (target) {
    'use strict';
    if (target == null) {
      throw new TypeError('Cannot convert undefined or null to object');
    }

    target = Object(target);
    for (var index = 1; index < arguments.length; index++) {
      var source = arguments[index];
      if (source != null) {
        for (var key in source) {
          if (Object.prototype.hasOwnProperty.call(source, key)) {
            target[key] = source[key];
          }
        }
      }
    }
    return target;
  };
}

// 通过配置实现canvas绘制图片,callback可以拿到base64的图片
function drawCanvas(canvas, data, callback, callbackError) {
  var defaultData = {
    width: 750,
    height: 10,
    lineHeight: 1.5,
    color: '#263238',
    textAlign: 'left',
    fontSize: 14,
    fontFamily: '"PingFang SC",tahoma,arial,"helvetica neue","hiragino sans gb","microsoft yahei",sans-serif',
    autoHeight: false,
  };

  var ctx = canvas.getContext('2d');

  var newData = Object.assign({}, defaultData, data);

  var currentY = 0,
    loadElementLength = 0,
    loadImgLength = 0,
    elementLength = 0,
    imgElementLength = 0;

  // 设置canvas宽高
  canvas.width = newData.width;
  canvas.height = 2000;

  ctx.fillStyle = newData.bgColor || '#fff';
  ctx.fillRect(0, 0, newData.width, 2000);

  // 指定宽高内绘制图片(background-size:cover方式)
  var drawImgCover = function (img, contW, contH, startX, startY) {
    if (img.width / img.height >= contW / contH) {
      var dH = img.height;
      var dW = Math.ceil(contW / contH * dH);
      ctx.drawImage(img, (img.width - dW) / 2, 0, dW, img.height, startX, startY, contW, contH);
    } else {
      var dW = img.width;
      var dH = Math.ceil(contH / contW * dW);
      ctx.drawImage(img, 0, (img.height - dH) / 2, img.width, dH, startX, startY, contW, contH);
    }
  };

  // 自动换行
  var canvasTextAutoLine = function (str, initX, initY, width, height, lineHeight) {
    var lineWidth = 0;
    var lastSubStrIndex = 0;
    var strLen = str.length;
    for (var i = 0; i < strLen; i++) {
      lineWidth += ctx.measureText(str[i]).width;
      if (lineWidth > width) {
        ctx.fillText(str.substring(lastSubStrIndex, i), initX, initY);
        initY += lineHeight;
        console.log(initY, height);
        if (newData.autoHeight && initY >= height) {
          currentY += lineHeight;
        }
        lineWidth = ctx.measureText(str[i]).width;
        lastSubStrIndex = i;
      }
      if (i == str.length - 1) {
        ctx.fillText(str.substring(lastSubStrIndex, i + 1), initX, initY);
      }
    }
  };

  // 绘制圆角并裁剪
  var drawRoundedRect = function (x, y, width, height, r) {
    ctx.save();
    ctx.beginPath();
    ctx.moveTo(x + r, y);
    ctx.arcTo(x + width, y, x + width, y + r, r);
    ctx.arcTo(x + width, y + height, x + width - r, y + height, r);
    ctx.arcTo(x, y + height, x, y + height - r, r);
    ctx.arcTo(x, y, x + r, y, r);
    ctx.closePath();
    ctx.clip();
  };

  // 居中写字
  var centerText = function (str, startY) {
    var textW = ctx.measureText(str).width;
    var x = (newData.width - textW) / 2;
    ctx.fillText(str, x, startY);
  };

  // 居中图片
  var centerImg = function (img, startY) {
    var w = img.width;
    var x = (newData.width - w) / 2;
    ctx.drawImage(img, x, startY);
  };

  // 最大长度
  var ellipsis = function (str, maxWidth) {
    var strLen = str.length;
    for (var i = 0; i < strLen; i++) {
      if (ctx.measureText(str.substr(0, i + 1)).width > maxWidth) {
        return `${str.substr(0, i)}...`;
      }
    }
    return str;
  };

  // 导出
  var output = function () {
    // 获取可变部分图像
    var contW = newData.width,
      contH = newData.height;

    if (newData.autoHeight) {
      contH += currentY;
    }
    console.log(contH, currentY);
    var main = ctx.getImageData(0, 0, contW, contH);

    // 重新建空画布
    canvas.width = contW;
    canvas.height = contH;
    ctx.clearRect(0, 0, contW, 2000); //清空

    // 绘制可变部分图像
    ctx.putImageData(main, 0, 0);

    // 导出图片url
    var dataUrl = canvas.toDataURL('image/png', 1);
    setTimeout(function () {
      callback && callback(dataUrl);
    }, 100);
  };

  var checkOutput = function () {
    loadElementLength++;
    if (loadElementLength === elementLength) {
      output();
    }
  };

  // 加载图片
  loadImage = function (imgArr, callback) {
    imgArr.forEach(function (item) {
      if (!item) return;
      var Img = new Image();
      Img.crossOrigin = "anonymous";
      Img.src = item.content;

      Img.onload = function () {
        item.imgObj = this;
        loadImgLength++;
        if (loadImgLength === imgElementLength) {
          callback && callback();
        };
      }

      Img.onerror = function (err) {
        callbackError && callbackError(err);
        return false;
      };
    });
  };

  // 一个字一个字绘制
  drawTextArr = function (contentArr, x, y, w, lineHeight, color, fontSize, fontfamily) {
    if (!contentArr || !contentArr.length) return;
    var lineWidth = 0;
    var initX = x;
    var initY = y;

    contentArr.forEach(function (item) {
      ctx.fillStyle = item.color || color;
      var font = (item.fontSize || fontSize) + 'px ' + (item.fontfamily || fontfamily);
      if (item.bold) {
        font = item.bold + ' ' + ctx.font;
      }
      ctx.font = font;
      var strtxt = item.content;

      for (let i = 0; i < strtxt.length; i++) {
        lineWidth += ctx.measureText(strtxt[i]).width;
        if (lineWidth > w) {
          initY += lineHeight;
          if (newData.autoHeight) {
            currentY += lineHeight;
          }
          lineWidth = ctx.measureText(strtxt[i]).width;
        }
        ctx.fillText(strtxt[i], initX + lineWidth - ctx.measureText(strtxt[i]).width, initY);
      }
    });
  };

  // 绘制图片
  var drawImage = function (element) {
    var x = element.x || 0,
      y = element.y || 0,
      w = element.width || 0,
      h = element.height || 0;

    if (currentY !== 0) {
      y += currentY;
    }

    var Img = element.imgObj;

    ctx.save();
    if (element.autoHeight) {
      var newH = Img.height * (w / Img.width);
      if (newData.autoHeight) {
        currentY += newH - h;
      }
      h = newH;
    }
    ctx.drawImage(Img, x, y, w, h);
    ctx.restore();

    if (element.r > 0) {
      drawRoundedRect(x, y, w, h, element.r);
    }
    checkOutput();
  };

  // 绘制文本
  var drawText = function (element) {
    var fontfamily = element.fontfamily || newData.fontFamily,
      fontSize = element.fontSize || newData.fontSize,
      cont = element.content,
      w = element.width || 0,
      h = element.height || 0,
      x = element.x || 0,
      y = element.y || 0,
      color = element.color || newData.color,
      font = fontSize + 'px ' + fontfamily,
      lh = element.lineHeight || newData.lineHeight;
    var lineHeight = lh * fontSize;

    if (element.bold) {
      font = element.bold + ' ' + font;
    }

    if (currentY !== 0) {
      y += currentY;
    }

    if (element.maxWidth) {
      cont = ellipsis(cont, element.maxWidth);
    }

    y += fontSize + (lineHeight - fontSize) / 4;

    ctx.beginPath();
    ctx.fillStyle = color;
    ctx.font = font;
    if (typeof cont !== 'string') {
      drawTextArr(cont, x, y, w, lineHeight, color, fontSize, fontfamily);
    } else if (element.textAlign === 'center') {
      centerText(cont, y);
    } else if (element.autoHeight) {
      canvasTextAutoLine(cont, x, y, w, y + h, lineHeight);
    } else {
      ctx.fillText(cont, x, y);
      ctx.fill();
    }
    ctx.closePath();
    checkOutput();
  };

  if (newData.elements && newData.elements.length) {
    elementLength = newData.elements.length;
    var imgArr = newData.elements.map(function (item) {
      if (item.type === 'img') {
        imgElementLength++;
        return item;
      }
    });
    loadImage(imgArr, function () {
      newData.elements.forEach(function (item, index) {
        if (item.type === 'img') {
          item.imgObj = imgArr[index].imgObj;
          drawImage(item);
        } else {
          drawText(item);
        }
      });
    });
  }
}

使用

function drawShare(canvas, data, callback, callbackError) {
  var newData = {
    width: 686,
    height: 954,
    color: '#263238',
    bgColor: '#fff',
    autoHeight: true,
    elements: [
      {
        type: 'img',
        x: 0,
        y: 0,
        width: 686,
        height: 514,
        autoHeight: true,
        content: data.pic,
      },
      {
        type: 'text',
        width: 622,
        height: 72,
        x: 32,
        y: 546,
        fontSize: 48,
        bold: 500,
        autoHeight: true,
        content: data.title,
      },
      {
        type: 'text',
        width: 622,
        height: 112,
        x: 32,
        y: 634,
        color: '#3C5A6E',
        fontSize: 32,
        autoHeight: true,
        lineHeight: 1.75,
        content: data.info,
      },
      {
        type: 'img',
        x: 32,
        y: 776,
        width: 622,
        height: 2,
        content: 'images/achievement/draw-share-line.png',
      },
      {
        type: 'img',
        x: 24,
        y: 802,
        width: 128,
        height: 128,
        content: data.qrcode,
      },
      {
        type: 'text',
        x: 176,
        y: 814,
        color: '#3C5A6E',
        fontSize: 24,
        content: '长按或扫描二维码',
      },
      {
        type: 'text',
        x: 176,
        y: 850,
        color: '#3C5A6E',
        fontSize: 24,
        content: '查看公益项目',
      },
      {
        type: 'img',
        x: 176,
        y: 894,
        width: 269,
        height: 36,
        content: 'images/achievement/draw-share-logo.png',
      },
      {
        type: 'img',
        x: 476,
        y: 780,
        width: 208,
        height: 176,
        content: 'images/achievement/draw-share-flower.png',
      },
    ],
  };
  drawCanvas(canvas, newData, callback, callbackError);
}

标签: none

评论已关闭