开源白板工具 Excalidraw 架构解读

本文讲解开源白板工具 Excalidraw 的架构设计。

版本 0.16.1

技术栈

Vite + React + TypeScript + Yarn + Husky。

脚手架原来是用的是 Create React App,但这个脚手架已经不维护了,一年多没发布新版本了。

目前市面上比较流行的 React 脚手架是 Vite,所以几个月前 Excalidraw 把脚手架替换为了 Vite,很合理。

使用了 React 去实现 UI 层,国外还是 React 流行一些。

TypeScript 用于类型标注,减少一些类型错误。

Yarn 是包管理器,没有使用 monorepo。

Husky 是 git hook 库,会在本地 git commit 做一些校验。

架构设计

模块耦合比较严重,基本核心逻辑都放在 App 类组件里,导致其所在的 App.tsx 文件行数达到 8000+ 行。

数据状态大多保存在 App 类组件的 state 属性中。

在这里插入图片描述

可以看到,编辑器的内核和 UI 是强绑定的,完完全全耦合在一起了。

如果你想把这个项目的 UI 层改成基于其他框架,比如 Vue,那基本是要重写了。

但一些复杂的方法也是会抽离出来放到一个单独的文件里,比如 group.js 里放的都是和编组相关的逻辑,但使用的是函数风格,里面全是零散的函数,没有使用面向对象的写法。

渲染方案

Excalidraw 选择了 Canvas 2D 渲染方案。

Canvas 2D 的优点是 API 做了高级封装,对开发者非常友好,缺点是其并不是为了高性能而设计的,很多底层图形计算是基于 CPU,不能很好地利用 GPU 的并行计算能力。

一句话就是性能较差,但比 SVG 好。

图形库使用了开源库 rough,一款基于 Canvas 2D 和 SVG 的手绘风图形库。

图形工具

this.state.activeTool.type 会记录当前使用的是哪个工具。

工具没有抽成类,它们的逻辑混合写在鼠标事件响应函数 handleCanvasPointerDown、handleCanvasPointerMove、handleCanvasPointerUp 中。

完全耦合在一起了。

如果我来做,我会抽一个工具管理类,然后实现各种工具类,把它们注册到工具管理类里。

在这里插入图片描述

这样写维护性非常差,你要改某一个工具,比如创建矩形的逻辑,你不得不看其他工具的逻辑,要从这一坨分支里面找到创建矩形操作会走的逻辑。

图形树

图形树的状态保存在 Scene 类中:

  • nonDeletedElements:一个拍平的图形元素数组;
  • elements:历史创建的所有图形,被删除的图形还能在这里找到。

有组的概念,图形对象的 groupIds 数组属性表示当前元素在哪些组下。

渲染

渲染图形树的入口方法是 renderStaticScene。

会顺序递归图形树的图形,将它们渲染出来。

没有使用脏矩形局部渲染。

图形拾取方案

图形拾取使用了几何法

不同图形的的渲染逻辑的判断逻辑是写在一起的。

历史记录

历史记录的逻辑在 History 类中。

Excalidraw 维护了两棵树,当图形树发生了变更时,会对一一比两棵树中图形的版本号。

如果相同,说明没发生变更,不添加新的历史记录项;否则,就创建一个历史记录项。

被更新的图形会拷贝一份,保存到 elementCache 里。

stateHistory 和 redoStack 记录的是整棵树的图形 id 和新的版本号,撤销重做时,会从中取出,去更新对应的图形为指定的版本。

国际化方案

国际化代码在 i18n.ts 文件中。

使用了状态管理库 jotai,去通知组件更新。

渲染性能优化

剔除, 视口外的图形不渲染,很基本的操作。

缓存, 图形到手绘风是需要计算的,Excalidraw 会把这个结果缓存下来,如果只是几何属性的改变,就能直接使用缓存可以减少计算量。

缩放进行防抖,延迟图形树的重渲染。

滚动事件频率很高,每一帧都重渲染,对于图形很多的情况下,Excalidraw 是吃不消的,因为 Canvas 2D 性能并不高,这时候可以考虑节流或防抖去减少重渲染的次数。

我们发现,通过滚轮放大画布时,Excalidraw 的图形是模糊的,鼠标释放时才真正重渲染。

结尾

Excalidraw 作为一款白板工具,功能很完善,美中不足的地方就是代码写得太面条。

本文透析了 Excalidraw 在图形编辑器上几个比较基础但很重要的功能,希望对你进行图形编辑器的架构有帮助。

我是前端西瓜哥,欢迎关注我,学习更多图形编辑器知识。

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

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

相关文章

协议栈——收发数据(拼接网络包,自动重发,滑动窗口机制)

目录 协议栈何时发送数据~ 数据长度 IP模块的分片功能 发送频率 网络包序号~利用syn拼接网络包ack确认网络包完整 确定偏移量 服务器ack确定收到数据总长度 序号作用 双端告知各自序号 协议栈自动重发机制 大致流程 ack等待时间如何调整 是…

色彩一致性自动处理方法在遥感图像中的应用

前言 在获取卫星遥感影像时,由于受不均匀的光照、不同的大气条件和不同的传感器设备等因素的影响,遥感影像中会存在局部亮度和色彩分布不均匀的现象,下面是在BigMap地图下载器中收集的几幅谷歌卫星影像,像下面这种都是拼接好的影像…

python对RabbitMQ的简单使用

原文链接:https://blog.csdn.net/weixin_43810267/article/details/123914324 RabbitMq 是实现了高级消息队列协议(AMQP)的开源消息代理中间件。消息队列是一种应用程序对应用程序的通行方式,应用程序通过写消息,将消…

图像处理与计算机视觉--第五章-图像分割-霍夫变换

文章目录 1.霍夫变换(Hough Transform)原理介绍2.霍夫变换(Hough Transform)算法流程3.霍夫变换(Hough Transform)算法代码4.霍夫变换(Hough Transform)算法效果 1.霍夫变换(Hough Transform)原理介绍 Hough Transform是一种常用的计算机视觉图形检验方法,霍夫变换一…

【vue3】wacth监听,监听ref定义的数据,监听reactive定义的数据,详解踩坑点

假期第二篇,对于基础的知识点,我感觉自己还是很薄弱的。 趁着假期,再去复习一遍 之前已经记录了一篇【vue3基础知识点-computed和watch】 今天在学习的过程中发现,之前记录的这一篇果然是很基础的,很多东西都讲的不够…

【Kafka专题】Kafka集群架构设计原理详解

目录 前言前置知识课程内容一、Kafka的Zookeeper元数据梳理1.1 zookeeper整体数据1.2 Controller Broker选举机制1.3 Leader Partition选举机制1.4 Leader Partition自动平衡机制*1.5 Partition故障恢复机制1.6 HW一致性保障-Epoch更新机制1.7 总结 学习总结感谢 前言 Kafka的…

数学建模Matlab之数据预处理方法

本文综合代码来自文章http://t.csdnimg.cn/P5zOD 异常值与缺失值处理 %% 数据修复 % 判断缺失值和异常值并修复,顺便光滑噪音,渡边笔记 clc,clear;close all; x 0:0.06:10; y sin(x)0.2*rand(size(x)); y(22:34) NaN; % 模拟缺失值 y(89:95) 50;% 模…

竞赛选题 机器视觉 opencv 深度学习 驾驶人脸疲劳检测系统 -python

文章目录 0 前言1 课题背景2 Dlib人脸识别2.1 简介2.2 Dlib优点2.3 相关代码2.4 人脸数据库2.5 人脸录入加识别效果 3 疲劳检测算法3.1 眼睛检测算法3.2 打哈欠检测算法3.3 点头检测算法 4 PyQt54.1 简介4.2相关界面代码 5 最后 0 前言 🔥 优质竞赛项目系列&#x…

【Java 进阶篇】JDBC PreparedStatement 详解

在Java中,与关系型数据库进行交互是非常常见的任务之一。JDBC(Java Database Connectivity)是Java平台的一个标准API,用于连接和操作各种关系型数据库。其中,PreparedStatement 是 JDBC 中一个重要的接口,用…

RAID知识点总结

目录 RAID类型 RAID的数据组织及存取方式 RAID热备与重构 RAID逻辑卷 常见的RAID RAID0 RAID 1 RAID3 RAID 5 RAID 6 RAID组合 RAID 10 RAID 50 总结 RAID技术对比 RAID的应用场景 RAID2.0 使用RAID2.0的原因 RAID2.0的发展 RAID2.0技术:两层虚拟…

K8s架构简述

以部署一个nginx服务说明kubernetes系统各个组件调用关系: 一旦kubernetes环境启动之后,master和node都会将自身的信息存储到etcd数据库中 一个nginx服务的安装请求会首先被发送到master节点的apiServer组件 apiServer组件会调用scheduler组件来决定到底…

【强化学习】05 —— 基于无模型的强化学习(Prediction)

文章目录 简介蒙特卡洛算法时序差分方法Example1 MC和TD的对比偏差(Bias)/方差(Variance)的权衡Example2 Random WalkExample3 AB 反向传播(backup)Monte-Carlo BackupTemporal-Difference BackupDynamic Programming Backup Boot…

请求转发与请求作用域

创建input.jsp页面,通过表单输入学号、姓名后,单击登录按钮,控制转发到FirstServlet对其进行处理,然后通过请求对象的getRequestDispartcher()获得RequestDispartcher对象,将请求转发至SecondServlet,在Sec…

SpringBoot 可以同时处理多少请求

一、前言 首先,在Spring Boot应用中,我们可以使用 Tomcat、Jetty、Undertow 等嵌入式 Web 服务器作为应用程序的运行容器。这些服务器都支持并发请求处理的能力。另外,Spring Boot 还提供了一些配置参数,可以对 Web 服务器进行调…

北大硕士7年嵌入式学习经验分享

阶段 1 大一到大三这个阶段我与大多数学生相同: 学习本专业知识(EE专业),学习嵌入式软件开发需要的计算机课程(汇编原理,计算机组成原理,操作系统,C语言等)&#xff0c…

常见web信息泄露

一、源码(备份文件)泄露 1、git泄露 Git是一个开源的分布式版本控制系统,在执行git init初始化目录的时候,会在当前目录下自动创建一个.git目录,用来记录代码的变更记录等。发布代码的时候,如果没有把.git这个目录删除&#xff…

SpringBoot 中使用JPA

最近忙里偷闲,想写一点关于JPA的东西,另外也加深下对JPA的理解,才有了此篇博文。 一、JPA JPA (Java Persistence API)Java持久化API,是一套Sun公司Java官方制定的ORM 规范(sun公司并没有实现…

mfc140u.dll是什么文件?mfc140u放在哪个文件夹?详细修复教程

今天我想和大家分享一个非常常见的问题——mfc140u.dll丢失的困扰以及解决方法。 首先,让我们来了解一下什么是mfc140u.dll。这是一个非常重要的动态链接库文件,它是Microsoft Foundation Class Library的一个组件。许多软件和游戏都需要这个文件的支持才…

github搜索技巧

指定语言 language:java 比如我要找用java写的含有blog的内容 搜索项目名称包含关键词的内容 vue in:name 其他如项目描述跟项目文档,如下 组合使用 vue in:name,description,readme 根据Star 或者fork的数量来查找 总结 springboot vue stars:>1000 p…

(三)激光线扫描-中心线提取

光条纹中心提取算法是决定线结构光三维重建精度以及光条纹轮廓定位准确性的重要因素。 1. 光条的高斯分布 激光线条和打手电筒一样,中间最亮,越像周围延申,光强越弱,这个规则符合高斯分布,如下图。 2. 传统光条纹中心提取算法 传统的光条纹中心提取算法有 灰度重心法、…