这篇文章使用了react+html2canvas+react-pdf实现了图片、pdf的高亮。
遇到了一些问题
- html2canvas在生产canvas时,创建了iframe标签,整个过程阻塞了浏览器,我们必须要等他结束后才能进行下一步的操作。当我一次性生成很多张的时候,会使得页面无法操作。
- 还是react组件的概念及使用,使用不友好
- 包的体积过大,对项目来说不友好
这次的优化点
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
- 上诉代码写死了pdf是一页的,如果pdf是多页,还要自行修改
- pdfjs-dist使用了2.16.105的版本,因为要兼容ie11。(不确定最新的版本是否支持,因此选择了2.16.105)