前端自定义导出PPT

1、背景

        前端导出PPT,刚接触这个需求,还是比较懵逼,然后就在网上查找资料,最终确认是可行的;这个需求也是合理的,我们做了一个可视化数据报表,报表导出成PPT,将在线报表转成文档类型留存;

2、技术方案

        实现这种复杂的功能,都得依赖前辈匠人,还好有一个比较完善好用的库:pptxgenjs

只有英文文档:Quick Start Guide | PptxGenJS,还可以搭配大家比较熟悉的库:html2canvas更好的实现完善的PPT

3、PptxGenJS运用

引入,生成一个简单的PPT文档

import pptxgen from "pptxgenjs";let pptx = new pptxgen();let slide = pptx.addSlide();slide.addText("React Demo!", { x: 1, y: 1, w: 10, fontSize: 36, fill: { color: "F1F1F1" }, align: "center" });pptx.writeFile({ fileName: "react-demo.pptx" }

pptx全局属性:

pptx.author = 'Brent Ely';
pptx.company = 'S.T.A.R. Laboratories';
pptx.revision = '15';
pptx.subject = 'Annual Report';
pptx.title = 'PptxGenJS Sample Presentation';
pptx.layout = 'LAYOUT_WIDE'; //13.5 x 7.5

其中最重要的属性,layout,顾名思义,就是设置PPT的slide的大小,默认就下面几种:

还可以自定义。这个x*y,是后面PPT页计算布局必不可少的;

slide master,自定义PPT页模板:

    ppt.defineSlideMaster({title: 'DEFAULT_SLIDE',objects: [{ image: { x: 0.25, y: 0.3, w: 0.6, h: 0.6, path: path} },{ image: { x: 10.8, y: 0.61, w: 1.485, h: 0.166, path: path} },{ image: { x: 12.3, y: 0.52, w: 0.72, h: 0.36, path: path} },{ line: { x: 0.25, y: 1, w: 12.8, h: 0, line: { color: '3874c5', width: 2 } } },{ image: { path: path, x: 0, y: 7.2, w: '100%', h: 0.3, size: { type: 'cover' } } },],});

我们自己创建PPT时,也会引用模板,这个就是自定义模板,就避免每页都设置;

slide,PPT页属性对象:

创建一页PPT,addSilde({masterName});masterName就是上面自定义的模板,就是上面的DEFAULT_SLIDE

4、添加表格

表格是一个常用功能,PPT的表格也比较完善,addTable();

表头和合计,这个pptx没有特殊的处理,只能作为正常row处理;

行高度问题row Height,表格设置高度h,如果不设置行高,rowH,这样表格会填充h,设置了rowH,最小高度会按rowH设置值显示;

表格一般有比较多的数据,PPT页就那么高,肯定会有超出PPT页的情况,pptx支持自定义分页,autoPage:true,然后结合autoPageCharWeight,autoPageLineWeight调试分页,在实际使用过程发现自动分页也是根据你设置的rowH和H来计算的,对表格单元格多行,还是会超出,然后导出的ppt文档,会报错,要修复啥的,所以我选择手动给它分页;

列宽问题,默认是等分的,colW,实际开发是最好根据列的宽度,然后计算colW的,记住设置了colW了,表格会严格按照设置的值展示,不会自适应,所以还要程序根据w在计算;

  setTable(data, option = {}) {let row = [];let options = { fontFace: 'Microsoft YaHei', fontSize: 12, margin: 0.05, valign: 'middle', align: 'left' };options['border'] = [{ pt: 1, color: 'ffffff', type: 'dash' }, { type: 'none' }, { pt: 1, color: 'ffffff', type: 'dash' }, { type: 'none' }];let head = [];let colW = [];data.head.forEach(item => {head.push({ text: item.label, options: { ...options, fill: '1E4265', color: 'ffffff' } });if (item.width < 85) {colW.push(0.1);} else if (item.width < 101) {colW.push(0.2);} else if (item.width < 121) {colW.push(0.3);} else {colW.push(0.4);}});// autoPage: true, newSlideStartY: 1.1, autoPageRepeatHeader: true,暂时自动分页不太行let page = { ...this.page, rowH: 0.5, valign: 'middle' };// 表格超出,ppt分页展示,最多展示三页数据let tableData = data.table || [];tableData = tableData.slice(0, 30);tableData.forEach((item, index) => {let temp = [];let fill = index % 2 == 1 ? 'ffffff' : 'f2f2f2';data.head.forEach(h => {temp.push({ text: item[h.prop] === null ? '' : item[h.prop], options: { ...options, fill } });});row.push(temp);});let sumW = colW.reduce((per, cur) => per + cur, 0);let fNum = option.h < 6 ? 5 : 10;let fRow = row.slice(0, fNum);let fOption = { ...page, ...option };fOption['colW'] = colW.map(item => Number((fOption.w / sumW) * item).toFixed(1));this.slide.addTable([head, ...fRow], fOption);let eNum = parseInt(fNum + 10);let tRow = row.slice(fNum, eNum);if (tRow.length) {// 第二页let slide = this.ppt.addSlide({ masterName: 'DEFAULT_SLIDE' });page['colW'] = colW.map(item => Number((page.w / sumW) * item).toFixed(1));slide.addTable([head, ...tRow], { ...page });tRow = row.slice(eNum, parseInt(eNum + 10));if (tRow.length) {// 第三页let slide = this.ppt.addSlide({ masterName: 'DEFAULT_SLIDE' });slide.addTable([head, ...tRow], { ...page });}}}
5、添加Image

addImage(),支持两种格式:data,base64数据;path,图片地址;注意,根据img的大小,然后换算实际的w和h;

  setChart(data, option = {}) {const { chartW, chartH, chartData } = data;let options = { ...this.page, ...option, sizing: { type: 'contain' } };let temp = (options.w / chartW) * chartH;if (temp > options.h) {temp = (options.h / chartH) * chartW;options.x = options.x + (options.w - temp) / 2;options.w = temp;} else {options.y = options.y + (options.h - temp) / 2;options.h = temp;}this.slide.addImage({ data: chartData, ...options });}

 复杂的html,可以通过html2canvas,把dom转成图片添加PPT

  async setDom2Image(dom, option = {}) {let result = {};const res = await html2Canvas(dom, { scale: 2 });result['chartData'] = res.toDataURL('image/jpeg', 1);result['chartH'] = res.height;result['chartW'] = res.width;this.setChart(result, option);}
6、添加文本

添加文本,这个介绍把一段html,添加成PPT文本

  setContent(content, option = {}) {let obj = [];content &&content.forEach(item => {let len = item.children.length;--len;item.children.forEach((cItem, index) => {obj.push({ text: cItem.text, options: { bold: cItem.bold ? true : false, color: cItem.color ? this.RGBToHex(cItem.color) : '333333', breakLine: index === len } });});});let options = { ...this.page, ...option, align: 'left' };if (options.h == 6) {options['fontSize'] = options.h == 6 ? 16 : 12;}this.setTitle(obj, { ...options });}setTitle(text, option = {}) {const options = {fontSize: 12, //字号fontFace: 'Microsoft YaHei',bold: false,color: '333333', //颜色 与背景颜色一样,一样不要 #,填满6位valign: 'middle', // 垂直居中 top middle bottom};this.slide.addText(text, { ...options, ...option });}

 由于PPT只支持16进制的颜色值,所以需要把rgb转成6位颜色值

  RGBToHex(rgb) {let regexp = /\d+/g;let res = rgb.match(regexp);return ((res[0] << 16) | (res[1] << 8) | res[2]).toString(16);}

把html转成slate的JSON

let document = new DOMParser().parseFromString(this.content, 'text/html');result['json'] = this.deserialize(document.body);deserialize(el, markAttributes = {}) {if (el.nodeType === Node.TEXT_NODE) {return jsx('text', markAttributes, el.textContent);} else if (el.nodeType !== Node.ELEMENT_NODE) {return null;}const nodeAttributes = { ...markAttributes };// define attributes for text nodesswitch (el.nodeName) {case 'STRONG':nodeAttributes.bold = true;}// font colorif (el.style.color) {nodeAttributes.color = el.style.color;}const children = Array.from(el.childNodes).map(node => this.deserialize(node, nodeAttributes)).flat();if (children.length === 0) {children.push(jsx('text', nodeAttributes, ''));}switch (el.nodeName) {case 'BR':return '\n';case 'P':return jsx('element', { type: 'paragraph' }, children);default:return children;}},

7、绘制图形Shapes

shapes,绘制图形,文档有详细介绍,这里我就不累述

 8、总结

整个功能实现下来还是比较耗时,主要文档都是英文,有些字段描述也不是很清晰,有的需要慢慢调试,上面一些介绍的功能,都是实际开发在使用的;总体来说,还是比较完美实现自定义导出PPT。欢迎大家一起沟通交流!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/136823.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

02、Servlet核心技术(下)

目录 1 ServletJDBC应用&#xff08;重点&#xff09; 2 重定向和转发&#xff08;重点&#xff09; 2.1 重定向的概述 2.2 转发的概述 3 Servlet线程安全&#xff08;重点&#xff09; 4 状态管理&#xff08;重点 &#xff09; 5 Cookie技术&#xff08;重点&#xf…

操作系统(5-7分)

内容概述 进程管理 进程的状态 前驱图 同步和互斥 PV操作&#xff08;难点&#xff09; PV操作由P操作原语和V操作原语组成&#xff08;原语是不可中断的过程&#xff09;&#xff0c;对信号量进行操作&#xff0c;具体定义如下&#xff1a; P&#xff08;S&#xff09;&#…

Selenium自动化测试 —— 通过cookie绕过验证码的操作

验证码的处理 对于web应用&#xff0c;很多地方比如登录、发帖都需要输入验证码&#xff0c;类型也多种多样&#xff1b;登录/核心操作过程中&#xff0c;系统会产生随机的验证码图片&#xff0c;进行验证才能进行后续操作 解决验证码的方法如下&#xff1a; 1、开发做个万能…

六、展示信息添加 animation 动态效果

简介 给每个信息组件内容添加动画效果,通过 animation 来怎么增强用户浏览时的交互体验。欢迎访问个人的简历网站预览效果 本章涉及修改与新增的文件:App.vue、main.ts、first.vue 、second.vue、third.vue 、fourth.vue 、fifth.vue 一、安装 animae 插件 先安装 animate…

Springboot整合规则引擎

Springboot整合Drools 规则引擎 1.添加maven 依赖坐标&#xff0c;并创建springboot项目 <!-- drools规则引擎 --> <dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>7.6.0.Final<…

JavaScript系列从入门到精通系列第一篇:JavaScript语言简介和它代码初体验

一&#xff1a;简介 1&#xff1a;起源 JavaScript诞生于1995年&#xff0c;它的出现主要是用于处理网页中的前端验证&#xff0c; 所谓的前端验证&#xff0c;就是指检查用户输入的内容是否符合一定的规则。 2&#xff1a;简史 JavaScript是由网景公司发明&#xff0c;起初命…

【SpringMVC】拦截器JSR303的使用

【SpringMVC】拦截器&JSR303的使用 1.1 什么是JSR3031.2 为什么使用JSR3031.3 常用注解1.4 Validated与Valid区别1.5 JSR快速入门1.5.2 配置校验规则# 1.5.3 入门案例二、拦截器2.1 什么是拦截器2.2 拦截器与过滤器2.3 应用场景2.4 拦截器快速入门2.5.拦截器链2.6登录案列权…

ZTMap是如何在相关政策引导下让建筑更加智慧化的?

近几年随着智慧楼宇概念的深入&#xff0c;尤其是在“十四五规划”“新基建”“数字经济”等相关战略和政策的引导下&#xff0c;智慧楼宇也迎来了快速发展期&#xff0c;对推动智慧城市系统的建设越来越重要。那么究竟什么是智慧楼宇呢&#xff1f;智慧楼宇其实就是整合楼宇内…

神经元量子点处理器赋能,三星QN85Z电视带来高品质视听盛宴

我们注意到近期三星发布新款Neo QLED电视QN85Z&#xff0c;有感于三星在芯片、面板技术等方面的深厚积累&#xff0c;我们对三星新发布的这款电视相当感兴趣。QN85Z新品搭载了三星“集成之芯”——神经元量子点处理器&#xff0c;搭配高端量子点Mini LED带来更为优化的画质和音…

IPV4和IPV6,公网IP和私有IP有什么区别?

文章目录 1、什么是IP地址&#xff1f;1.1、背景1.2、交换机1.3、局域网1.4、广域网1.5、ISP 互联网服务提供商 2、IPV42.1、什么是IPV4&#xff1f;2.2、IPV4的组成2.3、NAT 网络地址转换2.4、端口映射 3、公网IP和私有IP4、IPV6 1、什么是IP地址&#xff1f; 1.1、背景 一台…

机器人如何有效采摘苹果?

摘要&#xff1a;本文利用动捕数据构建拟人运动模型&#xff0c;对比观察两种苹果采摘模式&#xff0c;并对系统性能进行全面评估&#xff0c;为提高机器人采摘效率提供创新方法。 近期&#xff0c;一项关于苹果采摘机器人的有趣研究—— "Design and evaluation of a rob…

Git学习笔记8

Gitlab&#xff1a; Gitlab是利用Ruby on Rails 一个开源的版本管理系统&#xff0c;实现一个自托管的git项目仓库&#xff0c;可通过web界面进行访问公开或私有的项目。 Gitlab安装&#xff1a; 安装之前&#xff0c;将虚拟机的内存改成了4个G。内存如果太小&#xff0c;会有…

巧用“加减乘除”,停车场“碳”出节能发展新路

“绿色化、低碳化是高质量发展的关键”。中国电信以先驱姿态&#xff0c;积极贯彻“碳达峰碳中和”国家战略&#xff0c;以科技为画笔&#xff0c;绿色生态为底色&#xff0c;绘就节能低碳发展新蓝图。 在湖北省某办公楼宇&#xff0c;物业管理员因停车场照明现状发了愁。在过去…

Unity 开发人员转CGE(castle Game engine)城堡游戏引擎指导手册

Unity 开发人员的城堡游戏引擎概述 一、简介2. Unity相当于什么GameObject&#xff1f;3. 如何设计一个由多种资产、生物等组成的关卡&#xff1f;4. 在哪里放置特定角色的代码&#xff08;例如生物、物品&#xff09;&#xff1f;Unity 中“向 GameObject 添加 MonoBehaviour”…

1_图神经网络GNN基础知识学习

文章目录 安装PyTorch Geometric安装工具包 在KarateClub数据集上使用图卷积网络 (GCN) 进行节点分类两个画图函数Graph Neural Networks数据集&#xff1a;Zacharys karate club network.PyTorch Geometric数据集介绍 edge_index使用networkx可视化展示 Graph Neural Networks…

element-table出现错位解决方法

先看示例图&#xff0c;这个在开发中还是很常遇到的&#xff0c;在table切换不同数据时或者切换页面时&#xff0c;容易出现&#xff1a; 解决方法很简单&#xff0c;官方有提供方法&#xff1a; 我们可以在重新渲染数据后&#xff1a; this.$nextTick(() > {this.$refs.…

嵌入式笔试面试刷题(day15)

文章目录 前言一、Linux中的主设备号和次设备号1.查看方法2.主设备号和次设备号的作用 二、软件IIC和硬件IIC的区别三、变量的声明和定义区别四、static在C和C中的区别五、串口总线空闲时候的电平状态总结 前言 本篇文章继续讲解嵌入式笔试面试刷题&#xff0c;希望大家坚持跟…

【超实用】2023年,学生上班族如何简单快速,低成本的搭建一个博客网站

文章目录 前言实操环节香港虚拟机购买博客搭建ssl证书配置备份设置 总结 前言 因为工作和生活的需要&#xff0c;我一直有博客的搭建需求。我将总结下来&#xff0c;为读者提供参考。  起初&#xff0c;我采用的是香港云虚拟主机&#xff0c;这种虚拟机极其便宜&#xff08;一…

什么样的应用程序适合使用Flutter开发桌面?

桌面应用开发的现状 在过去&#xff0c;桌面应用程序的开发通常需要使用特定于操作系统的工具和语言&#xff0c;如C、C#、Java等。这导致了高昂的开发成本和维护困难。尽管有一些跨平台桌面开发工具&#xff0c;如Electron和Qt&#xff0c;但它们在性能、用户体验和开发效率方…

Linux Static Key原理与应用

文章目录 背景1. static-key的使用方法1.1. static-key定义1.2 初始化1.3 条件判断1.4 修改判断条件 2、示例代码参考链接 背景 内核中有很多判断条件在正常情况下的结果都是固定的&#xff0c;除非极其罕见的场景才会改变&#xff0c;通常单个的这种判断的代价很低可以忽略&a…