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)
相关推荐
学习前端的小z几秒前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
前端百草阁24 分钟前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜24 分钟前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
Backstroke fish25 分钟前
Token刷新机制
前端·javascript·vue.js·typescript·vue
zwjapple25 分钟前
typescript里面正则的使用
开发语言·javascript·正则表达式
小五Five27 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
临枫54127 分钟前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
酷酷的威朗普28 分钟前
医院绩效考核系统
javascript·css·vue.js·typescript·node.js·echarts·html5
前端每日三省28 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript
小刺猬_98529 分钟前
(超详细)数组方法 ——— splice( )
前端·javascript·typescript