基于 Konva 实现Web PPT 编辑器(三)

完善公式

        上一节我们简单讲述了公式的使用,并没有给出完整的样例,下面还是完善下相关步骤,我们是默认支持公式的编辑功能的哈,因此,我们只需要提供必要的符号即可:

        符号所表达的含义是 mathlive 的command命令符号:

        当我们点击符号的时候,执行 executeCommand('insert')即可:

const svgs = menuItem.querySelectorAll("svg");
svgs.forEach((svg) => {svg.addEventListener("click", () => {// 获取对应的 latex 命令const command = <string>svg.dataset?.command;latex.executeCommand("insert", command);latex.focus();});
});

         如果大家使用时,有些公式显示异常,应该是字体文件没有引入,需要将全部字体放到 public/fonts 下(字体文件在 node_modelus/mathlive下有的)

        导出的实现,就是基于html-to-image 库:

// 1. 将公式转为 HTML(一定先转HTML哈)
const html = convertLatexToMarkup(latex.value);// ... 还需要将生成的 html 插入页面,并做好隐藏// 调用 html-to-image 库
toBlob(html).then((blob) => {// ... 生成的文件转成 konva 图片即可
});

拖拽排序

        这个功能在很多网站都有,网上也有好多教程,大家搜一下就行了,给下大致实现思路:

  /** 拖拽开始 - 添加拖拽样式 */private imageBoxDragStart(e: DragEvent) {const thumbItem = <HTMLDivElement>e.target;nextTick(() => {thumbItem.classList.add("thumb-draging");});}
private imageBoxDragEnter(e: DragEvent) {// 父元素在 thumbBox 中,需要根据这个获取顺序,因为拖拽过程中  顺序是变得哦const childrenList = [...thumbBox.children];const dragingBox = thumbBox.querySelector(".thumb-draging")!;const dragingBoxParent = <HTMLElement>dragingBox.parentNode;const dragingIndex = childrenList.indexOf(dragingBoxParent);// 被进入元素 indexconst enterParent = <HTMLElement>target.parentNode;const enterIndex = childrenList.indexOf(enterParent);if (dragingIndex > enterIndex) {thumbBox.insertBefore(dragingBoxParent, enterParent);} else {thumbBox.insertBefore(dragingBoxParent, enterParent.nextElementSibling);}
}
private imageBoxDragEnd(e: DragEvent) {// 完成之后 更新 layerManager 的 layerListconst root = this.draw.getRootBox();const thumbBox = root.querySelector(".konva-root-container-thumb")!;const order = [];for (let i = 0; i < thumbBox.children.length; i++) {const item = <HTMLDivElement>thumbBox.children[i];order.push(parseInt(item.dataset.index!));}this.draw.getLayerManager().updateLayerList(order);
}

富文本实现

        为了丰富文本展示形式,利用quill进行富文本编辑,使用 html-to-image 转成konva图片的形式,实现富文本.

 // 1. 初始化 quillconst quill = new Quill(".richtext-editor #editor", {placeholder: "input your content...",modules: {toolbar: "#toolbar-container",},theme: "snow",});// 2. 确认按钮转 blobthis.confirmHandle = () => {return new Promise<void>((resolve) => {toBlob(quill.root).then((blob) => {// 为了下次编辑,还需要将当前的 Delta 转存到 shape 中// 下次编辑 setContents(delta: Delta, source: string = 'api'): Deltathis.result = { blob, delta: quill.getContents() };resolve();});});};

元素的复制粘贴

        我们的元素统一封装为 Group,因此,复制时,只需要拿到图形的ID属性,然后clone 一个新的图形,添加到画布上即可:

  // 复制 - 当前选中的元素public copy() {const selected = this.draw.getKonvaGraph().getSelected();if (!selected.length) return;const data = selected.map((g) => g.clone());const layerManager = this.draw.getLayerManager();layerManager.setCopyGroupList(data);}
  // 粘贴public async paste() {const layerManager = this.draw.getLayerManager();const copyGroupList = layerManager.getCopyGroupList();const layer = this.draw.getLayer();if (!copyGroupList.length || !layer) return;this.draw.clearTransformer();const newGroupList = <Konva.Group[]>[];// 不然 直接将 groupList 的group 修改 ID 及 x y 重新添加到当前 layer 上copyGroupList.forEach((group) => {const newGroup = group.clone();newGroup.setAttrs({id: getUniqueId(),x: newGroup.x() + 20,y: newGroup.y() + 20,});newGroupList.push(newGroup.clone());layer.add(newGroup);groupTransformer(this.draw, newGroup.children[0]);});// 实现持续的复制粘贴layerManager.setCopyGroupList(newGroupList);this.draw.render();}

         剪切的思路与上复制粘贴一致,就是先执行 group.destroy ,然后再将数据放置到copyGroupList 中即可。

实现文件的导入导出

        文件导入使用的是 pptxtojson这个库,

        在线体验地址:https://pipipi-pikachu.github.io/pptxtojson/

        文件导出使用的是 pptxgenjs这个库,

        官网地址:Quick Start Guide | PptxGenJS

        具体用法大家自行参考案例哈,这里不做细说了~

        文件导出这里我重点说一下哈:

我们需要设置幻灯片尺寸,也可以自定义,但是!!!

        真实的元素在页面上的位置,用的是像素!!!而不能直接转换成幻灯片的位置关系,这里建议使用 百分比 来处理,比较简单:

    /** 工具函数 - 转换成 百分比 显示 */function getPercent(type: "w" | "h" | "x" | "y", v: number) {let result = null;if (type === "h" || type === "y") result = (v / stageHeight) * 100 + "%";else result = (v / stageWidth) * 100 + "%";return <PptxGenJS.Coord>result;}

         同时,底层库不支持直接式添加文本,例如:

// 不支持直接式添加文本
slide.addShape("rect", {text:"直接式添加文本",rotate,fill: { color: getColor(fill) },x: getPercent("x", x),y: getPercent("y", y),w: getPercent("w", realWidth || width),h: getPercent("h", realHeight || height),
});

而是需要将文本节点单独添加:

//  创建文本节点
slide.addText(text, {valign: "middle",align: "center",rotate,x: getPercent("x", x),y: getPercent("y", y),w: getPercent("w", realWidth || width),h: getPercent("h", realHeight || height),color: getColor(fill),fontSize,
});

实现拖拽上传

        拖拽上传的核心事件是dragover、drop,在释放时,可以通过 dataTransfer 读取拖拽的内容,具体的使用可以看MDN dataTransfer :

container.addEventListener("dragover", e => {e.preventDefault(); ! important
});container.addEventListener("drop", e => {//  读取内容const data = e.dataTransfer?.getData("text");if (data) {// 存在则是 string 文本}else {// 不存在则读取文件const files = e.dataTransfer?.files;}
});

总结

        这篇主要丰富了Unipptx的功能,支持富文本、公式导出、拖拽上传及幻灯片排序等,还有难度较大的PPTX导入导出实现。整体来说,目前已经可完全编辑了,后面主要实现的功能有 预览、元素动画实现、协同实现。

        最近工作较忙哈,更新较慢,大家多多谅解,欢迎大家fork代码,一起创作开发~

        gitee:https://gitee.com/wfeng0/uni-pptx(未完全开发版)

        官方文档:https://wf0.github.io/unippt.html(非完整文档)

        大家有啥新的想法、创意,页可以留言讨论实现方案,一起完善相关功能~

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

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

相关文章

电力系统IEC-101报文主要常用详解

文章目录 1️⃣ IEC-1011.1 前言1.2 101规约简述1.3 固定帧格式1.4 可变帧格式1.5 ASDU1.5.1 常见类型标识1.5.2 常见结构限定词1.5.3 常见传送原因1.5.4 信息体地址 1.6 常用功能报文1.6.1 初始化链路报文1.6.2 总召报文1.6.3 复位进程1.8.4 对时1.8.4.1时钟读取1.8.4.2时钟写…

适用于 vue react Es6 jQuery 等等的组织架构图(组织结构图)

我这里找的是 OrgChart 插件; 地址: GitHub - dabeng/OrgChart: Its a simple and direct organization chart plugin. Anytime you want a tree-like chart, you can turn to OrgChart. 这里面能满足你对组织架构图的一切需求! ! ! 例: 按需加载 / 拖拽 / 编辑 / 自定义 / …

基于STM32F407VGT6芯片----跑马灯实验

一、在STM32F407VGT6芯片中配置GPIO环境 对于一个跑马灯实验&#xff0c;首先&#xff0c;要了解的就是&#xff0c;芯片是如何构造出来的&#xff0c;设计GPIO引脚&#xff1a;根据原理图&#xff0c; PC4&#xff0c;PC5,PC6,PC7 为 LED 输出控制管脚&#xff0c;PE0 为蜂鸣…

机器学习面试笔试知识点-线性回归、逻辑回归(Logistics Regression)和支持向量机(SVM)

机器学习面试笔试知识点-线性回归、逻辑回归Logistics Regression和支持向量机SVM 一、线性回归1.线性回归的假设函数2.线性回归的损失函数(Loss Function)两者区别3.简述岭回归与Lasso回归以及使用场景4.什么场景下用L1、L2正则化5.什么是ElasticNet回归6.ElasticNet回归的使…

嵌套div导致子区域margin失效问题解决

嵌套div导致子区域margin失效问题解决 现象原因解决方法 现象 <div class"prev"></div> <div class"parent"><div class"child"></div><div class"child"></div> </div> <div cl…

cisco网络安全技术第3章测试及考试

测试 使用本地数据库保护设备访问&#xff08;通过使用 AAA 中央服务器来解决&#xff09;有什么缺点&#xff1f; 试题 1选择一项&#xff1a; 必须在每个设备上本地配置用户帐户&#xff0c;是一种不可扩展的身份验证解决方案。 请参见图示。AAA 状态消息的哪一部分可帮助…

低代码可视化-uniapp海报可视化设计-代码生成

在uni-app中&#xff0c;海报生成器通常是通过集成特定的插件或组件来实现的&#xff0c;这些插件或组件提供了生成海报所需的功能和灵活性。我们采用了lime-painter海报组件。lime-painter是一款canvas海报组件&#xff0c;可以更轻松地生成海报。它支持通过JSON及Template的方…

企业网站设计之网站版式设计

一个成功的企业网站不仅仅需要强大的技术支持&#xff0c;更需要合理而吸引人的版式设计。版式设计在网站建设中扮演着关键角色&#xff0c;直接影响用户体验和品牌形象。本文将探讨主题企业网站版式设计的关键要素。 一、清晰的信息结构&#xff1a; 一个主题企业网站应该具有…

STM32学习笔记---独立看门狗

目录 一、什么是独立看门狗 1、什么是看门狗 2、看门狗的原理 3、看门狗的作用 4、看门狗的分类 二、如何配置独立看门狗 1、独立看门狗框图 2、独立看门狗的相关寄存器 2.1 关键字寄存器 2.2 分频寄存器 2.3 重载值寄存器 2.4 状态寄存器 3、程序设计 4、独立看门…

零基础入门人工智能,如何利用AI工具提升你的学习效率?

在这个信息爆炸的时代&#xff0c;人工智能&#xff08;AI&#xff09;不仅是技术行业的热词&#xff0c;更是我们日常生活中不可或缺的部分。你是否也想过&#xff0c;如何更有效地学习和利用这些强大的AI工具来提升自己的学习效率&#xff1f;今天&#xff0c;我们将介绍六款…

【WRF工具】QGis插件GIS4WRF:根据嵌套网格生成namelist.wps文件

【WRF工具】QGis插件GIS4WRF:根据嵌套网格生成namelist.wps文件 准备:WRF嵌套网格QGis根据嵌套网格生成namelist.wps文件检查:根据namelist.wps绘制模拟区域ArcGIS Pro中绘制嵌套网络投影变换参考GIS4WRF 是一个免费且开源的 QGIS 插件,旨在帮助研究人员和从业者进行高级研…

【Hive】8-Hive性能优化及Hive3新特性

Hive性能优化及Hive3新特性 Hive表设计优化 Hive查询基本原理 Hive的设计思想是通过元数据解析描述将HDFS上的文件映射成表 基本的查询原理是当用户通过HQL语句对Hive中的表进行复杂数据处理和计算时&#xff0c;默认将其转换为分布式计算 MapReduce程序对HDFS中的数据进行…

玫瑰花HTML源码

HTML源码 <pre id"tiresult" style"font-size: 9px; background-color: #000000; font-weight: bold; padding: 4px 5px; --fs: 9px;"><b style"color:#000000">0010000100000111101110110111100010000100000100001010111111100110…

buuctf[湖南省赛2019]Findme1

解压得5个图片&#xff0c;其中图片1&#xff0c;高度不正常&#xff0c;使用下面脚本破解真实高度和宽度 import os import binascii import structcrcbp open("1.png", "rb").read() for i in range(1024):for j in range(1024):data crcbp[12:16] st…

维修数据屏:重塑热力公司运维管理新格局

在热力公司的运维管理中&#xff0c;高效的报修和维修流程是确保系统稳定运行的关键。随着科技的发展&#xff0c;维修数据屏的出现为热力公司的运维工作带来了重大变革。 一、传统热力运维面临的挑战 过去&#xff0c;热力公司在报修和维修方面存在诸多问题&#xff0c;给运维…

SpringCloud学习:Seata总结与回顾

SpringCloud学习&#xff1a;Seata总结与回顾 文章目录 SpringCloud学习&#xff1a;Seata总结与回顾1. Seata实战&#xff1a;测试2. Seate原理总结和面试题3. Seata总结与回顾4. 易混点 1. Seata实战&#xff1a;测试 测试问题 未启用分布式事务 若不使用分布式事务&#xf…

Greenhills学习总结

学习背景&#xff1a;近期参与xx项目过程中&#xff0c;遇到较多的关于代码集成编译的知识盲区&#xff0c;因此需要进行相关知识的学习和扫盲。 参考资料&#xff1a;GreenHills2017.7编译手册:本手册是GreenHills 2017.7.14版编译器的软件使用手册。该手册详细介绍了GreenHi…

Docker consul注册中心

一、consul 1.1、什么是服务注册与发现 服务注册与发现是微服务架构中不可或缺的重要组件。 起初服务都是单节点的&#xff0c;不保障高可用性&#xff0c;也不考虑服务的压力承载&#xff0c;服务之间调用单纯的通过接口访问。 直到后来出现了多个节点的分布式架构&#x…

React(五) 受控组件和非受控组件; 获取表单元素的值。高阶组件(重点),Portals; Fragment组件;严格模式StrictMode

文章目录 一、受控组件1. 什么是受控组件2. 收集input框内容3. 收集checkBox的值4. 下拉框select总结 二、非受控组件三、高阶组件1. 高阶组件的概念 (回顾高阶函数)2. 高阶组件应用&#xff1a;注入props(1) 高阶组件给---函数式组件注入props(2) 高阶组件给---类组件注入prop…

前100+大型语言模型(LLMs)面试问题和路线图

介绍 获取前 100 个精选的 LLM 面试问题&#xff0c;了解如何准备生成式 AI 或 LLM 面试准备和大型语言模型 &#xff08;LLM&#xff09; 面试准备的学习路径。 This article explains learning path for large language models (LLMs) interview preparation. You will fin…