前端自定义导出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.xdnf.cn/news/141490.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录笔记--动态规划篇

1--动态规划理论基础 动态规划经典问题&#xff1a;① 背包问题&#xff1b;② 打家劫舍&#xff1b;③ 股票问题&#xff1b; ④ 子序列问题&#xff1b; 动态规划五部曲&#xff1a; ① 确定 dp 数组及其下标的含义&#xff1b; ② 确定递推公式&#xff1b; ③ 确定 dp 数组…

第二章 使用matplotlib绘制简单图表

第二章 使用matplotlib绘制简单图表 1.绘制折线图 1.1.使用plot()绘制折线图 ​ 使用pyplot的plot()函数可以快速绘制折线图。plot()函数的语法格式如下&#xff1a; plot&#xff08;x, y, fmt, scalexTrue, scaleyTrue, dataNone, labelNone, *args, **kwargs&#xff09…

工业互联网:数字化革命的引擎

工业互联网&#xff0c;作为数字化革命的引擎&#xff0c;正以前所未有的速度和力度改变着我们的世界。这一概念不再局限于企业内部的信息技术应用&#xff0c;而是将互联网、大数据、人工智能等技术深度融入到制造业、能源、交通、农业等各个领域&#xff0c;实现了设备、系统…

【深度学习】ONNX模型多线程快速部署【基础】

【深度学习】ONNX模型CPU多线程快速部署【基础】 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【深度学习】ONNX模型CPU多线程快速部署【基础】前言搭建打包环境python多线程并发简单教程基本教程ONNX模型多线程并发 打包成可执行文件总结 前…

rar格式转换zip格式,如何做?

平时大家压缩文件时对压缩包格式可能没有什么要求&#xff0c;但是&#xff0c;可能因为工作需要&#xff0c;我们要将压缩包格式进行转换&#xff0c;那么我们如何将rar格式转换为其他格式呢&#xff1f;方法如下&#xff1a; 工具&#xff1a;WinRAR 打开WinRAR&#xff0c…

友思特案例|友思特 Ensenso 3D相机:汽车工业自动化的革命性力量

01 内容摘要 在竞争激烈的汽车行业&#xff0c;自动化生产至关重要。友思特 Ensenso 3D相机为汽车制造商提供了可靠的工具和技术支持&#xff0c;助力多个关键环节。它在汽车座位泡棉切割中提高精确度&#xff0c;降低浪费&#xff0c;提高生产效率&#xff1b;在汽车压铸零部…

ad18学习笔记十二:如何把同属性的元器件全部高亮?

1、先选择需要修改的器件的其中一个。 2、右键find similar objects&#xff0c;然后在弹出的对话框中&#xff0c;将要修改的属性后的any改为same 3、像这样勾选的话&#xff0c;能把同属性的元器件选中&#xff0c;其他器件颜色不变 注意了&#xff0c;如果这个时候&#xff…

浅谈xss

XSS 简介 XSS,全称Cross Site Scripting,即跨站脚本攻击,是最普遍的Web应用安全漏洞。这类漏洞能够使得攻击者嵌入恶意脚本代码到正常用户会访问到的页面中,当正常用户访问该页面时,则可导致嵌入的恶意脚本代码的执行,从而达到恶意攻击用户的目的。需要强调的是,XSS不仅…

Win10专业版系统一键重装怎么操作?

Win10专业版系统一键重装怎么操作&#xff1f;与传统的系统重装相比&#xff0c;一键重装不仅省去了繁琐的安装步骤&#xff0c;这一简单操作使得系统维护和恢复变得更加便捷&#xff0c;让用户不再为系统问题而烦恼。下面小编给大家详细介绍关于一键重装Win10专业版系统的操作…

Tune-A-Video论文阅读

论文链接&#xff1a;Tune-A-Video: One-Shot Tuning of Image Diffusion Models for Text-to-Video Generation 文章目录 摘要引言相关工作文生图扩散模型文本到视频生成模型文本驱动的视频编辑从单个视频生成 方法前提DDPMsLDMs 网络膨胀微调和推理模型微调基于DDIM inversio…

ElasticSearch从入门到精通(二)

ElasticSearch 高级操作 bulk批量操作 批量操作-脚本 #批量操作 #1.删除5号 #新增8号 #更新2号 name为2号 POST _bulk {"delete":{"_index":"person1","_id":"5"}} {"create":{"_index":"person…

Android 10.0 系统开启和关闭黑白模式主题功能实现

1. 概述 在10.0的rom系统开发定制化中,在系统SystemUI的下拉状态栏中,产品开发功能需求要求添加黑白模式功能开关的功能,就是打开黑白模式,系统颜色就会变成黑白颜色, 关闭黑白模式开关系统就会变成彩色模式,所以就需要了解下系统是怎么设置黑白模式和彩色模式的,然后添…

docker下redis备份文件dump.rdb获取

1.查看镜像 docker ps -a 2.进入redis客户端 docker exec -it redis redis-cli 3.保存备份文件 save 4.查看文件存放位置 CONFIG GET dir 5.将docker中文件拷出 docker cp id或name:容器中文件的路径 目标目录地址

研究铜互连的规模能扩大到什么程度

随着领先的芯片制造商继续将finFET以及很快的纳米片晶体管缩小到越来越小的间距&#xff0c;使用铜及其衬垫和阻挡金属&#xff0c;较小的金属线将变得难以维持。接下来会发生什么以及何时发生&#xff0c;仍有待确定。 自从IBM在20世纪90年代向业界引入采用双镶嵌工艺的铜互连…

一文教你彻底理解什么是协程!

作为程序员&#xff0c;想必你多多少少听过协程这个词&#xff0c;这项技术近年来越来越多的出现在程序员的视野当中&#xff0c;尤其高性能高并发领域。当你的同学、同事提到协程时如果你的大脑一片空白&#xff0c;对其毫无概念。。。 那么这篇文章正是为你量身打造的。话不…

flink集群与资源@k8s源码分析-回顾

本章是分析系列最后一章,作为回顾,以运行架构图串联起所有分析场景 1 启动集群,部署集群(提交k8s),新建作业管理器组件 2 构建和启动flink master组件 3 提交作业,N/A

性能测试分析调优必备的java虚拟机知识

Java虚拟机 Java虚拟机&#xff08;Java Virtual Machine&#xff0c;简称JVM&#xff09;是一种用于执行Java字节码的虚拟计算机。它是Java平台的关键组成部分&#xff0c;负责将Java源代码编译为可在不同计算机体系结构上执行的字节码。 JVM起到了中间层的作用&#xff0c…

IP转地理位置:探讨技术与应用

IP地址是互联网上设备的唯一标识符&#xff0c;而将IP地址转换为地理位置信息是网络管理、安全监控和市场定位等领域中的一项重要任务。本文将深入探讨IP转地理位置的技术原理和各种应用场景。 IP地址与地理位置 IP地址&#xff08;Internet Protocol Address&#xff09;是一…

面试官:你了解axios的原理吗?有看过它的源码吗?

面试官&#xff1a;你了解axios的原理吗&#xff1f;有看过它的源码吗&#xff1f; 一、axios的基本使用 关于 axios 的基本使用&#xff0c;上篇文章已经有所涉及&#xff0c;这里再稍微回顾一下&#xff1a; 发送请求 import axios from axios;axios(config) // 直接传入…

一百八十六、大数据离线数仓完整流程——步骤五、在Hive的DWS层建动态分区表并动态加载数据

一、目的 经过6个月的奋斗&#xff0c;项目的离线数仓部分终于可以上线了&#xff0c;因此整理一下离线数仓的整个流程&#xff0c;既是大家提供一个案例经验&#xff0c;也是对自己近半年的工作进行一个总结。 二、数仓实施步骤 &#xff08;五&#xff09;步骤五、在Hive的…