html2canvas+jsPDF导出超长网页的PDF
项目需求:有一个网页大概60000px的高度,现在需要导出为PDF
index.vue
<template><div class="ctn"><div class="pdf-ctn"><div class="pdf-panel" ><div class="pdf-inside-panel" id="myList"><div v-for="(item, index) in 3000" :key="index" style="height: 20px">{{index}}---我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我的高度{{(index+1)*20}}</div></div></div><divclass="pdf-header"style="font-weight: bold;padding: 15px 8px;width: 100%;border-bottom: 1px solid rgba(0, 0, 0, 0.85);color: rgba(0, 0, 0, 0.85);position: fixed;top: -100vh;">页头</div><divclass="pdf-footer"style="font-weight: bold;padding: 15px 8px;width: 100%;border-top: 1px solid rgba(0, 0, 0, 0.85);position: fixed;top: -100vh;"><divstyle="display: flex;justify-content: center;align-items: center;padding-top: 5px;">我是页尾</div><divstyle="display: flex;justify-content: center;align-items: center;margin-top: 20px;">第<div class="pdf-footer-page"></div>页 / 第<div class="pdf-footer-page-count"></div>页</div></div></div><div><a-buttonstyle="top: 50px; left: 1450px; position: fixed"@click="handleOutput">测试导出</a-button></div></div>
</template><script>
import { message } from "ant-design-vue";
import { outCanvas } from "../scroll";
export default {name: "HelloWorld",props: {msg: String,},methods: {async handleOutput() {const element = document.querySelector("#myList");const header = document.querySelector(".pdf-header");const footer = document.querySelector(".pdf-footer");await outCanvas(element);let endTime = new Date().getTime();let timeElapsed = endTime - startTime; // 获取时间差(毫秒)console.log(`函数运行时间: ${timeElapsed} 毫秒`);} catch (error) {console.log(error)message.error(typeof error === "string" ? error : JSON.stringify(error));}},}
};
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
.ctn {.pdf-ctn {width: 1300px;.pdf-panel {position: relative;}}
}
</style>
JS
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
import { message } from 'ant-design-vue';// jsPDFs实例
let pdf = new jsPDF({unit: 'pt',format: 'a4',orientation: 'p',// format: [550, 550]
});// 对图片进行等比缩放
function resizeImage(imgWidth, imgHeight, maxWidth = 590) {// 计算当前图片的宽高比const ratio = imgWidth / imgHeight;// 如果最大宽度小于当前宽度,则按最大宽度进行缩放if (imgWidth > maxWidth) {return {newWidth: maxWidth,newHeight: maxWidth / ratio};} else { // 否则,图片本身就在允许的最大宽度内,不需要缩放return {newWidth: imgWidth,newHeight: imgHeight};}
}async function toCanvas(element,scrolledHeight=0,viewHeight=window.innerHeight) {// 放大倍率const scaleRatio = window.devicePixelRatio * 2const canvas = await html2canvas(element, {scale: scaleRatio,useCORS: true,width: document.querySelector("#myList").scrollWidth,height: Math.min(element.scrollHeight, viewHeight),windowWidth: document.querySelector("#myList").scrollWidth,windowHeight: document.querySelector("#myList").scrollHeight,x: 0,y: scrolledHeight,})let canvasImg = canvas.toDataURL("image/jpeg",1);return { width:canvas.width, height:canvas.height, data: canvasImg}
}// 循环生成PDF
let pdfImgTop = 0
let pageHeight = 0
async function loopGeneratePDF(targetElement, scrolledHeight = 0, viewHeight =window.innerHeight) {const A4_HEIGHT = 900if (scrolledHeight >= targetElement.scrollHeight) {message.success("生成PDF成功");return;}const { data: imgData, height, width } = await toCanvas(targetElement, scrolledHeight, viewHeight);// console.log("图片",imgData)const { newWidth, newHeight } = resizeImage(width, height);pdf.addImage(imgData, 'JPEG', 0, pdfImgTop, newWidth, newHeight);const pages = pdf.internal.getNumberOfPages()message.success(`生成第${pages}页`)// 下一次需要截取的开始高度scrolledHeight += Math.floor(height / 2);pdfImgTop += newHeight;// 如果当前页内容不足一页A4纸的高度,则递归调用并调整视图高度if (A4_HEIGHT>scrolledHeight) {// 剩余页面的高度pageHeight = A4_HEIGHT - scrolledHeight;return loopGeneratePDF(targetElement, scrolledHeight, pageHeight);}else {if(targetElement.scrollHeight - scrolledHeight > A4_HEIGHT || pdfImgTop>A4_HEIGHT){pdf.addPage();pdfImgTop = 10;}return loopGeneratePDF(targetElement, scrolledHeight-20);}}export const outCanvas = async function (targetElement) {if (!(targetElement instanceof HTMLElement)) {return;}await loopGeneratePDF(targetElement,0,window.innerHeight)return pdf.save('test.pdf');
}相关文章:
html2canvas+jsPDF导出超长网页的PDF
项目需求:有一个网页大概60000px的高度,现在需要导出为PDF index.vue <template><div class"ctn"><div class"pdf-ctn"><div class"pdf-panel" ><div class"pdf-inside-panel" id"myList">&…...
云计算:OpenStack 分布式架构管理VXLAN网络(单控制节点与多计算节点)
目录 一、实验 1.环境 2.各节点新增网卡准备VXLAN网络 3.控制节点配置私有网络 4.计算节点1配置私有网络 5.计算节点2配置私有网络 6.重启服务 7.修改Dashboard 8.新建项目(租户)及用户 9.新建网络与子网 10.新建实例 11.新建路由 12.新增浮…...
MATLAB --- dlmread( )函数的用法
dlmread() 是 MATLAB 中用于读取以特定分隔符分隔的文本文件数据的函数 下面是 dlmread() 函数的用法: M dlmread(filename) M dlmread(filename, delimiter) M dlmread(filename, delimiter, R, C) M dlmread(filename, delimiter, range)参数说明࿱…...
STM32CubeMX RS485接口使用
一、基本知识 TTL(Transistor-Transistor Logic): 电平范围: 逻辑1对应于2.4V–5V,逻辑0对应于0V–0.5V。通信特点: 全双工。特点: 常见于单片机和微控制器的IO电平,USB转TTL模块通常…...
ClickHouse(20)ClickHouse集成PostgreSQL表引擎详细解析
文章目录 PostgreSQL创建一张表实施细节用法示例 资料分享参考文章 PostgreSQL PostgreSQL 引擎允许 ClickHouse 对存储在远程 PostgreSQL 服务器上的数据执行 SELECT 和 INSERT 查询. 创建一张表 CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] (name…...
R304S 指纹识别模块功能实现示例
1 基本通信流程 1.1 UART 命令包的处理过程 1.2 UART 数据包的发送过程 UART 传输数据包前,首先要接收到传输数据包的指令包,做好传输准备后发送成功应答包,最后才开始传输数据包。数据包主要包括:包头、设备地址、包标识、包长…...
2、Excel:基础概念、表格结构与常见函数
数据来源:八月成交数据 数据初探 业务背景 数据来源行业:金融行业(根据应收利息和逾期金额字段来判断) 可以猜测: 业务主体:某互联网金融公司(类似支付宝)也业务模式:给…...
鱼类识别Python+深度学习人工智能+TensorFlow+卷积神经网络算法
一、介绍 鱼类识别系统。使用Python作为主要编程语言开发,通过收集常见的30种鱼类(‘墨鱼’, ‘多宝鱼’, ‘带鱼’, ‘石斑鱼’, ‘秋刀鱼’, ‘章鱼’, ‘红鱼’, ‘罗非鱼’, ‘胖头鱼’, ‘草鱼’, ‘银鱼’, ‘青鱼’, ‘马头鱼’, ‘鱿鱼’, ‘鲇…...
ThreadLocal线程重用导致用户信息错乱的 Bug
在生产上遇到一个诡异的问题,有时获取到的用户信息是别人的。查看代码后,我发现他使用了 ThreadLocal 来缓存获取到的用户信息。 我们知道,ThreadLocal 适用于变量在线程间隔离,而在方法或类间共享的场景。如果用户信息的获取比较…...
洛谷——P1143 进制转换
文章目录 一、题目进制转换题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 二、题解基本思路: 一、题目 进制转换 题目描述 请你编一程序实现两种不同进制之间的数据转换。 输入格式 共三行,第一行是一个正整数,表示需要转换的…...
linux stop_machine 停机机制应用及一次触发 soft lockup 分析
文章目录 stop_mchine 引起的 soft lockup触发 soft lockup 原因分析(一):触发 soft lockup 原因分析(二)触发 soft lockup 原因分析(三) stop_mchine 引起的 soft lockup 某次在服务器上某节点…...
ARM 链接器优化功能介绍
消除公共部分组 链接器可以检测节组的多个副本,并丢弃其他副本。 Arm Compiler for Embedded 生成用于链接的完整对象。因此: 如果 C 和 C 源代码中存在内联函数,则每个对象都包含该对象所需的内联函数的外联副本。如果在 C 源代码中使用…...
动手学深度学习之卷积神经网络之池化层
池化层 卷积层对位置太敏感了,可能一点点变化就会导致输出的变化,这时候就需要池化层了,池化层的主要作用就是缓解卷积层对位置的敏感性 二维最大池化 这里有一个窗口,来滑动,每次我们将窗口中最大的值给拿出来 还是上…...
HackTheBox - Medium - Linux - Ambassador
Ambassador Ambassador 是一台中等难度的 Linux 机器,用于解决硬编码的明文凭据留在旧版本代码中的问题。首先,“Grafana”CVE (“CVE-2021-43798”) 用于读取目标上的任意文件。在研究了服务的常见配置方式后,将在其…...
嵌入式——循环队列
循环队列 (Circular Queue) 是一种数据结构(或称环形队列、圆形队列)。它类似于普通队列,但是在循环队列中,当队列尾部到达数组的末尾时,它会从数组的开头重新开始。这种数据结构通常用于需要固定大小的队列,例如计算机内存中的缓冲区。循环队列可以通过数组或链表实现,…...
2024.1.7-实战-docker方式给自己网站部署prometheus监控ecs资源使用情况-2024.1.7(测试成功)
实战-docker方式给自己网站部署prometheus监控ecs资源使用情况-2024.1.7(测试成功) 目录 最终效果 原文链接 https://onedayxyy.cn/docs/prometheus-grafana-ecs 参考模板 https://i4t.com/ https://grafana.frps.cn 🔰 额,注意哦: 他这个是通过frp来…...
20240107 SQL基础50题打卡
20240107 SQL基础50题打卡 1978. 上级经理已离职的公司员工 表: Employees ----------------------- | Column Name | Type | ----------------------- | employee_id | int | | name | varchar | | manager_id | int | | salary | int | -…...
阿里云公网带宽出网和入网是什么?上行和下行是什么?
什么是阿里云服务器ECS的入网带宽和出网带宽?以云服务器为中心,流入云服务器占用的带宽是入网带宽,流量从云服务器流出的带宽是出网带宽。阿里云服务器网aliyunfuwuqi.com分享入网带宽和出网带宽说明表: 带宽类别说明入网带宽&am…...
eureka工作原理是什么
EUREKA 是一个基于 RESTful 风格的服务发现系统,它主要用于帮助实现在微服务架构中的服务自动发现与注册。其工作原理主要包括以下几个步骤: 注册中心:EUREKA 中有一个集中的注册中心,所有的服务都将在此注册和发现。注册中心可以…...
Vue中的事件委托(事件代理)使用方法介绍
事件委托(事件代理) 将原本需要绑定在子元素上的事件监听器委托在父元素上,让父元素充当事件监听的职务。 事件委托是一种利用事件冒泡的特性,在父节点上响应事件,而不是在子节点上响应事件的技术。它能够改善性能&a…...
Obsidian个性化主页:如何用3款模板解决知识管理效率难题?
Obsidian个性化主页:如何用3款模板解决知识管理效率难题? 【免费下载链接】obsidian-homepage Obsidian homepage - Minimal and aesthetic template (with my unique features) 项目地址: https://gitcode.com/gh_mirrors/obs/obsidian-homepage …...
在高校科研项目中采用 Taotoken 实现多模型对比实验的便捷方案
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 在高校科研项目中采用 Taotoken 实现多模型对比实验的便捷方案 高校科研团队在进行大模型相关的对比实验时,常常面临一…...
深度拆解:ReID 跨镜跟踪短板,对比镜像视界无感定位优势前言:跨镜追踪的本质不是 “认长相”,而是 “追空间”跨镜连续追踪,是数字孪生、视频孪生、全域安防与实景管控的核心底座能力。
深度拆解:ReID 跨镜跟踪短板,对比镜像视界无感定位优势前言:跨镜追踪的本质不是 “认长相”,而是 “追空间”跨镜连续追踪,是数字孪生、视频孪生、全域安防与实景管控的核心底座能力。行业长期依赖 ReID(行…...
【ElevenLabs尼泊尔文语音实战指南】:20年AI语音工程师亲授7大避坑要点与本地化部署全流程
更多请点击: https://intelliparadigm.com 第一章:ElevenLabs尼泊尔文语音技术概览与核心价值 ElevenLabs 自 2023 年起逐步扩展其多语言语音合成能力,尼泊尔文(Nepali, ISO 639-1: ne)作为首批支持的南亚语系之一&am…...
QtUnblockNeteaseMusic终极指南:高效解锁网易云音乐地区限制
QtUnblockNeteaseMusic终极指南:高效解锁网易云音乐地区限制 【免费下载链接】QtUnblockNeteaseMusic A desktop client for UnblockNeteaseMusic, made with Qt. 项目地址: https://gitcode.com/gh_mirrors/qt/QtUnblockNeteaseMusic QtUnblockNeteaseMusic…...
别再死磕官方文档了!R语言circlize包画圈图,这份新手避坑笔记帮你省下三天时间
R语言circlize包实战指南:从挫败感到高效绘图的进阶之路 第一次打开circlize包的官方文档时,那种扑面而来的复杂参数和抽象概念让人望而生畏。作为生物信息学分析中常用的环形可视化工具,circlize包在基因组数据展示、多维度数据关联分析等领…...
Claude与Codex双引擎协作:AI代码生成的新范式与实践
1. 项目概述:当Claude遇上Codex,双引擎驱动的代码生成新范式最近在GitHub上看到一个挺有意思的项目,叫claude-codex-duo。光看名字,你大概就能猜到它的核心玩法——把Anthropic的Claude和OpenAI的Codex这两个顶级的AI模型给“撮合…...
从一次安全扫描报告说起:聊聊SSH Banner泄露那些事儿,以及比修改Banner更重要的安全习惯
从SSH版本泄露看现代安全防御:工程师的深度实践指南 那天下午,我正在整理新部署的云服务器集群的安全扫描报告,一个看似"古老"的漏洞引起了我的注意——CVE-1999-0634,SSH版本信息可被获取。这个诞生于上世纪的安全问题…...
从功能测试到测试开发,薪资翻倍的秘密都在这里
当“点点点”撞上职业天花板 如果你是一名功能测试工程师,下面的场景你一定不陌生:每天对着需求文档编写用例,在测试环境里重复着相似的操作路径,偶尔发现一个边界值缺陷便觉得一天没有白费。然而,当你在招聘网站上搜…...
VMware ESXi版本回退全攻略:从适用条件、DCUI操作到6.x升7.0的‘后悔药’失效分析
VMware ESXi版本回退深度解析:从技术原理到实战避坑指南 在虚拟化运维领域,版本升级往往伴随着不可预知的风险。当新版本出现兼容性问题或性能异常时,版本回退能力就成为系统管理员手中的"后悔药"。然而,不同于普通软件…...
