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)
相关推荐
真滴book理喻1 小时前
Vue(四)
前端·javascript·vue.js
程序员_三木1 小时前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
开心工作室_kaic3 小时前
springboot476基于vue篮球联盟管理系统(论文+源码)_kaic
前端·javascript·vue.js
川石教育3 小时前
Vue前端开发-缓存优化
前端·javascript·vue.js·缓存·前端框架·vue·数据缓存
搏博3 小时前
使用Vue创建前后端分离项目的过程(前端部分)
前端·javascript·vue.js
温轻舟3 小时前
前端开发 之 12个鼠标交互特效上【附完整源码】
开发语言·前端·javascript·css·html·交互·温轻舟
web135085886353 小时前
2024-05-18 前端模块化开发——ESModule模块化
开发语言·前端·javascript
LCG元4 小时前
javascript页面设计案例【使用HTML、CSS和JavaScript创建一个基本的互动网页】
javascript
技术程序猿华锋5 小时前
Gemini 2.0 Flash 体验版实测:日常视觉识别的最佳选择,关键在于其API Key现在是免费调用
开发语言·javascript·ecmascript·googlecloud·gemini
TttHhhYy5 小时前
uniapp+vue开发app,蓝牙连接,蓝牙接收文件保存到手机特定文件夹,从手机特定目录(可自定义),读取文件内容,这篇首先说如何读取,手机目录如何寻找
开发语言·前端·javascript·vue.js·uni-app