canvas 生成海报

2021/7/28 3:17:36图片处理js

示例:
image.png

请求图片:

async function getImg(url) {
  return new Promise((resolve, reject) => {
    fetch(url)
      .then(res => res.blob())
      .then(res => {
        var a = new FileReader();
        a.readAsDataURL(res);
        a.onload = function(ev) {
          const img = new Image();
          img.src = ev.target.result;
          resolve([null, img]);
        };
      }).catch(e => {
      reject([e]);
    });
  });
}

请求视频:

async function getVideoFrame(url) {
  return new Promise((resolve, reject) => {
    const video = document.createElement('video');
    video.crossOrigin = '*';
    video.src = url;
    video.addEventListener('loadedmetadata', e => {
      const canvas = document.createElement('canvas');
      canvas.width = video.videoWidth;
      canvas.height = video.videoHeight;
      canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
      const img = new Image();
      img.src = canvas.toDataURL('image/png');
      resolve([null, img]);
    });
    video.addEventListener('error', e => {
      reject(['video load error']);
    });
  });
}

生成二维码:

async function getQr(txt, options) {
  return new Promise((resolve, reject) => {
    QRCode.toDataURL(txt, {
      margin: 0,
      width: options.width || 200,
      // errorCorrectionLevel: 'H',
    }, function(err, url) {
      if (err) {
        reject([err]);
      } else {
        const img = new Image();
        img.src = url;
        resolve([null, img]);
      }
    });
  });
}

实现圆角矩形:

// radius 是一个数字或数组:
// 如果是一个数字,则4个角均是圆角
// 如果是一个数组,必须是一个[topLeft,topRight,bottomRight,bottomLeft]

function fillRoundRect(ctx, x, y, width, height, radius, /*optional*/ fillColor) {
  //圆的直径必然要小于矩形的宽高
  if (2 * radius > width || 2 * radius > height) {
    return false;
  }

  ctx.save();
  ctx.translate(x, y);
  //绘制圆角矩形的各个边
  drawRoundRectPath(ctx, width, height, radius);
  ctx.fillStyle = fillColor || '#000'; //若是给定了值就用给定的值否则给予默认值
  ctx.fill();
  ctx.restore();
}

function drawRoundRectPath(ctx, width, height, radius) {
  if (!isNaN(radius)) {
    radius = [radius, radius, radius, radius];
  }
  ctx.beginPath(0);
  //从右下角顺时针绘制,弧度从0到1/2PI
  ctx.arc(width - radius[2], height - radius[2], radius[2], 0, Math.PI / 2);

  //矩形下边线
  ctx.lineTo(radius[2], height);

  //左下角圆弧,弧度从1/2PI到PI
  ctx.arc(radius[3], height - radius[3], radius[3], Math.PI / 2, Math.PI);

  //矩形左边线
  ctx.lineTo(0, radius[3]);

  //左上角圆弧,弧度从PI到3/2PI
  ctx.arc(radius[0], radius[0], radius[0], Math.PI, Math.PI * 3 / 2);

  //上边线
  ctx.lineTo(width - radius[0], 0);

  //右上角圆弧
  ctx.arc(width - radius[1], radius[1], radius[1], Math.PI * 3 / 2, Math.PI * 2);

  //右边线
  ctx.lineTo(width, height - radius[1]);
  ctx.closePath();
}

文字自动换行:

/*
str:要绘制的字符串
canvas:canvas对象
initX:绘制字符串起始x坐标
initY:绘制字符串起始y坐标
lineHeight:字行高,自己定义个值即可
*/
function canvasTextAutoLine(str, ctx, initX, initY, maxWidth, lineHeight) {
  var lineWidth = 0;
  var lastSubStrIndex = 0;
  for (let i = 0; i < str.length; i++) {
    lineWidth += ctx.measureText(str[i]).width;
    if (lineWidth > maxWidth) {
      ctx.fillText(str.substring(lastSubStrIndex, i), initX, initY);
      initY += lineHeight;
      lineWidth = 0;
      lastSubStrIndex = i;
    }
    if (i === str.length - 1) {
      ctx.fillText(str.substring(lastSubStrIndex, i + 1), initX, initY);
    }
  }
}

垂直水平剧中自适应图片:

function drawImgInArea(img, ctx, area) {
  const areaScale = area.width / area.height;
  const imgScale = img.width / img.height;
  let imgWidth = img.width;
  let imgHeight = img.height;
  if (areaScale > imgScale) {
    // 竖图
    imgHeight = area.height;
    imgWidth = imgHeight * imgScale;
    ctx.drawImage(img, area.x + (area.width - imgWidth) / 2, area.y, imgWidth, imgHeight);
  } else {
    imgWidth = area.width;
    imgHeight = imgWidth / imgScale;
    ctx.drawImage(img, area.x, area.y + (area.height - imgHeight) / 2, imgWidth, imgHeight);
  }
}

图片滤镜:黑白照片

function darkImgFilter(img) {
  return new Promise((resolve) => {
    const canvas = document.createElement('canvas');
    canvas.width = img.width;
    canvas.height = img.height;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0, img.width, img.height);
    const imgData = ctx.getImageData(0, 0, img.width, img.height);
    //我这个图片大小是235*235,这里指从(10,10)获取宽235高235的像素的data
    const data = imgData.data;//每个像素的data是个数组(红,绿,蓝,透明度)
    //遍历每个像素
    for (var i = 0, len = data.length; i < len; i += 4) {
      var black = (imgData.data[i] + imgData.data[i + 1] + imgData.data[i + 2]) / 3;
      if (black >= 100) {
        black = 255;
      } else {
        black = 0;
      }
      imgData.data[i] = black;
      imgData.data[i + 1] = black;
      imgData.data[i + 2] = black;
    }

    //在指定位置输出图片
    ctx.putImageData(imgData, 0, 0);

    const image = new Image();
    image.src = canvas.toDataURL('image/png');
    resolve([null, image]);
  });
}

base64转blob,进行图片预览:

function base64ToBlobUrl(base64) {
  var arr = base64.split(','), mime = arr[0].match(/:(.*?);/)[1],
    bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  const blob = new Blob([u8arr], { type: mime });
  const blobUrl = URL.createObjectURL(blob);
  
  return blobUrl;
}

下载图片

function downloadImg(url,name) {
  const a = document.createElement('a');
  a.style.display = 'none';
  a.href = url;
  a.download = name;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
}

作图:伪代码

const canvas = document.createElement('canvas');
canvas.width = 1080;
canvas.height = 1920;
const ctx = canvas.getContext('2d');

// 将图片画到canvas上。
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

// 圆角矩形
fillRoundRect(ctx, 0, 0, 100, 100, [10,10,10,10], '#fff');

// 二维码
ctx.drawImage(qrImg, 0, 0, 100, 100);

// 在指定范围内,自适应图片
drawImgInArea(img, ctx, {
  x: 0,
  y: 0,
  width: 100,
  height: 100,
});

// 绘制字体
ctx.font = '20px "Helvetica"';
ctx.fillStyle = '#333';
ctx.textBaseline = 'top';
canvasTextAutoLine(txt, ctx, 0, 0, 100, 20);

// 到处图片
const base = canvas.toDataURL('image/png', 0.4);
const blobUrl = base64ToBlobUrl(base);

downloadImg(blobUrl,'test.png');