如何在React中集成 PDF.js?构建支持打印下载的PDF阅读器详解
本文深入解析基于 React 和 PDF.js 构建 PDF 查看器的实现方案,该组件支持 PDF 渲染、图片打印和下载功能,并包含完整的加载状态与错误处理机制。
完整代码在最后
一个PDF 文件:
https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf
效果展示:
安装 PDF.js
要使用 pdf.js
,你可以通过 npm 安装它。以下是安装和配置 pdf.js
的详细步骤:
1. 安装 pdfjs-dist
pdf.js
的官方 npm 包名为 pdfjs-dist
。可以通过以下命令安装:
npm install pdfjs-dist
pdfjs-dist
是 PDF.js 的分发版本,包含了核心功能和 Worker 文件。- 安装完成后,你可以在项目中直接引入并使用。
2. 设置 Worker 文件路径
PDF.js 需要一个 Worker 文件来处理 PDF 渲染任务。默认情况下,Worker 文件路径需要手动设置。
方法 1:使用 CDN
如果你不想将 Worker 文件打包到项目中,可以直接使用 CDN 提供的 Worker 文件:
import { GlobalWorkerOptions } from 'pdfjs-dist';// 设置 Worker 文件路径
GlobalWorkerOptions.workerSrc = '//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';
方法 2:本地 Worker 文件
如果你希望将 Worker 文件打包到项目中,可以这样做:
-
在代码中引入 Worker 文件:
import { GlobalWorkerOptions, getDocument } from 'pdfjs-dist'; import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';// 设置 Worker 文件路径 GlobalWorkerOptions.workerSrc = pdfjsWorker;
-
这种方式适合离线环境或需要完全控制依赖的情况。
技术架构解析
1. 核心依赖
- PDF.js:Mozilla 开源的 PDF 解析库,支持 Web 端 PDF 渲染
- React Hooks:使用 useState 管理状态,useEffect 处理副作用,useRef 操作 DOM
- Ant Design:采用 Button 组件构建操作界面
pdf.js 官网:https://mozilla.github.io/pdf.js/
2. 初始化配置
通过配置 Worker 文件实现 PDF 解析的 Web Worker 并行处理,避免阻塞主线程。
GlobalWorkerOptions.workerSrc = '//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';
核心功能实现
1. PDF 渲染流程
这段代码的核心功能是加载一个 PDF 文件,并将其第一页渲染到 <canvas>
元素中。
// 加载 PDF 并渲染到 canvasuseEffect(() => {const abortController = new AbortController();if (!pdfUrl) return;setIsLoading(true);setError(null);getDocument(pdfUrl).promise.then((pdf) => {if (abortController.signal.aborted) return;console.log('PDF loaded');// 获取第一页pdf.getPage(1).then((page) => {if (abortController.signal.aborted) {page.cleanup();return;}console.log('Page 1 loaded');const scale = 1.5;const viewport = page.getViewport({ scale });if (!canvasRef.current) {throw new Error('Canvas element not found');}const canvas = canvasRef.current;const context = canvas.getContext('2d');// 清除之前的画布内容context?.clearRect(0, 0, canvas.width, canvas.height);canvas.height = viewport.height;canvas.width = viewport.width;console.log('Canvas尺寸设置:', canvas.width, 'x', canvas.height);// 渲染 PDF 页面到 canvasconst renderContext: any = {canvasContext: context,viewport: viewport,};const renderTask = page.render(renderContext);renderTask.promise.then(() => {console.log('页面渲染完成');setIsLoading(false);}).catch((renderError) => {if (!abortController.signal.aborted) {setError('页面渲染失败: ' + renderError.message);}});});}).catch((error) => {if (!abortController.signal.aborted) {setError('PDF加载失败: ' + error.message);console.error('Error loading PDF:', error);}}).finally(() => {if (!abortController.signal.aborted) {setIsLoading(false);}});return () => {abortController.abort();const canvas = canvasRef.current;if (canvas) {const context = canvas.getContext('2d');context?.clearRect(0, 0, canvas.width, canvas.height);}};}, [pdfUrl]);
- 初始化加载状态:设置加载提示和清理错误信息。
- 加载 PDF 文件:使用
getDocument
加载 PDF,并获取第一页。 - 计算视口与画布尺寸:根据缩放比例调整
<canvas>
的大小。 - 渲染 PDF 页面:将 PDF 页面绘制到
<canvas>
上。 - 错误处理与清理:捕获加载和渲染中的错误,并在组件卸载时清理资源。
以下是核心实现原理与技术要点的详细解读:
1. useEffect
的作用
useEffect(() => {// ...
}, [pdfUrl]);
useEffect
是 React 的生命周期钩子,用于处理副作用(如数据加载、DOM 操作等)。- 这里的依赖项
[pdfUrl]
表示当pdfUrl
发生变化时,这个useEffect
会重新执行。 - 如果
pdfUrl
为空或未改变,则不会触发新的加载。
2. 创建 AbortController
const abortController = new AbortController();
AbortController
是一个浏览器内置的 API,用于取消异步操作(如网络请求)。- 在这里,我们用它来确保在组件卸载或重新加载时,可以中断正在进行的 PDF 加载任务,避免内存泄漏或不必要的资源消耗。
3. 检查 pdfUrl
是否有效
if (!pdfUrl) return;
- 如果
pdfUrl
为空,则直接退出函数,避免无效操作。
4. 设置加载状态
setIsLoading(true);
setError(null);
- 将
isLoading
设置为true
,表示开始加载 PDF。 - 清空之前的错误信息(如果有)。
5. 加载 PDF 文件
getDocument(pdfUrl).promise.then((pdf) => { ... }).catch((error) => { ... }).finally(() => { ... });
getDocument
是 PDF.js 提供的方法,用于加载 PDF 文件。- 它返回一个 Promise,成功时会返回一个
pdf
对象,失败时会抛出错误。
关键点:
-
abortController.signal.aborted
:if (abortController.signal.aborted) return;
- 如果组件已经被卸载或取消了加载任务,则直接退出,避免继续执行无意义的操作。
-
pdf.getPage(1)
:pdf.getPage(1).then((page) => { ... });
getPage(1)
获取 PDF 的第一页(索引从 1 开始)。
6. 计算视口与画布尺寸
const scale = 1.5;
const viewport = page.getViewport({ scale });if (!canvasRef.current) {throw new Error('Canvas element not found');
}
const canvas = canvasRef.current;
const context = canvas.getContext('2d');// 清除之前的画布内容
context?.clearRect(0, 0, canvas.width, canvas.height);canvas.height = viewport.height;
canvas.width = viewport.width;
console.log('Canvas尺寸设置:', canvas.width, 'x', canvas.height);
关键点:
-
scale
:scale=1.5
表示将 PDF 页面放大 1.5 倍进行渲染。- 可以根据需求调整缩放比例。
-
getViewport
:getViewport({ scale })
根据缩放比例计算页面的视口尺寸(宽度和高度)。
-
清空画布:
- 使用
context.clearRect
清除之前的内容,确保每次渲染都是干净的。
- 使用
-
设置画布尺寸:
- 将
<canvas>
的宽高设置为视口的宽高,以匹配 PDF 页面的比例。
- 将
7. 渲染 PDF 页面到 Canvas
const renderContext: any = {canvasContext: context,viewport: viewport,
};const renderTask = page.render(renderContext);renderTask.promise.then(() => {console.log('页面渲染完成');setIsLoading(false);}).catch((renderError) => {if (!abortController.signal.aborted) {setError('页面渲染失败: ' + renderError.message);}});
关键点:
-
renderContext
:-
包含两个主要属性:
canvasContext
:指向<canvas>
的 2D 上下文。viewport
:定义了渲染区域的尺寸和比例。
-
-
page.render
:- 调用
page.render
方法将 PDF 页面渲染到<canvas>
上。 - 返回一个
renderTask
对象,包含一个promise
,用于监听渲染完成或失败的状态。
- 调用
-
渲染完成后的回调:
- 当渲染成功时,将
isLoading
设置为false
,隐藏加载提示。 - 如果渲染失败,则记录错误信息。
- 当渲染成功时,将
8. 错误处理
.catch((error) => {if (!abortController.signal.aborted) {setError('PDF加载失败: ' + error.message);console.error('Error loading PDF:', error);}
})
.finally(() => {if (!abortController.signal.aborted) {setIsLoading(false);}
});
关键点:
-
捕获加载错误:
- 如果 PDF 加载失败(如 URL 错误或网络问题),则设置错误信息并打印日志。
-
finally
块:- 无论成功还是失败,都会执行
finally
块。 - 确保即使发生错误,
isLoading
也会被重置为false
,避免加载状态卡住。
- 无论成功还是失败,都会执行
9. 清理逻辑
return () => {abortController.abort();const canvas = canvasRef.current;if (canvas) {const context = canvas.getContext('2d');context?.clearRect(0, 0, canvas.width, canvas.height);}
};
关键点:
-
取消加载任务:
abortController.abort()
中断正在进行的 PDF 加载任务,避免资源浪费。
-
清理画布:
- 在组件卸载时清除
<canvas>
的内容,确保没有残留的绘制数据。
- 在组件卸载时清除
2. 打印功能实现
效果展示:
// 打印 PDF 页面const handlePrint = () => {const canvas = canvasRef.current;if (canvas) {// 创建隐藏的iframeconst iframe = document.createElement('iframe');iframe.style.display = 'none';document.body.appendChild(iframe);// 将canvas内容转换为图片const imgData = canvas.toDataURL('image/png');const printDocument = iframe.contentWindow?.document;if (printDocument) {printDocument.open();printDocument.write(`<html><head><title>Print</title></head><body style="margin: 0;"><img src="${imgData}" style="width: 100%; height: auto;" /></body></html>`);printDocument.close();// 延迟执行打印以确保内容加载setTimeout(() => {iframe.contentWindow?.print();document.body.removeChild(iframe);}, 500);}}};
- 获取
<canvas>
内容:将画布内容转换为 Base64 编码的图片数据。 - 创建隐藏的
<iframe>
:提供一个独立的文档环境,用于打印特定内容。 - 向
<iframe>
中写入 HTML:将图片嵌入到<iframe>
的文档中。 - 触发打印:延迟调用浏览器的打印功能,并在完成后清理
<iframe>
。
1. 函数定义
const handlePrint = () => { ... }
- 这是一个 React 组件中的方法,用于处理 PDF 页面的打印操作。
- 当用户点击“打印”按钮时,会调用这个函数。
2. 获取 <canvas>
元素
const canvas = canvasRef.current;
if (canvas) { ... }
- 使用
canvasRef.current
获取当前组件中引用的<canvas>
元素。 - 如果
<canvas>
存在,则继续执行打印逻辑;否则直接退出。
3. 创建隐藏的 <iframe>
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
关键点:
-
为什么使用
<iframe>
?- 浏览器的打印功能通常会打印整个页面的内容。为了只打印特定内容(如
<canvas>
的内容),我们将其封装到一个隐藏的<iframe>
中。 <iframe>
是一个独立的文档环境,不会影响主页面的内容。
- 浏览器的打印功能通常会打印整个页面的内容。为了只打印特定内容(如
-
隐藏
<iframe>
- 设置
iframe.style.display = 'none'
将其隐藏,避免用户看到额外的 UI 元素。
- 设置
-
添加到 DOM
- 使用
document.body.appendChild(iframe)
将<iframe>
添加到页面的 DOM 树中。
- 使用
4. 将 <canvas>
内容转换为图片
const imgData = canvas.toDataURL('image/png');
关键点:
-
toDataURL
方法<canvas>
提供了toDataURL
方法,可以将画布内容转换为 Base64 编码的图片数据。- 参数
'image/png'
指定输出的图片格式为 PNG。
-
Base64 图片数据
-
imgData
是一个字符串,包含图片的 Base64 编码。例如:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...
-
5. 向 <iframe>
中写入 HTML 内容
const printDocument = iframe.contentWindow?.document;
if (printDocument) {printDocument.open();printDocument.write(`<html><head><title>Print</title></head><body style="margin: 0;"><img src="${imgData}" style="width: 100%; height: auto;" /></body></html>`);printDocument.close();
}
关键点:
-
获取
<iframe>
的文档对象iframe.contentWindow?.document
获取<iframe>
的document
对象。- 使用可选链操作符
?.
确保contentWindow
存在。
-
写入 HTML 内容
-
调用
printDocument.open()
打开文档流。 -
使用
printDocument.write()
向文档中写入 HTML 内容。 -
写入的内容包括:
- 一个
<img>
标签,其src
属性设置为 Base64 图片数据。 - 样式设置为
width: 100%
和height: auto
,确保图片自适应打印区域。
- 一个
-
调用
printDocument.close()
关闭文档流。
-
6. 延迟执行打印
setTimeout(() => {iframe.contentWindow?.print();document.body.removeChild(iframe);
}, 500);
关键点:
-
延迟执行
- 使用
setTimeout
延迟 500 毫秒后再执行打印操作。 - 这是为了确保
<iframe>
中的内容已经完全加载,避免打印时出现空白或不完整的情况。
- 使用
-
触发打印
- 调用
iframe.contentWindow?.print()
触发浏览器的打印对话框。 - 用户可以选择打印机或保存为 PDF。
- 调用
-
清理
<iframe>
- 打印完成后,使用
document.body.removeChild(iframe)
从 DOM 中移除<iframe>
,释放资源。
- 打印完成后,使用
3. 下载功能实现
将 <canvas>
中的内容转换为图片,并提供下载功能。用户点击“下载”按钮后,会触发这个函数,将画布内容保存为 PNG 文件。
效果展示:
// 下载 PDF 页面const handleDownload = () => {const canvas = canvasRef.current;if (canvas) {const imgData = canvas.toDataURL('image/png');const fileName = pdfUrl.split('/').pop()?.replace(/\.pdf$/i, '') || 'download';const link = document.createElement('a');link.download = `${fileName}.png`;link.href = imgData;document.body.appendChild(link);link.click();document.body.removeChild(link);}}
- 获取
<canvas>
内容:将画布内容转换为 Base64 编码的图片数据。 - 生成文件名:根据
pdfUrl
提取文件名,并移除.pdf
后缀。 - 创建下载链接:动态生成一个
<a>
元素,并设置其href
和download
属性。 - 触发下载:通过模拟点击事件触发浏览器的下载功能,并在完成后清理 DOM。
1. 函数定义
const handleDownload = () => { ... }
- 这是一个 React 组件中的方法,用于处理 PDF 页面的下载操作。
- 当用户点击“下载”按钮时,会调用这个函数。
2. 获取 <canvas>
元素
const canvas = canvasRef.current;
if (canvas) { ... }
- 使用
canvasRef.current
获取当前组件中引用的<canvas>
元素。 - 如果
<canvas>
存在,则继续执行下载逻辑;否则直接退出。
3. 将 <canvas>
内容转换为图片
const imgData = canvas.toDataURL('image/png');
关键点:
-
toDataURL
方法<canvas>
提供了toDataURL
方法,可以将画布内容转换为 Base64 编码的图片数据。- 参数
'image/png'
指定输出的图片格式为 PNG。
-
Base64 图片数据
-
imgData
是一个字符串,包含图片的 Base64 编码。例如:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...
-
4. 生成文件名
const fileName = pdfUrl.split('/').pop()?.replace(/.pdf$/i, '') || 'download';
关键点:
-
提取文件名
pdfUrl.split('/')
将pdfUrl
按/
分隔成数组。.pop()
获取数组的最后一个元素,通常是文件名(如example.pdf
)。
-
移除
.pdf
后缀-
.replace(/.pdf$/i, '')
使用正则表达式将文件名中的.pdf
后缀替换为空字符串。 -
/.pdf$/i
的含义:.
匹配点号(.
是特殊字符,需要用反斜杠转义)。pdf
匹配字符串 “pdf”。$
表示匹配字符串的结尾。i
表示忽略大小写。
-
-
默认文件名
- 如果
pdfUrl
为空或无法提取文件名,则使用默认值'download'
。
- 如果
5. 创建下载链接
const link = document.createElement('a');
link.download = `${fileName}.png`;
link.href = imgData;
关键点:
-
创建
<a>
元素document.createElement('a')
创建一个 HTML 锚点(<a>
)元素。- 这个元素用于模拟下载行为。
-
设置下载属性
link.download =
${fileName}.png` 设置下载文件的名称。- 例如,如果
fileName
是example
,则下载的文件名为example.png
。
-
设置链接地址
link.href = imgData
将 Base64 图片数据赋值给href
属性。- 浏览器会将其识别为一个可下载的资源。
6. 触发下载
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
关键点:
-
添加到 DOM
- 使用
document.body.appendChild(link)
将<a>
元素临时添加到页面的 DOM 树中。 - 这是为了确保浏览器能够识别并触发下载行为。
- 使用
-
触发点击事件
- 调用
link.click()
模拟用户点击下载链接。 - 浏览器会自动开始下载文件。
- 调用
-
清理 DOM
- 使用
document.body.removeChild(link)
从 DOM 中移除<a>
元素。 - 避免页面上留下多余的元素。
- 使用
总结
本文实现的 PDF 查看器具有以下特点:
特性 | 实现方案 | 优势 |
---|---|---|
按需加载 | 首屏仅加载第一页 | 快速呈现 |
打印/下载 | Canvas 转图片方案 | 跨浏览器兼容性好 |
错误恢复 | 状态隔离 + 错误边界 | 增强组件健壮性 |
内存安全 | AbortController + 清理函数 | 防止内存泄漏 |
该方案为基本的 PDF 预览需求提供了可靠实现,开发者可根据具体业务场景进行扩展优化。对于需要完整 PDF 功能(如文本选择、表单填写等)的场景,建议结合 PDF.js 的完整功能 API 进行深度定制。
完整代码:
import React, { useEffect, useRef, useState } from 'react';
import { getDocument } from 'pdfjs-dist';
import { GlobalWorkerOptions } from 'pdfjs-dist';
import { Button } from 'antd';// 设置 Worker 文件路径
GlobalWorkerOptions.workerSrc = '//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';// 为 pdfUrl 参数添加 string 类型注解,解决隐式 any 类型问题
const PDFViewer = ({ pdfUrl }: { pdfUrl: string }) => {const canvasRef = useRef<HTMLCanvasElement>(null);const [isLoading, setIsLoading] = useState(true);const [error, setError] = useState<string | null>(null);// 打印 PDF 页面const handlePrint = () => {const canvas = canvasRef.current;if (canvas) {// 创建隐藏的iframeconst iframe = document.createElement('iframe');iframe.style.display = 'none';document.body.appendChild(iframe);// 将canvas内容转换为图片const imgData = canvas.toDataURL('image/png');const printDocument = iframe.contentWindow?.document;if (printDocument) {printDocument.open();printDocument.write(`<html><head><title>Print</title></head><body style="margin: 0;"><img src="${imgData}" style="width: 100%; height: auto;" /></body></html>`);printDocument.close();// 延迟执行打印以确保内容加载setTimeout(() => {iframe.contentWindow?.print();document.body.removeChild(iframe);}, 500);}}};// 下载 PDF 页面const handleDownload = () => {const canvas = canvasRef.current;if (canvas) {const imgData = canvas.toDataURL('image/png');const fileName = pdfUrl.split('/').pop()?.replace(/\.pdf$/i, '') || 'download';const link = document.createElement('a');link.download = `${fileName}.png`;link.href = imgData;document.body.appendChild(link);link.click();document.body.removeChild(link);}}// 加载 PDF 并渲染到 canvasuseEffect(() => {const abortController = new AbortController();if (!pdfUrl) return;setIsLoading(true);setError(null);getDocument(pdfUrl).promise.then((pdf) => {if (abortController.signal.aborted) return;console.log('PDF loaded');// 获取第一页pdf.getPage(1).then((page) => {if (abortController.signal.aborted) {page.cleanup();return;}console.log('Page 1 loaded');const scale = 1.5;const viewport = page.getViewport({ scale });if (!canvasRef.current) {throw new Error('Canvas element not found');}const canvas = canvasRef.current;const context = canvas.getContext('2d');// 清除之前的画布内容context?.clearRect(0, 0, canvas.width, canvas.height);canvas.height = viewport.height;canvas.width = viewport.width;console.log('Canvas尺寸设置:', canvas.width, 'x', canvas.height);// 渲染 PDF 页面到 canvasconst renderContext: any = {canvasContext: context,viewport: viewport,};const renderTask = page.render(renderContext);renderTask.promise.then(() => {console.log('页面渲染完成');setIsLoading(false);}).catch((renderError) => {if (!abortController.signal.aborted) {setError('页面渲染失败: ' + renderError.message);}});});}).catch((error) => {if (!abortController.signal.aborted) {setError('PDF加载失败: ' + error.message);console.error('Error loading PDF:', error);}}).finally(() => {if (!abortController.signal.aborted) {setIsLoading(false);}});return () => {abortController.abort();const canvas = canvasRef.current;if (canvas) {const context = canvas.getContext('2d');context?.clearRect(0, 0, canvas.width, canvas.height);}};}, [pdfUrl]);return (<div style={{ position: 'relative', width: '100%' }}><Button onClick={handlePrint}>打印</Button><Button onClick={handleDownload}>下载</Button>{isLoading && <div>加载中...</div>}{error && <div style={{ color: 'red' }}>{error}</div>}<div id="printPdfButton"></div><canvaskey={pdfUrl}ref={canvasRef}style={{ width: '100%', height: 'auto' }}/></div>);
};export default PDFViewer;
使用此组件:
import React from "react";
import "./index.less";
import PDFViewer from "./PDFViewer";const ImgePage = () => {return (<divstyle={{ height: "80vh", border: "1px solid #ddd", position: "relative" }}><PDFViewerpdfUrl={"https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"}/></div>);
};export default ImgePage;
相关文章:
如何在React中集成 PDF.js?构建支持打印下载的PDF阅读器详解
本文深入解析基于 React 和 PDF.js 构建 PDF 查看器的实现方案,该组件支持 PDF 渲染、图片打印和下载功能,并包含完整的加载状态与错误处理机制。 完整代码在最后 一个PDF 文件: https://mozilla.github.io/pdf.js/web/compressed.tracemo…...
React-Markdown 组件底层实现原理详解
如何在 React 中渲染 Markdown 文档 React-Markdown 组件底层实现原理详解 一、核心架构:基于 Unified.js 的编译流水线 React-Markdown 的底层实现依赖于 Unified.js 这一开源内容处理系统,其核心是一个可插拔的编译流水线。整个过程分为四个阶段&…...
基于单片机的防火防盗报警系统设计(论文+源码)
2.1系统的功能及方案设计 本次课题为基于单片机的防火防盗报警系统,其系统采用STC89C52单片机为控制器,并结合SIM800短信模块,DS18B20温度检测模块,MQ-2烟雾检测模块,红外人体检测模块,按键模块,…...
NO.72十六届蓝桥杯备战|搜索算法-DFS|选数|飞机降落|八皇后|数独(C++)
P1036 [NOIP 2002 普及组] 选数 - 洛谷 组合型枚举,路径⾥⾯记录选择数的「总和」。在选出k 个数之后,判断「是否是质数」 #include <bits/stdc.h> using namespace std;const int N 25; int n, k; int a[N];int ret; int path; //记录路径中所…...
网络Socket编程基于UDP协议模拟简易网络通信
一、预备知识 网络编程(Network Programming)是指编写程序来实现计算机网络之间的通信。这通常涉及到使用套接字(sockets)来建立连接、发送和接收数据。 (一)套接字 套接字(Socket࿰…...
rust 使用select退出线程
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct Capture {clear: bool, // ????????interface: String, // ??times: u64, // ?? }pub async fn cmd_capture(State(web_env): State<ArcWebEnv>,Json(args): Json<C…...
C++学习day7
思维导图: 使用vector实现一个简单的本地注册登录系统 注册:将账号密码存入vector里面,注意防重复判断 登录:判断登录的账号密码是否正确 #include <iostream> #include <cstring> #include <cstdlib> #includ…...
【学习笔记】CoACD: 基于碰撞感知凹性与树搜索的近似凸分解
CoACD 基于碰撞感知凹性与树搜索的近似凸分解 CoACD 官方文档 CoACD(Convex Approximation of Complex Decompositions)是一种用于将复杂网格分解为多个凸包的算法, 专为 3D 网格设计了近似凸分解算法,强调在保持物体间潜在碰撞条件的同时减…...
Three.js 系列专题 6:后处理与特效
内容概述 后处理(Post-Processing)是在渲染完成后对画面进行额外的处理,以实现模糊、辉光、颜色校正等效果。Three.js 通过 EffectComposer 提供后处理支持。本专题还将简要介绍着色器和粒子系统,为更复杂的特效打基础。 学习目标 掌握 EffectComposer 的基本使用。实现辉…...
2025 年江苏保安员职业资格考试经验分享
江苏保安行业发展成熟,2025 年考试注重对考生综合素养的考查。报考条件常规,但对诚信记录有额外关注,如有不良信用记录可能影响报考资格。 报名在江苏省各地级市公安局指定点进行,提交资料包括身份证、学历证、个人诚信报告&am…...
亚马逊算法重构消费市场:解码2024年Q1北美站热搜商品的底层逻辑
在跨境电商迈入精细化运营时代的背景下,亚马逊平台最新发布的《2024年Q1零售搜索趋势报告》揭示了算法驱动下的消费新图景。数据显示,北美站点月均超300万人次重复搜索特定品类商品,健康生活、智能家居等五大领域形成持续增长极。这份由亚马逊…...
powershell绑定按钮事件的两种方式
写一个powershell的简单GUI做本地任务,试验出2个方法: 方法1: function btn1_click {write-host $text1.Text -ForegroundColor Green -BackgroundColor Black }$btn1.Add_Click({btn1_click})方法2: $btn2_click {write-host $…...
LearnOpenGL——OIT
教程地址:简介 - LearnOpenGL CN 简介 原文链接:LearnOpenGL - Introduction 前言 在混合(Blending)章节中,我们介绍了颜色混合的主题。混合是在3D场景中实现透明表面的方法。简而言之,透明度涉及到在计算…...
【蓝桥杯】Python大学A组第十五届省赛
1.填空题 1.1.拼正方形 问题描述 小蓝正在玩拼图游戏,他有个的方块和个的方块,他需要从中挑出一些来拼出一个正方形。 比如用个和个的方块可以拼出一个的正方形;用个的方块可以拼出一个的正方形。 请问小蓝能拼成的最大的正方形的边长为多少。 import math # 2*2的个数 a =…...
使用JS+HTML+CSS编写提词器实例
手搓提词器网页版,有些BUG但是基本功能使用没有问题,有需要的可复制粘贴,BUG自行修复。下面直接进入代码: <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><me…...
人工智能基础知识详解:从概念到前沿技术与应用
在数字化浪潮席卷全球的今天,人工智能(Artificial Intelligence,简称AI)已不再是科技前沿的神秘概念,而是融入我们日常工作的实用工具。从智能语音助手到自动驾驶汽车,从医疗影像诊断到生成式艺术创作&…...
重温经典,畅享怀旧游戏盛宴
FC街机是一款专为安卓设备设计的经典游戏合集应用,它将玩家熟悉的红白机(FC)游戏体验带到了移动设备上。这款应用不仅提供了丰富的游戏选择,还通过优化的界面和操作,让玩家能够随时随地享受经典游戏的乐趣。 游戏非常…...
CPU 压力测试命令大全
CPU 压力测试命令大全 以下是 Linux/Unix 系统下常用的 CPU 压力测试命令和工具,可用于测试 CPU 性能、稳定性和散热能力。 1. 基本压力测试命令 1.1 使用 yes 命令 yes > /dev/null & # 启动一个无限循环进程 yes > /dev/null & # 启动第二个进…...
Windows 系统下用 VMware 安装 CentOS 7 虚拟机超详细教程(包含VMware和镜像安装包)
前言 资源 一、准备工作 (一)下载 VMware Workstation (二)下载 CentOS 7 镜像 二、安装 VMware Workstation(比较简单,按下面走即可) 三、创建 CentOS 7 虚拟机 四、安装 CentOS 7 系统…...
QLineEdit的提交前验证
QLineEdit是pyqt中常用的输入控件,默认情况下,它可以接受键盘输入的任何可打印字符。有时候,我们需要在用户提交前对其输入的内容先行验证,当用户输入不符合预期时予以清空,这就需要对QLineEdit控件进行以下操作&#…...
【Linux高级IO(二)】初识epoll
目录 1、epoll的接口 2、epoll原理 3、epoll工作方式 1、epoll的接口 #include <sys/epoll.h> 1、int epoll_create(int size) :创建epoll模型 返回值是一个文件描述符,创建一个struct file结构体,指向epoll模型,返回的…...
2018年真题
数学基础 一、 (共4分)用逻辑符号表达下列语句(论域为包含一切事物的集合) 1、(2分)集合A的任一元素的元素都是A的元素 经过对图片文字的识别与逻辑分析,结果如下: 符号定义&…...
Linux xxd命令
目录 一. xxd命令简介二. 简单使用三. -p选项纯16进制输出四. -r选项将十六进制还原成原始内容五. 小应用 一. xxd命令简介 xxd 是一个将文件或输入内容转换为十六进制(Hex Dump)格式的工具,也可以将十六进制恢复成原始数据。 它在调试二进制…...
高校实验室安全数智化分级分类管理-危化品管理LIMS
一、背景与依据 传统实验室安全管理如同老式挂钟,齿轮咬合处总会随时间产生间隙。为进一步规范学校实验室建设与适用,从源头管控实验室和实验项目安全风险,确保教学科研活动安全有序开展,分级分类体系构建如同绘制实验室的"…...
春芽儿智能跳绳:以创新技术引领运动健康新潮流
在全球运动健康产业蓬勃发展的浪潮中,智能健身器材正成为连接科技与生活的重要纽带。据《中国体育用品产业发展报告》显示,2023年中国智能运动装备市场规模突破千亿元,其中跳绳类目因兼具大众普及性与技术升级空间,年均增速超30%。…...
Fast网络速度测试工具
目录 网站简介 功能特点 测试过程 为什么使用Fast 如果网络速度不达标 网站简介 Fast是一个由Netflix提供的网络速度测试工具,主要用来测试用户的互联网下载速度。它以其简洁的界面和快速的测试过程而受到用户的欢迎。 功能特点 下载速度测试:这是…...
java的文件输入输出流(FileInputStream、FileOutputStream、FileReader、FileWriter)
文章目录 文件输入输出流1 java I/O 流的原理流的分类 2 FileInputStream 文件字节输入流3 FileOutputStream 文件字节输出流4 使用文件字节输入输出流完成对文件的拷贝5 FileReader 文件字符输入流6 FileWriter 文件字符输出流 文件输入输出流 1 java I/O 流的原理 I/O 是 In…...
stm32week10
stm32学习 七.CAN 7.STM32 CAN外设 标识符过滤器: 每个过滤器的核心由两个32位寄存器组成:R1[31:0]和R2[31:0] FSCx:位宽设置,置0为16位,置1为32位 FBMx:模式设置,置0为屏蔽模式,…...
【UnityEditor扩展】如何在 Unity 中创建棱柱体(用作VR安全区检测),同时在编辑器插件中实现与撤销/恢复功能
Unity 编辑器扩展:3D 空间中绘制安全区棱柱体(含撤销/恢复/保存/读取) 在虚拟现实(VR)和增强现实(AR)开发中,安全区是保障用户安全的重要组成部分。通过精确控制用户活动范围&#…...
C++小游戏 合集
生化危机 #include<conio.h> #include<string.h> #include<stdio.h> #include<stdlib.h> #include<windows.h> #include<time.h> #include<direct.h> int n,round,gold0; bool f1,f2,f3,deadfalse,PC_64Bit; char str[4]; struct n…...
常州 d??
回来了! 今天(?发出来的时候可能已经是第二天了吧 真的爆零了qaq 挺难的 最高分只有100 而且t2t3t4一个人都没拿分qaq 这段时间在写网络流 唉博客还是不要写太水了 抄一点网络流代码上来 EK不写了 dinic会就行了 板子题洛谷p3376 dini…...
juc并发包的常用类、线程安全实现方式、锁机制及 JVM 优化策略
juc并发包的常用类、线程安全实现方式、锁机制及 JVM 优化策略 1. juc包下的常用类:线程池:并发集合类:同步工具类:原子类: 2. 怎么保证多线程安全:3. Java中常用锁及使用场景:4. 线程同步的方法…...
学习日记-0407(Inductive Matrix Completion Using Graph Autoencoder)
论文阅读:Inductive Matrix Completion Using Graph Autoencoder 代码:swtheing/IMC-GAE 总而言之就是设计了一个不同评分下的邻接图,然后对每一个评分图T进行独立GNN编码。这个 GNN 编码器主要由三个组件构成:嵌入层、消息传递层…...
FPGA入门:状态机思想编程
一、状态机思想编写流水灯 1、状态机思想的概念 状态机思想是一种用于描述和处理具有多个状态以及状态之间转换关系的系统的思维方式。以下是对其主要概念、应用场景和优势的介绍: 主要概念 状态:指系统在某一时刻的状况或条件。例如,在一…...
【电路笔记】-切换触发器
切换触发器 文章目录 切换触发器1、概述2、切换触发器3、JK触发器转换为D型触发器4、D型触发器转换为切换触发器切换触发器是常用的时序逻辑电路,作为单个比特双稳态存储元件,在计数器、存储器设备中经常使用,或作为响应时钟脉冲的分频器。 1、概述 切换触发器是另一种基于…...
示例项目文档模板集:TaskBoard 任务管理系统
一套完整、高可读性、结构清晰的项目文档模板,适用于中小型软件项目的设计、开发、交接与展示全流程。 📌 项目概述文档(overview.md) 📂 项目名称:TaskBoard 🧭 项目简介 TaskBoard 是一款专为敏捷团队打造的任务管理系统,支持任务分配、状态追踪与协作沟通,帮…...
TF-IDF忽略词序问题思考
自从开始做自然语言处理的业务,TF-IDF就是使用很频繁的文本特征技术,他的优点很多,比如:容易理解,不需要训练,提取效果好,可以给予大规模数据使用,总之用的很顺手,但是人…...
代理模式的优缺点是什么?
什么是代理模式? 代理模式(Proxy Pattern)是一种结构型设计模式,它通过创建代理对象来控制对原始对象的访问。 这种模式在前端开发中广泛应用,特别是在需要控制对象访问、添加额外逻辑或优化性能的场景中。 核心…...
十分钟上手:Distilling the Knowledge in a Neural Network
概述:知识蒸馏是一种模型压缩技术,通过让轻量化的学生模型模仿复杂教师模型的输出概率分布,结合软目标和硬目标进行训练,从而将教师模型的泛化能力迁移至学生模型,实现小模型的高效部署而不显著降低性能。 硬目标&…...
百度的deepseek与硅基模型的差距。
问题: 已经下载速度8兆每秒,请问下载30G的文件需要多长时间? 关于这个问题。百度的回答如下: 30GB文件下载时间计算 理论计算(基于十进制单位): 单位换算 文件大小:3…...
OpenCV 图形API(18)用于执行两个矩阵(或数组)的逐元素减法操作函数sub()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 描述 计算两个矩阵之间的逐元素差值。 sub 函数计算两个矩阵之间的差值,要求这两个矩阵具有相同的尺寸和通道数: dst ( I ) src…...
布谷一对一直播源码android版环境配置流程及功能明细
一:举例布谷交友(一对一直播源码)搭建部署的基本环境说明 1. 首先安装Center OS 7.9系统,硬盘最低 40G 2. 安装宝塔环境 https://bt.cn(强烈推荐使用) 3. 安装环境 ● PHP 7.3(安装redis扩展…...
#MongoDB 快速上手
docker pull mongo docker run -d --name my-mongo -p 27017:27017 mongo docker exec -it my-mongo mongo 🚪进入 Mongo Shell 后的第一步 你进入后会看到类似提示符: >说明已经进入 Mongo Shell,现在就可以操作数据库了。 …...
docker相关命令
常用命令 #创建并启动 docker-compose up -d # 启动之后就可以通过浏览器访问了 #停止并删除 docker-compose down #重启 docker-compose restart #停止 docker-compose stop #启动 docker-compose startdocker search #搜索镜像(只搜索官方仓库的,官方仓库地址&am…...
浅谈进程与程序的区别
如大家所了解的,进程与程序是有区别的。 下面做了一个总结,供大家参考、学习: 1. 程序是指令的有序集合,是一个静态的概念,其本身没有任何运行的含义。进程是程序在 CPU 上的一次执行过程,是一个动态的概…...
redis 和 MongoDB都可以存储键值对,并且值可以是复杂json,用完整例子分别展示说明两者在存储json键值对上的使用对比
Redis 存储 JSON 键值对示例 存储操作: // 存储用户信息(键:user:1001,值:JSON对象) SET user:1001 {"name":"Alice", "age":30, "address":"New York&quo…...
基于chatgpt得到的生活成本计算
意大利的生活成本因城市而异,比如米兰和罗马相对较贵,而南部城市如那不勒斯或巴勒莫则便宜一些。下面是意大利大致的基本生活成本和费用明细(以欧元€为单位,2025年初数据为基础,具体数值可能随时间和汇率略有变化&…...
C和C++有什么区别?
C和C是两种不同的编程语言,虽然它们有许多相似之处,但也存在一些关键的区别。 C是一种过程化编程语言,专注于函数和流程控制,非常适合系统级编程。而 C是一种面向对象编程语言,支持类、对象和封装、继承、多态等特性。…...
力扣1338 === 贪心算法解决数组减半问题
目录 问题分析 方法思路:贪心算法 步骤分解 代码解释 复杂度分析 正确性证明 示例验证 边界情况 总结 要解决这个问题,我们需要找到最少需要删除的不同整数集合,使得剩余的元素个数不超过原数组的一半。以下是对该问题的详细分析和解…...
企业知识库如何搭建?应对高频咨询的AI自助问答系统
在客户服务和内部沟通中,“同样的问题被反复问”、“信息找不到”、“新员工上手慢”等现象屡见不鲜。为了提升企业运营效率,越来越多企业开始重视知识库建设,而“企业知识库如何搭建”也成为热门话题。 尤其在AI技术快速发展的今天…...