掘金文章归档 · 第 89 篇 Canvas 3/3

前端 canvas 自定义截图,并下载

画布绘制 + 拖拽框选 + getImageData 裁剪 + toDataURL 下载 / 传后端 OCR

原文作者:随意_(掘金) | juejin.cn/post/7348994520589582362 | 发布于 2024-03-22 | 阅读 275 · 约 2 分钟

0背景

最近公司项目有一个需求:在一张图片上(这个图片可以是 pdf、word 等文件转义成的图片),用户可以自定义裁剪图片中的内容,前端将裁剪的图片传给后端,用于 OCR 识别,可以提取到此裁剪区域的文字等信息,用于下一步的业务流程(比如复制裁剪框中的文案等)。

1实现方案

借助 canvas 绘制图片和自定义裁剪框,保存为 base64 格式,也可以自定义下载,传给后端 OCR。直接上代码了,注释的很详细:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Canvas Screenshot</title>
  <style>
    canvas {
      border: 1px solid black;
    }
  </style>
</head>

<body>
  <canvas id="myCanvas" width="500" height="500"></canvas>
  <img id="sourceImage" src="https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF" style="display: none;">

  <script>
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    const sourceImage = document.getElementById('sourceImage');
    let startX, startY, isDragging = false;

    // 加载图片到Canvas
    function loadImage() {
      sourceImage.onload = function () {
        ctx.drawImage(sourceImage, 0, 0, canvas.width, canvas.height);
      };
      // 处理下图片url
      const mysrc = 'https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF' + '?' + new Date().getTime();
      sourceImage.src = mysrc // 替换为你的图片URL
      // 此操作是解决跨域 否则下一步的getImageData报错跨域
      sourceImage.setAttribute('crossOrigin', '');
    }

    // 监听鼠标事件
    canvas.addEventListener('mousedown', function (e) {
      startX = e.offsetX;
      startY = e.offsetY;
      isDragging = true;
    });

    // 黄色内容均为黄色  两种截屏样式,自己采用合适的
    // canvas.addEventListener('mousemove', function (e) {
    //     if (!isDragging) return;
    //     ctx.clearRect(0, 0, canvas.width, canvas.height);
    //     ctx.drawImage(sourceImage, 0, 0, canvas.width, canvas.height);
    //      区域绘制成黄色
    //     ctx.globalAlpha = 0.5;
    //     ctx.fillStyle = 'yellow';
    //     ctx.fillRect(startX, startY, e.offsetX - startX, e.offsetY - startY);
    //     ctx.globalAlpha = 1;
    // });

    // 仅仅边框黄色 内容透明
    canvas.addEventListener('mousemove', function (e) {
      if (!isDragging) return;

      // 清除整个画布
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      // 绘制原始图像
      ctx.drawImage(sourceImage, 0, 0, canvas.width, canvas.height);

      // 设置填充样式为透明,绘制白色矩形覆盖选区内容
      ctx.fillStyle = 'transparent';
      ctx.fillRect(startX, startY, e.offsetX - startX, e.offsetY - startY);

      // 设置边框样式  仅仅边框黄色 内容透明
      ctx.strokeStyle = 'yellow';
      ctx.lineWidth = 2; // 边框宽度

      // 绘制黄色边框,但仅在边缘,不覆盖整个选区
      ctx.strokeRect(startX, startY, e.offsetX - startX, e.offsetY - startY);
    });

    canvas.addEventListener('mouseup', function (e) {
      if (!isDragging) return;
      isDragging = false;
      // 获取选择区域的图片
      const selectionCanvas = document.createElement('canvas');
      const selectionCtx = selectionCanvas.getContext('2d');
      const imageData = ctx.getImageData(startX, startY, e.offsetX - startX, e.offsetY - startY);
      selectionCanvas.width = imageData.width;
      selectionCanvas.height = imageData.height;
      selectionCtx.putImageData(imageData, 0, 0);
      const selectionDataURL = selectionCanvas.toDataURL('image/png');

      // selectionDataURL是一个base64格式的图片格式

      // 可下载,可传输后端

      // 创建一个用于下载的<a>标签
      const downloadLink = document.createElement('a');
      downloadLink.href = selectionDataURL;
      downloadLink.download = 'screenshot.png'; // 设置下载文件名

      // 为了防止页面跳转,阻止<a>标签的默认行为
      downloadLink.style.display = 'none'; // 隐藏这个链接,因为我们不希望它在页面上可见
      document.body.appendChild(downloadLink); // 将<a>标签添加到DOM中

      // 模拟点击这个链接来触发下载
      downloadLink.click();

      // 下载完成后,可以移除这个<a>标签
      document.body.removeChild(downloadLink);
    });

    loadImage();
  </script>
</body>

</html>
截图占位:在 canvas 图片上拖拽出黄色裁剪框的效果

2关键点拆解

环节实现
加载图片drawImage 铺满画布,crossOrigin + 时间戳防跨域污染
框选mousedown 记起点,mousemove 绘制黄色边框(内容透明)
裁剪getImageData 取选区像素,putImageData 到新 canvas
输出toDataURL('image/png') 得到 base64,可下载可传后端
下载a 标签 href=base64 + download 属性,click() 触发
📌 selectionDataURL 是一个 base64 格式的图片,可下载、可传输后端 OCR。
⚠️ 跨域图片必须设置 crossOrigin 属性,否则 getImageData 会因画布被污染而抛错。