JS+canvas+pdfjs实现图片或pdf高亮

这篇文章使用了react+html2canvas+react-pdf实现了图片、pdf的高亮。

遇到了一些问题

  1. html2canvas在生产canvas时,创建了iframe标签,整个过程阻塞了浏览器,我们必须要等他结束后才能进行下一步的操作。当我一次性生成很多张的时候,会使得页面无法操作。
  2. 还是react组件的概念及使用,使用不友好
  3. 包的体积过大,对项目来说不友好

这次的优化点

1、js手动绘制canvas,不再使用html2canvas

2、不再使用react组件,一个方法获取图片的Url

3、只有pdfjs-dist依赖包,用来打开pdf

代码

javascript 复制代码
/*
 * @Author: atwlee
 * @Date: 2023-08-25 13:48:06
 * @LastEditors: atwlee
 * @LastEditTime: 2023-09-14 10:57:56
 * @Description:
 * @FilePath: 
 */
import * as pdfjsLib from "pdfjs-dist";

pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
  "pdfjs-dist/build/pdf.worker.min.js",
  import.meta.url
).toString();

export type Rect = [number, number, number, number];

const drawShadows = (
  ctx: CanvasRenderingContext2D,
  canvas: HTMLCanvasElement,
  rect: Rect
) => {
  ctx.fillRect(0, 0, canvas.width, rect[1]); // top
  ctx.fillRect(
    rect[0] + rect[2],
    rect[1],
    canvas.width - rect[0] - rect[2],
    rect[3]
  ); // right
  ctx.fillRect(
    0,
    rect[1] + rect[3],
    canvas.width,
    canvas.height - rect[1] - rect[3]
  ); // bottom
  ctx.fillRect(0, rect[1], rect[0], rect[3]); // left
};

export default async function (
  type: "img" | "pdf",
  url: string,
  rects: Rect[],
  fileSize?: [number, number]
) {
  let result: string[] = []; // 初始化返回结果
  const canvasW = fileSize?.[0]; // 定义外面传入宽作为画布宽
  const canvasH = fileSize?.[1]; // 定义外面传入高作为画布高
  const canvas = document.createElement("canvas"); // 创建canvas
  if (!canvas.getContext) throw "当前浏览器不支持canva"; //   判断浏览器是否支持canvas
  const ctx = canvas.getContext("2d")!; // 创建canvas上下文
  // 判断是图片还是pdf
  if (type === "img") {
    const img = new Image(); // 创建图片对象
    img.src = url; // 设置图片地址
    img.setAttribute("crossOrigin", "Anonymous"); // 设置图片支持跨域
    return await new Promise<string[]>((resolve) => {
      img.onload = () => {
        // 设置canvas宽高,以外面传入优先
        canvas.width = canvasW || img.width;
        canvas.height = canvasH || img.height;
        // 循环坐标,生成对应图片
        ctx.fillStyle = "rgba(0,0,0,0.6)";
        rects.forEach((rect) => {
          ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除上次画的内容
          ctx.drawImage(img, 0, 0); // 画图片
          drawShadows(ctx, canvas, rect); // 画阴影
          result.push(canvas.toDataURL());
        });
        resolve(result);
      };
    });
  } else {
    const loadPage = async (
      doc: pdfjsLib.PDFDocumentProxy,
      pageNum: number
    ) => {
      const pageResult: string[] = []; // 初始化返回结果
      return await new Promise<string[]>(async (resolve) => {
        await doc.getPage(pageNum).then(async (page) => {
          const viewport = page.getViewport({ scale: 1 }); // 获取页面的视图
          // 宽高以外面传入优先
          viewport.width = canvasW || viewport.width;
          viewport.height = canvasH || viewport.height;
          canvas.width = canvasW || viewport.width;
          canvas.height = canvasH || viewport.height;
          // 循环坐标,生成对应图片
          ctx.fillStyle = "rgba(0,0,0,0.6)";
          for (const rect of rects) {
            ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除上次画的内容
            await page.render({
              canvasContext: ctx,
              viewport: viewport,
            }).promise; // 渲染pdf page
            drawShadows(ctx, canvas, rect); // 画阴影
            pageResult.push(canvas.toDataURL());
          }
        });
        resolve(pageResult);
      });
    };
    const loadingTask = pdfjsLib.getDocument(url); // 获取pdf document
    return await new Promise<string[]>(async (resolve) => {
      await loadingTask.promise.then(async (doc) => {
        // const numPages = doc.numPages;
        const numPages = 1;
        // for (let i = 1; i <= numPages; i++) {
        //   result.push(await loadPage(doc, i));
        // }
        result = [...result, ...(await loadPage(doc, numPages))]; // 加载页面
      });
      resolve(result);
    });
  }
}

使用方法

javascript 复制代码
import pdfUrl from "./fp.pdf";

  const handleGetImg = async () => {
    const result = await generateCanvas(
      "pdf",
      // "img",
      // "https://img1.baidu.com/it/u=1264115082,660479506&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800",
      pdfUrl,
      [
        [100, 200, 300, 200],
        [200, 300, 300, 200],
      ]
    );
    console.log("result", result);

    result && setImgs(result);
  };

  useEffect(() => {
    handleGetImg();
  }, []);

PS

  1. 上诉代码写死了pdf是一页的,如果pdf是多页,还要自行修改
  2. pdfjs-dist使用了2.16.105的版本,因为要兼容ie11。(不确定最新的版本是否支持,因此选择了2.16.105)
相关推荐
熊的猫1 小时前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
别拿曾经看以后~2 小时前
【el-form】记一例好用的el-input输入框回车调接口和el-button按钮防重点击
javascript·vue.js·elementui
川石课堂软件测试3 小时前
性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台
运维·javascript·深度学习·jmeter·docker·容器·grafana
JerryXZR3 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
problc3 小时前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter
Gavin_9153 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼4 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
待磨的钝刨5 小时前
【格式化查看JSON文件】coco的json文件内容都在一行如何按照json格式查看
开发语言·javascript·json
前端青山10 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
从兄11 小时前
vue 使用docx-preview 预览替换文档内的特定变量
javascript·vue.js·ecmascript