Canvas简单绘制乐高拼图风格图片

1,668 阅读1分钟

使用canvas读取像素点和绘制简单形状两个功能点,几十行代码就可以简单实现乐高风格图片转化

功能演示

思路一共两步

  • 获取图片像素马赛克化
  • 马赛克块填充为乐高块图形

马赛克化图片

1. 基础工作,canvas显示原始图片

  const canvas = document.getElementById("mosaic");
  const ctx = canvas.getContext("2d");
  const image = new Image();
  image.src = "./Test.png";

  image.onload = function () {
    if (!ctx) {
      return null;
    }
    canvas.width = image.width;
    canvas.height = image.height;
    draw();
  };

  const draw = () => {
    // 绘制原始图片
    ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
  }

正常展示如下 Test.png

2. 将原始图片马赛克化

思路很简单,首先设置马赛克块的size,根据size计算x和y轴马赛克块像素 参考canvas像素操作获取该块rgba信息,根据颜色信息填充该(size * size)马赛克块

  const size = 20;
  const draw = () => {
    // 绘制原始图片
    ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
    // 获取原始图片信息
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    // 生成 i * j 的马赛克块
    for (let i = 0; i < Math.floor(canvas.width / size); i++) {
      for (let j = 0; j < Math.floor(canvas.height / size); j++) {
        // 从马赛克块中随机抽出一个像素点信息
        let rgba = getPxInfo(
          imageData,
          i * size + Math.floor(Math.random() * size),
          j * size + Math.floor(Math.random() * size)
        );
        // 填充马赛克块
        fillMosaicRect(ctx, rgba, i * size, j * size, size, size);
      }
    }
  };
  
  // 获取该像素RGBA信息
  const getPxInfo = (imgData, x, y) => {
    const color = [];
    const data = imgData.data;
    const w = imgData.width;
    color[0] = data[(y * w + x) * 4];
    color[1] = data[(y * w + x) * 4 + 1];
    color[2] = data[(y * w + x) * 4 + 2];
    color[3] = data[(y * w + x) * 4 + 3];
    return color;
  };
  
  // 填充该块颜色
  const fillMosaicRect = (ctx, rgba, x, y, w, h) => {
    ctx.fillStyle = `rgb(${rgba.join(",")})`;
    ctx.fillRect(x, y, w, h);
  };

此时效果就是正常的马赛克

下载 (1).png

填充乐高块图形

简单来说,只需要修改填充该块颜色函数,除了填充颜色外,需要绘制一个乐高风格的圆形按钮,为了让方块和圆按钮都具有一定质感,最简单的方法就是加上阴影效果

  const fillMosaicRect = (ctx, rgba, x, y, w, h) => {
    // 阴影效果
    ctx.shadowBlur = w / 4;
    ctx.shadowColor = `rgb(${rgba.map((color) => color * 0.6).join(",")})`;

    // 背景色
    ctx.fillStyle = `rgb(${rgba.join(",")})`;
    ctx.fillRect(x, y, w, h);

    // 乐高圆形按钮
    const circle = new Path2D();
    circle.arc(x + w / 2, y + h / 2, w / 3, 0, 2 * Math.PI);
    ctx.fill(circle);
  };

大功告成

下载 (2).png

更多细节

虽然已经完成了乐高块的渲染,但实际上来说,我们为了乐高的效果可以增加更多细节,比如说浮雕效果(或者还可以自己添加Lego水印/反光效果/阴影角度...)

添加浮雕效果

const fillMosaicRect = (ctx, rgba, x, y, w, h) => {
    // 阴影效果
    ctx.shadowBlur = w / 4;
    ctx.shadowColor = `rgb(${rgba.map((color) => color * 0.6).join(",")})`;

    // 背景色
    ctx.fillStyle = `rgb(${rgba.join(",")})`;
    ctx.fillRect(x, y, w, h);

    // 浮雕背景色
    const lineargradient = ctx.createLinearGradient(x, y, x, y + h);
    lineargradient.addColorStop(0, `#ffffff`);
    lineargradient.addColorStop(0.5, `rgb(${rgba.join(",")})`);
    lineargradient.addColorStop(1, `#000000`);
    ctx.fillStyle = lineargradient;

    const circleFore = new Path2D();
    circleFore.arc(x + w / 2, y + h / 2, w / 3, 0, 2 * Math.PI);
    ctx.fill(circleFore);

    // 浮雕前景色
    ctx.fillStyle = `rgb(${rgba.join(",")})`;
    ctx.shadowBlur = 0;
    ctx.shadowColor = "";

    const circleBack = new Path2D();
    circleBack.arc(x + w / 2, y + h / 2, (w / 3) * 0.8, 0, 2 * Math.PI);
    ctx.fill(circleBack);
}

下载 (4).png

最终实现

Github源码查看

实际的功能演示中增加了一些配置项,体验起来趣味满多的