html2canvas + jspdf实现页面导出成pdf

html2canvas + jspdf实现页面导出成pdf

封装一个好用的页面导出 PDF 工具 Hook (html2canvas + jspdf)

在最近的一个项目中,遇到一个将页面内容(详情页)导出为 PDF的需求,但是好像目前没有直接把dom转成pdf这样一步到位的技术,所以自己封装了一个间接转换的方法,基于 Vue3 + TypeScript 的通用 Hook 封装,利用 html2canvas 和 jspdf 实现网页内容导出为 PDF,并解决了 滚动截断 、 清晰度不足 以及 自动分页 等常见问题。

一、 技术选型

  • html2canvas : 将 DOM 元素转换为 Canvas 图片。
  • jspdf : 将 Canvas 图片生成 PDF 文件。
  • 封装 : 使用 Hook 方式封装,方便复用。

二、 核心痛点与解决方案

在实现过程中,我们通常会遇到以下几个坑:

  1. 导出内容不全 :如果页面有滚动条,直接截图只能截取可视区域。
    • 解法 :在截图前将 DOM 高度设置为 auto ,并获取 scrollHeight 传递给 html2canvas 的 windowHeight 参数。
  2. 图片模糊 :默认截图出来的 PDF 很模糊。
    • 解法 :设置 scale: 2 ,提高 Canvas 的像素密度。
  3. PDF 分页问题 :长图直接放入 PDF 会被压缩变形。
    • 解法 :计算内容高度与 A4 纸高度的比例,通过循环 addPage() 实现自动分页切割。

三、 源码实现

新建文件 useExportPdf.ts,下载依赖 html2canvas 和 jspdf 然后引入:

import html2canvas from 'html2canvas'; import jsPDF from 'jspdf'; /** * 导出页面为 PDF * @param dom 需要导出的 DOM 元素 * @param fileName 导出的文件名(不含后缀) */ export const useExportPDF = async (dom: HTMLElement, fileName: string) => { const element = dom; if (!element) { console.error('导出失败,未找到导出元素'); return; } // 1. 解决滚动截断问题:获取元素实际高度 const originalHeight = element.scrollHeight; // 临时设置高度为 auto,确保能截取到所有内容 const originalStyleHeight = element.style.height; element.style.height = 'auto'; try { // 2. 将 DOM 转换为 Canvas const canvas = await html2canvas(element, { useCORS: true, // 允许跨域图片 scale: 2, // 2倍缩放,解决模糊问题 scrollY: -window.scrollY, // 修正滚动条偏移 scrollX: 0, windowHeight: originalHeight, // 告诉 html2canvas 完整高度 }); // 3. 初始化 PDF 实例 // p: 纵向, mm: 单位毫米, a4: 纸张格式 const pdf = new jsPDF('p', 'mm', 'a4'); // A4 纸内容宽度(留边距) const imgWidth = 190; // 根据宽度计算等比例的高度 const imgHeight = (canvas.height * imgWidth) / canvas.width; // 获取 PDF 页面可用高度 const pdfPageHeight = pdf.internal.pageSize.getHeight(); // 4. 处理分页逻辑 let position = 0; // 第一页 pdf.addImage(canvas, 'PNG', 10, 10 - position, imgWidth, imgHeight); position += pdfPageHeight; // 这里简化处理,按页面高度分页 // 如果内容高度超过一页,循环添加新页 while (position < imgHeight) { pdf.addPage(); // 移动图片位置,实现视觉上的“接续” // 注意:这里简单的 position += pageHeight 可能需要根据实际情况调整, // 比如减去一些边距来防止文字被切断,Demo 中使用了简化的逻辑。 pdf.addImage(canvas, 'PNG', 10, 10 - position, imgWidth, imgHeight); position += pdfPageHeight; } // 5. 保存文件 pdf.save(`${fileName}.pdf`); } catch (error) { console.error('导出 PDF 异常:', error); } finally { // 6. 恢复原始样式 element.style.height = originalStyleHeight; } }; 

四、 如何在组件中使用

在 Vue 组件中,我们只需要获取到 DOM 引用,然后调用这个 Hook 即可。

 <a-button type="primary" @click="exportPDF" v-if="disabled"> 导出PDF </a-button> 
import { useExportPDF } from '/@/hooks/exportpdf/useExportpdf'; // 导出的 DOM 元素 const pdfContainer = ref<HTMLDivElement>(null); // 导出 const exportPDF = async () => { loading.value = true; try { await useExportPDF(pdfContainer.value, 'xxxxpdf'); loading.value = false; } catch (e) { console.log(e); loading.value = false; } }; 

五、 值得注意的事项

  • html2canvas 将dom元素转成canvas图片的时候如果dom元素中有图片,需要解决跨域问题,这个一般来讲可以在服务端(图片源)设置 :
    图片的响应头(Response Header)必须包含 CORS 头,允许你的域名访问,或者Nginx配置下代理,或者前端解决的话就把图片转成Base64格式,但是如果图片比较多,就不建议前端解决了,第一个因为转图片格式图片一多就消耗更多时间,第二个是因为转成Base64格式的图片会增加文件大小。
  • 还有一个就是如果你想导致的内容之中,有些是不用导出,或者根据不同条件来区分是否导出可以使用 data-html2canvas-ignore这个属性,设置为true就不会导出这个元素。

最终实现效果
需要导出的页面

img


导出的pdf

img

六、 总结

通过这个封装,我们实现了一个轻量级且功能完备的 PDF 导出工具。它不仅解决了最让人头疼的 长页面截断 问题,还通过 scale 参数保证了导出的清晰度。

七、 小思考

为啥img标签就能通过图片url加载图片,但是把图片转成Canvas就会出现跨域问题?
简单来说就是 标签只是“展示”数据,而 转成Canvas 需要“读取”数据 。浏览器的安全策略(同源策略)就是“看一眼”和“拿走数据”的区别
 标签展示数据跟把图片转成转成Canvas浏览器都会请求图片,服务器返回图片数据。区别在于:
1. 标签加载

  • 请求头 :浏览器发起请求时, Origin 字段可能不被包含(或者是 null ),或者仅仅作为 Referer 发送。它通常被视为一个“简单请求”。
  • 响应头 :服务器返回图片数据。通常 不需要 包含 Access-Control-Allow-Origin 等 CORS 相关头信息。
  • 结果 :浏览器接收到数据,渲染引擎直接解码并在屏幕上绘制像素。JavaScript 无法接触到这些数据。
    2.开启 CORS 的情况( crossorigin="anonymous" 或 Canvas 请求)
    当你为了 Canvas 导出而给图片添加 crossorigin 属性,或者使用 JS fetch 请求图片时:
  • 请求头 :浏览器 强制添加 Origin: https://你的域名.com 字段,明确告诉服务器是谁在请求。
  • 响应头(关键差别) :
    • 如果服务器支持跨域 :必须返回 Access-Control-Allow-Origin: * 或 Access-Control-Allow-Origin: https://你的域名.com 。
    • 如果服务器不支持 :服务器可能正常返回了图片数据(状态码 200),但 缺少了 CORS 响应头 。
  • 结果 :
    • 如果 有 CORS 头:浏览器认为这份数据是“安全”的,允许 Canvas 读取和导出。
    • 如果 没有 CORS 头:虽然数据下载下来了,但浏览器(网络层或渲染层)会 拦截 这次加载,报错 CORS policy ,图片甚至可能直接裂开(加载失败),更别说画到 Canvas 上了。

Read more

用OpenClaw做飞书ai办公机器人(含本地ollama模型接入+自动安装skills+数据可视化)

用OpenClaw做飞书ai办公机器人(含本地ollama模型接入+自动安装skills+数据可视化)

执行git clone https://github.com/openclaw/openclaw克隆项目,执行cd openclaw进入项目 执行node --version看看node的版本是否大于等于22(没有node.js需自行安装),再执行npm install -g pnpm安装作为包管理器,并执行pnpm install安装依赖 首次执行pnpm ui:build构建 Web UI(会先安装 ui/ 目录的依赖) 执行pnpm build构建主程序 执行pnpm openclaw onboard --install-daemon运行配置向导(安装守护进程),完成初始化 按键盘右箭头选择Yes,同样Yes 任选一个模型提供商都行,没有对应的提供商的密钥可以跳过,如果是本地模型选vLLM(需用vLLM框架启动模型,有性能优势,但原生vLLM仅完全支持Linux的cuda)、Custom Provider(可以连接任何 OpenAI 或 Anthropic 兼容的端点,

By Ne0inhk
低成本开源!ESP32轮腿机器人实战

低成本开源!ESP32轮腿机器人实战

低成本开源!ESP32-S3轮腿机器人实战:自平衡+身高调节,语音控制在路上 作为机器人爱好者,你是否想亲手打造一款兼具灵活性与功能性的轮腿机器人,却担心成本过高、技术门槛难跨越?今天给大家分享一个超实用的开源项目——L在这里插入代码片eTian-robot2,一款基于ESP32-S3的低成本轮腿机器人,不仅实现了自平衡、身高调节、无线控制等核心功能,还开源了全部PCB、原理图和代码,新手也能跟着复刻! 一、项目初衷:从模仿到创新,解锁轮腿机器人的更多可能 这款机器人的灵感来源于大名鼎鼎的Ascento机器人,最初的设计目标是通过实践学习控制算法,最终实现酷炫的跳跃功能。虽然受限于理论知识储备,跳跃功能的建模仿真与实物落地预计要到明年6月才能完成,但目前已成功实现自平衡、身高调节、无线控制三大核心功能,后续还将迭代离线语音控制,性价比直接拉满! 更值得一提的是,项目完全开源,从PCB设计图、原理图、三维模型到BOM清单,所有资源都能免费获取,大大降低了制作门槛,让更多爱好者能参与到轮腿机器人的研发与优化中。 二、硬件篇:低成本选材,兼顾性能与性价比 1. 核心主控与P

By Ne0inhk
Flutter 三方库 whatsapp_bot_flutter 自动化社交矩阵鸿蒙多维协同适配指引:横向打通设备生态通信拦截管道、打造多模态实体机器人事件分发-适配鸿蒙 HarmonyOS ohos

Flutter 三方库 whatsapp_bot_flutter 自动化社交矩阵鸿蒙多维协同适配指引:横向打通设备生态通信拦截管道、打造多模态实体机器人事件分发-适配鸿蒙 HarmonyOS ohos

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 whatsapp_bot_flutter 自动化社交矩阵鸿蒙多维协同适配指引:横向打通设备生态通信拦截管道、打造多模态实体机器人事件分发极限制化与消息群发堡垒 前言 在 OpenHarmony 的企业级服务助理、自动化通知分发系统或者是个人智能机器人应用中,如何打通全球主流的即时通讯链路是开发者必须跨越的门槛。whatsapp_bot_flutter 库为 Flutter 开发者提供了一套基于协议或 Web 端桥接的自动化社交机器人方案。本文将带大家在鸿蒙端实战适配该库,探索社交自动化的无限可能。 一、原直线性 / 概念介绍 1.1 基础原理/概念介绍 whatsapp_bot_flutter 的核心逻辑是基于 基于流的会话状态机与加密协议握手 (Encryption Protocol Handshake)。它模拟官方客户端的连接逻辑,通过与指定网关建立受保护的 WebSocket 链路,并实时监听业务事件流(消息、

By Ne0inhk

【机器人零件】行星减速器

行星减速器 行星减速器作为精密传动系统的核心部件,在现代工业中扮演着至关重要的角色。本文将全面介绍行星减速器的减速比计算公式、提供C++代码实现实例,并详细分析其应用场景和使用条件。通过深入理解这些内容,工程师和技术人员能够更准确地选择、设计和应用行星减速器,满足各种机械传动需求。 行星减速器基本原理与结构组成 行星减速器,又称行星齿轮减速器,是一种采用行星轮系传动原理的精密减速装置。其基本结构由四个主要部件构成:位于中心的太阳轮(Sun Gear)、围绕太阳轮旋转的行星轮(Planetary Gear)、固定不动的内齿圈(Ring Gear)以及连接行星轮的行星架(Planetary Carrier)。这种独特的结构使得行星减速器能够在紧凑的空间内实现高减速比和大扭矩输出。 行星减速器的工作原理基于齿轮啮合理论,通过太阳轮、行星轮和内齿圈之间的相互作用实现动力传递和转速降低。当电机或其他动力源驱动太阳轮旋转时,行星轮不仅会绕自身轴线自转,还会在行星架的带动下绕太阳轮公转。这种复合运动通过行星架输出,实现减速和增扭的效果。由于多个行星轮同时参与啮合,载荷被均匀分散,这使得行星

By Ne0inhk