【可视化系统】如何维护保存状态并实现undo和redo?
前言
这是我们 手摸手实现可视化系统系列文章的第六篇,前面我们完成了:
- 主舞台的搭建
- 拖放组件到主舞台
- 组件的自由变换
- 属性配置面板的设计与实现 专栏地址: 前端可视化
接下来我们来探讨保存按钮的状态维护和撤销重做的设计和实现,如图右上角:
需求分析
保存的状态维护: 截止到上次保存之后,属性和数据未发生变化时,按钮应该是禁用状态; 有数据和属性变化时,保存按钮可点击。
undo和redo: 每次操作都需要记录,用户可点击按钮回退(undo),回退之后可以向前(redo)。
方案设计
保存状态
保存状态的实现相对比较简单,我们在项目中使用了vuex来存储状态,screen数据用来记录主舞台的属性,elements用来记录舞台上添加的全部组件的属性数据。
const state = {
elements: [],
screen: {},
screenId: "",
screenChanged: false,
elementsChanged: false,
};
当我们调用更新elements
和screen
方法的时候,修改screenChange和elementsChange
setScreenData(state, value) {
if (state.screen.id && state.screen.id === value.id) {
state.screenChanged = true;
}
Object.assign(state.screen, { ...value });
},
这里state.screen.id && state.screen.id === value.id
判断是为了排除掉切换项目的操作,刚进来一个项目并不算数据变动,不需要激活保存按钮。
changed: state => {
return state.screenChanged || state.elementsChanged;
},
两者任意数据变动都应激活保存按钮。
undo和redo
undo和redo 相对来说就比较复杂一些。 先来画个图分析一下:
场景一:从E回退到C ,然后执行了新的操作F,那么D E 需要从记录队列中删除,F加入到C之后。
场景二: F之后回退到C,还能够再redo到F
场景三: 队列达到最大值之后,每从队尾加一个,就需要从队首剔除一个。
思路比较清晰了:
- 维护一个队列(用数组模拟), 存储每一步的操作
- 回退之后再执行新操作,清除已回退的步骤 -- 场景一
- 数组达到最大值之后,每添加一天新纪录就删除一条最旧记录
这里的难点也就是如何存储每一步操作? 以怎样的格式存储?
-
方案一: 把全部的数据序列化之后,直接存入数组,回退就从数组中取出数据进行反序列化。 这种方案简单粗暴,能够实现功能,但是会需要存储大量的冗余数据。
-
方案二: vue虚拟dom的更新不是用到了diff算法吗? 我们是不是也可以比对 数据的变化, 只记录变化的数据。
最终方案
store.subscribe(() => {})
监听 mutation 的变化 v3.vuex.vuejs.org/zh/api/#sub…json-diff
比较数据的变化 github.com/andreyvit/j… (备选github.com/benjamine/j…)- 存储并维护快照数组 juejin.cn/post/699029…
方案有了,下一步就是撸代码了~
TODO
p0:
- 预览页面
- 图表组件的完善
p1:
- 看板导出成文件(可以独立运行)
- 数据接入
项目源码地址
github: vis-board-webpack 有兴趣的小伙伴可以fork、star、pr,欢迎大佬莅临指导~
写在最后
更文不易,你的支持是我持续创作的动力~
如果对可视化系统感兴趣,可以持续关注专栏。
转载自:https://juejin.cn/post/7107632694548742152