使用Canvas实现PNG图片描边

3,163 阅读1分钟

实现思路

  1. 首先将原始图像微量平移8个方向
  2. 然后用描边颜色去填充移动后的图像
  3. 最后将原图叠加上去

核心代码

生成画布

这一步没什么说的,就是创建canvas,加载图片,设置canvas的宽高为图片宽高:

// create canvas
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");

// create image
const image = new Image();
image.onload = function() {
    canvas.width = image.width;
    canvas.height = image.height;
};
image.src = url;
image.crossOrigin = "Anonymous";

平移图像

WeChat801522fec0a5ea061be48eb6c9de67d5.png

我们构造一个数组,2个元素表示一个方向

const dArr = [-1, -1, 0, -1, 1, -1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 1];

设置borderWidth4,根据方向和borderWidth,我们可以计算出平移后的x,y,

const borderWidth = 4;
const x = dArr[i] * borderWidth;
const y = dArr[i + 1] * borderWidth;

循环所有方向绘制原图:

for (let i = 0; i < dArr.length; i += 2) {
    ...
    ctx.drawImage(image, x, y);
}

平移.jpg

平移后的图像相当于原始图像向四周扩大了borderWidth的距离。

填充描边色

这里我们需要用到globalCompositeOperation中的source-in模式

source-in:在当前图像内绘制新图像,且只显示新图像。

ctx.globalCompositeOperation = "source-in";
ctx.fillStyle = "red";
ctx.fillRect(0, 0, canvas.width, canvas.height);

填充.jpg

绘制原图

将混合模式改为默认的source-over,然后绘制原图:

ctx.globalCompositeOperation = "source-over";
ctx.drawImage(image, 0, 0);

1640834358781.jpg

在线代码

codepen地址

完整代码

/**
 * 图片描边
 * @param {string} url - 图片地址或base64
 * @param {string} [borderColor=red] - 描边颜色
 * @param {number} [borderWidth=4] - 描边宽度
 * @return {string} base64 - 描边后的图片字符串
 */
function strokeImage(url, borderColor='red', borderWidth = 4) {
  return new Promise((resolve) => {
    // create canvas
    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext("2d");

    // create image
    const image = new Image();
    image.onload = draw;
    image.src = url;
    image.crossOrigin = "Anonymous";

    function draw() {
      canvas.width = image.width;
      canvas.height = image.height;

      const dArr = [-1, -1, 0, -1, 1, -1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 1]; // offset array

      // 平移图像
      for (let i = 0; i < dArr.length; i += 2)
        ctx.drawImage(image, dArr[i] * borderWidth, dArr[i + 1] * borderWidth);

      // 填充描边色
      ctx.globalCompositeOperation = "source-in";
      ctx.fillStyle = borderColor;
      ctx.fillRect(0, 0, canvas.width, canvas.height);

      // 添加原图
      ctx.globalCompositeOperation = "source-over";
      ctx.drawImage(image, 0, 0);

      resolve(canvas.toDataURL());
    }
  });
}