前端 PDF 导出实战:jsPDF+html2canvas 实现分页与清晰度控制

前端导出 PDF 这件事,绕不开两个名字:jsPDF 和 html2canvas。我前阵子做项目验收报告导出,用户要求页面长什么样,PDF 就长什么样,思来想去还是回到了这套组合上。它并不完美,但如果目标是“所见即所得”,它比浏览器打印和纯服务端渲染都更快落地。写这篇上篇,是把我在实际项目中踩过的分页、模糊、跨域、超长页面问题整理出来。适合前端开发、全栈,以及所有需要把网页内容转成 PDF 交付的人参考。

这篇文章不是只贴一个能跑的按钮就完事,我会把“为什么这么写”也讲清楚。比如为什么要用 html2canvas 截图而不是直接调 jsPDF 写文本,为什么分页要自己算像素高度,为什么导出图片总是发虚,为什么长页面导出时浏览器动不动崩溃。这些问题单看官方文档很难串起来,但它们是实际业务里最消耗时间的部分。上篇先讲原理、最小可运行示例、分页切割、清晰度控制和常见坑,下篇再往深处讲长文档的逐区块拼接、元素避让切断、水印页码和 PDF 压缩。

1. 为什么方案是 jsPDF + html2canvas,而不是浏览器打印

1.1 浏览器打印到底输在哪

很多人第一反应是 window.print(),再加上几个 @media print 样式,页面就能打印成 PDF。这个方案对“简单内容”没问题,比如一篇文章、一个表单。但真正做导出功能时,浏览器打印的弱点会一个接一个冒出来:不同操作系统的打印默认页边距不一样,用户浏览器设置的缩放比例会直接改变输出结果,Chrome 和 Safari 在“另存为 PDF”时对背景色的处理也不同。最难受的是分页控制,你很难精确告诉浏览器“这块内容必须放在一页里”,更别提在每一页底部自动加页码、页眉、水印。

如果你尝试过用打印方案做带复杂图表的报告,很快就会遇到一个局面:页面预览里一切正常,导出的 PDF 里某一行字被切掉一半,某几个色块消失了,页脚的日期还可能重叠。原因很简单,浏览器打印走的是另一个渲染通道,它和屏幕渲染天生不是完全等价。为了彻底摆脱这种“打印预览不可控”,我们才会走向截图方案。

1.2 两个库的分工:截图后贴图

jsPDF 和 html2canvas 的组合,本质是“截图后贴图”。html2canvas 负责把指定的 DOM 节点克隆一份、解析样式、最终绘制成一张 canvas 位图;jsPDF 负责创建 PDF 文档,把这张位图以页面为画布贴进去,然后保存。也就是说,你最终拿到的 PDF 是一页一页的图片,里面的文字不能被选中,文件体积也会比纯文本 PDF 大不少。

这一点非常重要,它决定了后续几乎所有的坑。比如分页时不能直接用 jsPDF 的 text 方法换行,因为整页都是位图;又比如文字清晰度取决于 canvas 的分辨率,而不是 PDF 自身的矢量精度。理解了这一点,再看后面的代码就不会觉得奇怪。如果你需要的是“页面视觉完全一致”的合同、报告、截图快照,位图方案是性价比最高的;如果你需要的是“可搜索的小体积 PDF”,那就该换 jsPDF.text 加手动排版,而不是套截图。

1.3 这个组合的适用边界

用这套方案前,先明确目标。我个人的判断标准是:

  • 适合:视觉型文档,例如运营活动页面截图、数据看板快照、带复杂样式模板的合同、验收报告、电子签章页面。
  • 适合:页面元素不能被轻易重新排版,后端生成成本高,或者需要在前端即时导出的场景。
  • 不适合:几百页的电子书、大量纯文本内容、需要选中复制和检索的文件。
  • 不适合:超大型表格跨页展示,因为截图分页没法做到表头自动重复、行不被切断这样的复杂逻辑。

在业务里,需求方通常只关心“样式别乱、字别糊、页数对”。这时候 jsPDF 加 html2canvas 是唯一能在最短时间内满足需求的路线。先把边界定好,后面遇到问题才不会硬扛。

2. 最小可运行示例:从一个导出按钮到一张 PDF

2.1 安装和引包

如果你用 npm 或现代构建工具,安装非常简单:

npm install jspdf html2canvas

注意包名是jspdf,不是jsPDF。引入时一般这样写:

import { jsPDF } from 'jspdf'; import html2canvas from 'html2canvas';

如果你不想用打包工具,也可以直接引 dist 目录下的 UMD 文件,脚本加载后全局变量分别是window.jspdf.jsPDF和window.html2canvas。因为 html2canvas 在 npm 上同时提供 commonjs 和 es module 两种格式,平时容易踩到引入错误,建议项目里统一用按需导入的方式。

版本方面我一般会锁定一个稳定版本,不盲目追新。早几年 html2canvas 0.x 和 1.x 的行为差别很大,升级后经常出现样式渲染不一致,所以在生产项目里我会把版本锁死,后续只做安全补丁升级。

2.2 第一步导出单页 PDF

我们先写一个最简单、能跑的版本。假设页面里有这样一段内容:

<div id="exportContent"> <h2>项目验收报告</h2> <p>这是一段需要原样导出到 PDF 的内容。</p> <div class="chart-box">这里是图表区域</div> </div> <button id="exportBtn">导出 PDF</button>

对应的导出函数如下:

async function exportSinglePagePdf() { const element = document.getElementById('exportContent'); const canvas = await html2canvas(element, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }); const pdf = new jsPDF('p', 'mm', 'a4'); const pageWidth = pdf.internal.pageSize.getWidth(); // 210 const pageHeight = pdf.internal.pageSize.getHeight(); // 297 const margin = 10; const contentWidth = pageWidth - margin * 2; const imgHeight = canvas.height * contentWidth / canvas.width; pdf.addImage( canvas.toDataURL('image/jpeg', 0.95), 'JPEG', margin, margin, contentWidth, imgHeight ); pdf.save('report.pdf'); }

这段代码虽然只有十几行,但它已经包含了这套组合最核心的三步:html2canvas 生成 canvas,jsPDF 创建空白 PDF,addImage 把 canvas 放进 PDF。第一次跑通后,你会发现导出的 PDF 内容可能超出页面底部,没关系,这是单页版,下一篇开始就处理分页。

2.3 为什么必须换算毫米单位

这里有个很容易被忽略的单位问题。jsPDF 默认使用毫米作为单位,而 html2canvas 给出的 canvas.width 和 canvas.height 是像素,二者不能直接混用。如果你把 canvas.width 直接传给 addImage,图片在 PDF 里会按 1 像素 = 1 毫米绘制,结果就是图片大到只剩一角。

正确做法是先确定一个显示宽度,然后按 canvas 的宽高比算出高度。上面代码里的canvas.height * contentWidth / canvas.width就是这一层换算。拿到毫米高度后,再决定要不要分页、需要几张 A4 纸。很多初学者导出的 PDF 是空白页或半页,十有八九是这里没换算对。

3. 多页 PDF 的核心:长页面截图后的分页切割逻辑

3.1 为什么不能直接把完整 canvas 塞进 jsPDF

业务里的 PDF 通常不止一页,但 html2canvas 只能产生一张完整的长图。如果你把一整张超高 canvas 直接 addImage 到 PDF,结果只有两种:要么塞进一页导致内容压缩到看不清,要么写到页面边界外,最终只显示一部分。

jsPDF 本身不会帮你自动分页。它的 addPage 只是增加一张空白画布,没有“检测内容溢出然后自动换页”的能力。所以分页这事必须在前端手动完成:先确定每一页 PDF 能显示多少内容,再按这个高度把 canvas 切成若干段,每一段通过 addImage 放到单独的一页里。

3.2 按高度切片:一份可复制的分页代码

我直接给出实际项目里验证过的分页切割函数。这个函数不是最复杂的,但足够稳定,能够处理大多数长页面:

async function exportContentToPdf(elementId) { const element = document.getElementById(elementId); const canvas = await html2canvas(element, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }); const pdf = new jsPDF('p', 'mm', 'a4'); const pageWidth = pdf.internal.pageSize.getWidth(); const pageHeight = pdf.internal.pageSize.getHeight(); const margin = 10; const contentWidth = pageWidth - margin * 2; const contentHeight = pageHeight - margin * 2; const canvasWidth = canvas.width; const canvasHeight = canvas.height; const pxPerMm = canvasWidth / contentWidth; const pageHeightPx = contentHeight * pxPerMm; let renderedHeightPx = 0; let pageIndex = 0; while (renderedHeightPx < canvasHeight) { const sliceHeightPx = Math.min(pageHeightPx, canvasHeight - renderedHeightPx); const sliceCanvas = document.createElement('canvas'); sliceCanvas.width = canvasWidth; sliceCanvas.height = sliceHeightPx; const ctx = sliceCanvas.getContext('2d'); ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, canvasWidth, sliceHeightPx); ctx.drawImage(canvas, 0, renderedHeightPx, canvasWidth, sliceHeightPx, 0, 0, canvasWidth, sliceHeightPx); if (pageIndex > 0) { pdf.addPage(); } const imgData = sliceCanvas.toDataURL('image/jpeg', 0.92); const imgWidthMm = contentWidth; const imgHeightMm = sliceHeightPx / pxPerMm; pdf.addImage(imgData, 'JPEG', margin, margin, imgWidthMm, imgHeightMm); renderedHeightPx += sliceHeightPx; pageIndex++; } pdf.save('multipage.pdf'); }

这段代码的核心思路是:先算出 A4 可打印区域对应的像素高度pageHeightPx,然后从 canvas 顶部开始,每次截取一小段 canvas 作为独立的切片,再换算成毫米高度放入 PDF。切片之间不会重叠,也不会遗漏,最终页数就是Math.ceil(canvasHeight / pageHeightPx)。对于内容刚好卡在边界的情况,最后一页高度不足一页是正常的。

3.3 避免文字被拦腰切断:元素边界对齐的思路

按固定高度切片有一个绕不开的问题:如果页面内容有标题、段落、卡片,切片点可能会落在一行文字中间,输出后看起来就像一刀切在了文字上。要彻底解决,需要做“元素边界对齐”。

思路是这样的:不要在sliceHeightPx处直接切,而是往前找一个“安全切点”。一个常见做法是先收集目标区域内所有可能被截断的元素,例如设置了page-break-inside: avoid样式的卡片、带边框的盒子、表格行,然后获取它们的offsetTop和offsetHeight。当计算出的当前切割点落进某个元素中间时,就把切割点调整到这个元素底部或顶部。因为 html2canvas 的 canvas 是基于 DOM 克隆渲染,canvas 像素坐标和页面元素坐标存在一个 scale 倍数的对应关系,调整时记得乘以scale。

下面是一个简化版的伪代码,演示怎么求出最接近目标高度但不会切断元素的分页点:

function findSafeCutY(els, startY, endY, scale) { let safeY = endY; for (const el of els) { const top = el.offsetTop * scale; const bottom = (el.offsetTop + el.offsetHeight) * scale; if (bottom > startY && bottom < endY) { // 当前切割点在元素内部,让出元素底部 safeY = Math.min(Math.max(safeY, bottom), endY); } } return safeY; }

这个函数不完美,但对于块状卡片较多的页面,已经能把切断率从“经常发生”降到“偶尔发生”。真正要求高的场景,我会在下篇讲按区块逐段截图,也就是每个内容块单独截图排页,而不是整页 canvas 一刀切,那样永远不会切断块内部。

4. 清晰度、跨域图片和文件体积:三个绕不开的细节

4.1 scale 参数不是越大越好

很多教程会告诉你用scale: 2,但很少有人解释为什么。html2canvas 默认的 scale 是 1,也就是说生成的 canvas 像素数等于元素 CSS 像素数。在普通屏幕下,这看起来没问题,但 PDF 里的图片是按物理尺寸呈现的,同样的内容如果分辨率不足,放大后边缘发虚、文字发糊。

使用scale: 2相当于把 canvas 的宽和高都放大一倍,图片信息量变成原来的 4 倍。用scale: window.devicePixelRatio则能匹配 Retina 屏幕的物理像素密度,导出后和你在屏幕上看到的清晰度最接近。但 scale 不是越大越好,因为 canvas 的内存占用按面积增长,执行时间也明显增加。我实际测试过的体感如下:

scale清晰度导出时间内存压力适用场景
1一般快低长页面快速预览,不追求打印
2清晰中等中等大多数业务报表、合同
3很清慢高精细设计稿、印刷需求
4及以上边际收益低很慢可能崩溃不建议生产环境使用

经验上,先固定 scale 2,如果导出后小字仍然发虚,再加到 3。多数屏幕尺寸下,2 到 3 已经是清晰度与性能最好的平衡点。

4.2 JPEG 与 PNG 的选择

canvas 转图片时,常用toDataURL('image/jpeg', quality)或toDataURL('image/png')。JPEG 体积小,但对文字边缘有压缩失真,而且 JPEG 不支持透明背景;PNG 清晰无损,但文件体积经常是 JPEG 的几倍甚至十几倍。

所以我习惯这样做:页面底色是白色或浅色时,用 JPEG 加 0.92 质量;页面有透明底色或大量小字号文字时,先用 PNG 导出一版,确认体积可接受再上线。还有一个细节:在切片 canvas 上绘制前,我会先填充白色背景再做 drawImage,这样即使页面某处原本透明,导出 JPEG 后也不会变成黑色区块。

4.3 跨域图片与字体加载

html2canvas 本质上是在浏览器里重新绘制 DOM,它读取图片数据时会受到 canvas 跨域限制。如果你的页面图片来自另一个域名,并且图片服务没有返回Access-Control-Allow-Origin头,canvas 会被污染,轻则图片区域空白,重则整张 canvas 无法导出。

处理方式分两步。第一步,给图片加上跨域属性并确保服务端允许跨域:

const img = new Image(); img.crossOrigin = 'anonymous'; img.src = 'https://example.com/image.jpg'; await img.decode();

第二步,在 html2canvas 里开启useCORS: true,同时设置allowTaint: false。如果图片服务端无法改响应头,那就只能走图片转 base64、同域代理这类方案。还有一个经常被忽略的点:导出前要等字体加载完成,否则 canvas 里可能出现后备字体,和页面显示不一致。建议在调用 html2canvas 前执行:

await document.fonts.ready;

这能规避相当一部分“导出后字体变了”的投诉。

5. 实测高频坑:从白屏、截断到内存暴涨

5.1 html2canvas 不支持的 CSS 特性

html2canvas 不是浏览器本身,它支持的 CSS 特性是有边界的。我踩得最多的三个坑:backdrop-filter完全不支持,元素带毛玻璃效果时导出往往只保留半透明底色;复杂box-shadow虽然能渲染但开销很大,多了会明显拖慢导出;部分现代 CSS 颜色函数和混合模式在 1.4.1 版本下也可能出问题。遇到这种场景,我的原则是导出页尽量用简单可靠的颜色和布局,或者准备一套专门用于导出的“降级样式”,利用 html2canvas 的onclone回调在克隆文档里临时替换 class。

onclone是 html2canvas 提供的一个很有用的钩子,它会在克隆文档准备完成后、截图渲染前执行。你可以在这里隐藏不需要导出的元素、给节点加替代类名,甚至提前把远程图片替换成本地图片,都不会影响页面本身。

5.2 导出结果白屏的常见原因

白屏不是“bug”,而是某个前置条件没满足。最常见的几个原因:目标元素是display: none或者宽度为 0;元素在 iframe、webgl 里,html2canvas 抓不到;图片跨域导致 canvas 被污染;导出前元素尚未渲染完成。排错顺序我一般按照“可见性 -> 尺寸 -> 资源加载 -> 跨域”来走。

如果你碰到的是“预览时一切正常,导出后白屏”,先打开控制台看是否有Tainted canvases或Unable to load image的报错。没有报错的话,检查一下目标元素是否被 CSS 隐藏了,或者是否用了 canvas 绘制但来源是跨域图片。html2canvas 的logging: true选项也能帮你看到它具体卡在哪一步。

5.3 超长页面的内存与 canvas 尺寸上限

这个坑最隐蔽,也最容易在生产环境炸。浏览器对 canvas 有一个最大面积限制,不同浏览器阈值不同,但共同点是:当你的页面高度超过几千像素,再加上 scale 2 的放大,canvas 总像素数很快会冲破安全线,导致 html2canvas 报错、浏览器标签页崩溃,或者生成的白屏 PDF。

我习惯把canvas.height > 6000作为警戒线。一旦超过,就不再对整个页面做一次 html2canvas,而是把页面拆成几个区块,对每个区块分别导出,再按区块拼接成分页。或者干脆把 scale 降到 1,牺牲一部分清晰度来保稳定。具体判断可以在导出函数开头加一段:

const canvas = await html2canvas(element, { scale: 2 }); if (canvas.height > 8000 || canvas.width * canvas.height > 12000000) { console.warn('canvas 尺寸接近浏览器上限,建议拆分导出'); }

除了 canvas 上限,切片时也要注意内存。上面分页代码里,每次循环创建的 sliceCanvas 会在循环结束后释放,但如果一次性把几十个切片都放进数组,内存峰值会很高。我这里直接把它写在导出函数内部,用完即丢,效果更好。

5.4 版本锁定和效果差异

html2canvas 1.4.1 和 1.4.0 之间已经有一些渲染差异,更不要说 0.5 版本和 1.x 的巨大差别。我在不同版本之间切换测试过,同样的页面,旧版本可能边框宽度更粗,新版本对 CSS 变量的支持更好。生产项目中如果导出效果已经稳定,尽量不要随意升级。jsPDF 也一样,早期 1.x 和 2.x 在pdf.internal.pageSize和 addImage 行为上都有变化,代码里写死版本号,升级前单独回归测试导出功能,会省掉很多线上问题。

6. 上篇的边界与下篇的方向

6.1 这套方案的适用边界:真实体感

写到这里,我再分享一点个人体感。jsPDF 加 html2canvas 的组合,放到生产环境里完全可用,但它更像“重剑无锋”,谈不上精巧。你付出的是文件体积和执行时间,换来的是开发速度和样式还原度。如果你的需求是把几千字纯文本变成一份可搜索的 PDF,我会建议你别用截图,直接 jsPDF.text 一行行排版,生成的文件又小又干净。但如果是合同、验收报告、运营模板,用户不会关心 PDF 里能不能选词,他只关心“看起来和页面一样”,那么截图方案就是最优解。

实际项目里最让我印象深刻的教训是:先确认页面设计是否适合截图导出,再动手写代码。如果页面本身用了大量backdrop-filter、动态背景、外部 iframe,那无论你用多新的版本都要踩坑。提前和设计师、产品确认导出页的视觉边界,比事后修补 CSS 高效得多。

6.2 下篇继续深入的内容

这篇上篇覆盖的是基础方案和最常见的坑。真正复杂的项目里,还会有几个更深入的话题没有展开:长文档如何按内容区块逐段截图,做到完全不会切断标题;如何在每一页 PDF 上统一叠加页码、水印、页眉;前端生成多份 PDF 时如何压缩体积和内存占用;移动端 Safari 下的兼容性处理。这些我打算放到下篇里逐一讲。

如果你现在刚好在用这套组合做导出,建议先把上篇的分页切割和 scale 选择跑通,再去研究避让算法。下篇会在这个基础上继续,尤其是“元素级避让”“逐区块拼接”这两个方向,能够显著提升导出的精致程度。到时候见。

相关新闻

PyTorch深度学习教材学习路线:从环境搭建到代码复现

PyTorch深度学习教材学习路线:从环境搭建到代码复现

你有没有过这样的经历&#xff1a;看到“364页PyTorch版开源深度学习教材”“终于开源了”“熬夜也要啃完”这种标题&#xff0c;手指一滑就点了收藏&#xff0c;然后那份PDF就安静地躺在网盘里&#xff0c;再也没被打开过。这套被追着转了无数次的PyTorch版《动手学深度学习》…

2026/10/20 5:00:56 阅读更多 →
人脸识别DEMO解剖:从检测对齐到动态阈值的鲁棒性设计

人脸识别DEMO解剖:从检测对齐到动态阈值的鲁棒性设计

简介&#xff1a;这是一份面向Android开发者的人脸识别技术实战DEMO&#xff0c;聚焦移动端人脸检测、特征提取与匹配全流程实现&#xff0c;适用于安全门禁、身份核验等场景的快速原型开发与SDK集成学习。资源包含1758个文件&#xff0c;主体为Java源码&#xff08;42个&#…

2026/10/20 5:00:56 阅读更多 →
大模型工具使用与函数调用:从文本生成到动手执行

大模型工具使用与函数调用:从文本生成到动手执行

最近我在整理某高校AI系统课程CS329Z的第四讲资料时&#xff0c;把“工具使用与函数调用”这个主题从头到尾重新捋了一遍。说实话&#xff0c;这一讲在我看来是整个AI应用开发里最容易被低估的章节——很多人觉得函数调用不就是让模型输出一段JSON吗&#xff0c;但真正落地过的…

2026/10/20 5:00:56 阅读更多 →

最新新闻

投机推理:突破大模型自回归瓶颈的并行验证加速技术

投机推理:突破大模型自回归瓶颈的并行验证加速技术

1. 大模型推理的“车堵一条道”困局老做推理优化的朋友都有过这种体验&#xff1a;GPU 明明算力爆炸强&#xff0c;但每次生成一个 token 都得几毫秒甚至几十毫秒&#xff0c;batch 稍微大一点&#xff0c;延时就像坐火箭一样往上蹿。一开始我们都骂“模型太大了”&#xff0c;…

2026/10/20 7:13:55 阅读更多 →
Spring Boot+微信小程序汽车用品商城开发:从车型适配到支付回调

Spring Boot+微信小程序汽车用品商城开发:从车型适配到支付回调

先说句实在话&#xff1a;汽车用品这个品类&#xff0c;看着和普通电商差不多&#xff0c;但等你真把“Spring Boot 微信小程序”这套销售系统落地时&#xff0c;会发现一堆让人挠头的细节——车型适配怎么做、库存怎么拆、微信支付回调怎么验签、审核类目怎么过。这篇文章不聊…

2026/10/20 7:13:55 阅读更多 →
Delta机器人正逆运动学C#实现:高精度实时解算模块

Delta机器人正逆运动学C#实现:高精度实时解算模块

简介&#xff1a;本资源是一套面向机器人控制开发者与自动化专业学习者的Delta并联机器人运动学算法C#实现工程&#xff0c;聚焦高速拾放场景下的正向/逆向运动学求解这一核心难点。项目以Visual Studio解决方案形式组织&#xff0c;含41个文件&#xff0c;涵盖6个核心C#源码&a…

2026/10/20 7:13:55 阅读更多 →
无语言基础模型如何实现通用视觉异常检测?原理与落地

无语言基础模型如何实现通用视觉异常检测?原理与落地

这两年做计算机视觉落地&#xff0c;特别是工业视觉相关的项目时&#xff0c;我几乎绕不开一个词&#xff1a;异常检测。不管是手机外壳上的划痕、布料上的断纱&#xff0c;还是电路板上的漏焊&#xff0c;本质都是同一件事——在绝大多数正常样本中&#xff0c;把分布外的那一…

2026/10/20 7:13:55 阅读更多 →
Chatterbox TTS实战:开源语音合成全家桶部署与调优指南

Chatterbox TTS实战:开源语音合成全家桶部署与调优指南

去年底我就在关注开源TTS圈子的动向&#xff0c;当时看到Chatterbox TTS这个项目时&#xff0c;第一反应是"又一个语音合成模型"&#xff0c;但真正部署完、跑了几天推理之后&#xff0c;我改变了看法——这玩意儿不是单个模型&#xff0c;而是一整套开箱即用的语音合…

2026/10/20 7:13:55 阅读更多 →
微服务商城对接支付宝支付:独立支付服务与异步回调实战

微服务商城对接支付宝支付:独立支付服务与异步回调实战

1. 支付服务在微服务架构里的定位做电商项目&#xff0c;前面把商品、库存、订单、用户这些服务都搭起来之后&#xff0c;早晚要面对一个绕不开的环节——支付。我这次在基于Vue和SpringCloudAlibaba的微服务商城项目里&#xff0c;把支付单独拆成了一个平台服务&#xff0c;专…

2026/10/20 7:12:54 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →