fix(extension): 【dynamic-group】修复mousemove和isCollapsed相关问题
fix https://github.com/didi/LogicFlow/issues/1912 fix https://github.com/didi/LogicFlow/issues/1914 fix https://github.com/didi/LogicFlow/issues/1918
#1912
问题发生的原因
下面视频展示的关系为:dynamicGroupA嵌套dynamicGroupB,dynamicGroupB嵌套普通Node
由于dynamicGroupA嵌套dynamicGroupB,在dynamicGroupA进行折叠时,会触发dynamicGroupB的隐藏,从而触发dynamicGroupB的componentWillUnmount()销毁,但是如下面视频所示,dynamicGroupB的componentDidMount()注册了很多事件:
componentDidMount() {
super.componentDidMount()
const { eventCenter } = this.props.graphModel
// 在 group 旋转时,对组内的所有子节点也进行对应的旋转计算
eventCenter.on('node:rotate', this.onNodeRotate)
// 在 group 缩放时,对组内的所有子节点也进行对应的缩放计算
eventCenter.on('node:resize', this.onNodeResize)
// 在 group 移动时,对组内的所有子节点也进行对应的移动计算
eventCenter.on('node:mousemove', this.onNodeMouseMove)
}
但是忘记执行对应的事件移除,因此导致每一次折叠=>展开的时候,都会重复注册一次事件,因此导致下面视频所展示触发多次 moveNodes,也就是dynamicGroupB移动1px,会触发多次它的children的1px移动,从而造成移动错乱问题
https://github.com/user-attachments/assets/f36d09c0-d750-4c6a-86f6-5e1117949cd1
解决方法
将componentDidMount注册的监听事件方法抽离出来,为后面解除事件监听做准备
提交记录:
refactor(extension): 【dynamic-group】将componentDidMount注册的监听事件方法抽离出来,为后面解除事件监听做准备
交互逻辑无变化,只是将注册方法抽离为this.xxxx方法,放在外部
componentWillUnmount移除监听
componentWillUnmount() {
super.componentWillUnmount()
const { eventCenter } = this.props.graphModel
eventCenter.off('node:rotate', this.onNodeRotate)
eventCenter.off('node:resize', this.onNodeResize)
eventCenter.off('node:mousemove', this.onNodeMouseMove)
}
#1914
问题发生的原因
在https://github.com/didi/LogicFlow/pull/1858改变了 group移动时,移动时需要同时移动组内的所有节点的逻辑代码,原来2.0.8版本中的逻辑是:
graphModel.addNodeMoveRules((model, deltaX, deltaY) => {
// 判断如果是 group,移动时需要同时移动组内的所有节点
if (model.isGroup) {
const nodeIds = this.getNodesInGroup(model as DynamicGroupNodeModel)
graphModel.moveNodes(nodeIds, deltaX, deltaY, true)
return true
}
//...
}
在2.0.9版本中的逻辑是
而为什么会出现问题呢?
是因为
-
onDragging()->会进行坐标的处理,其中包括了SCALE的转化->触发addNodeMoveRules的注册的监听方法,也就是上面的2.0.8版本中的逻辑的graphModel.moveNodes(),此时的deltaX和deltaY是经过SCALE后的数据 - 而在在
2.0.9版本中的mousemove回调是跟onDragging()同时发生的,拿到的deltaX和deltaY是没有经过SCALE后的数据
handleMouseMove = (e: MouseEvent) => {
//...
Promise.resolve().then(() => {
this.onDragging({
deltaX,
deltaY,
event: e,
})
this.eventCenter?.emit(EventType[`${this.eventType}_MOUSEMOVE`], {
deltaX,
deltaY,
e,
data: this.data || elementData,
})
//...
}
解决方法
deltaX和deltaY增加SCALE的转化
#1918
问题发生的原因
在packages/extension/src/dynamic-group/model.ts中有这么一段代码
setAttributes() {
super.setAttributes()
// 初始化时,如果 this.isCollapsed 为 true,则主动触发一次折叠操作
if (this.isCollapsed) {
this.toggleCollapse(true)
}
}
初始化时主动触发一次折叠操作是没问题的,但是我们从toggleCollapse可以知道,我们是通过elementsModelMap去获取对应的数据,但是存在着group已经初始化完成,但是children还没初始化的情况,这个时候elementsModelMap是找不到children数据的
toggleCollapse(collapse?: boolean) {
const nextCollapseState = !!collapse
this.isCollapsed = nextCollapseState
// step 1
//...
// step 2
const childrenArr = Array.from(this.children)
forEach(childrenArr, (elementId) => {
// FIX: 当使用 graphModel.getElement 获取元素时,会因为
// const model = this.graphModel.getElement(elementId)
const model = this.graphModel.elementsModelMap.get(elementId)
//...
})
// step 3
//...
}
group已经初始化完成,但是children还没初始化的情况是如何发生的呢?
lf.render({
nodes: [
{
type: "dynamic-group",
x: 400,
y: 400,
properties: {
children: ["rect_2"],
isCollapsed: true,
},
},
{
id: "rect_2",
type: "circle",
x: 400,
y: 400,
},
],
});
当我们使用上面的数据进行渲染时,会触发getModelAfterSnapToGrid(),然后触发group的new Model(),从而触发model.setAttributes()->toggleCollapse()->使用elementsModelMap寻找对应的childrenId对应的Model,此时找不到children的Model,因为要等待group初始化好了,再执行children的new Model(),然后将children的Model放入到elementsModelMap中
getModelAfterSnapToGrid(node: NodeConfig) {
//...
const nodeModel = new Model(node, this)
this.nodeModelMap.set(nodeModel.id, nodeModel)
this.elementsModelMap.set(nodeModel.id, nodeModel)
return nodeModel
}
因此只要把数据翻转,也就是下面的顺序,先渲染children->group,那么一切就正常了!
lf.render({
nodes: [
{
id: "rect_2",
type: "circle",
x: 400,
y: 400,
},
{
type: "dynamic-group",
x: 400,
y: 400,
properties: {
children: ["rect_2"],
isCollapsed: true,
},
},
],
});
解决方法
将初始化时主动触发一次折叠操作放在全部渲染完成之后再执行,也就是
⚠️ No Changeset found
Latest commit: 05c801c7a0c13e548a0194745888e927b1614948
Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.
This PR includes no changesets
When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types
Click here to learn what changesets are, and how to add one.
Click here if you're a maintainer who wants to add a changeset to this PR
大佬也太效率了。
还有点小问题,我这边把 dynamicGroup example 中 group_1 的 isCollapsed 设置为 true,初始化之后,分组没有自动合起来 0.o,你看下你那边有没有这个问题?
oh...失误=_=刚刚提交的@wbccbchore(extension): 【dynamic-group】删除无用的注释代码
又引发另一个eslint报错,我修复下
"我这边把 dynamicGroup example 中 group_1 的 isCollapsed 设置为 true,初始化之后,分组没有自动合起来"是因为上面截图的原因 @boyongjiong 例子可能得优化下,用的还是旧的Group
需要我把这个代码也提交上去吗?
"我这边把 dynamicGroup example 中 group_1 的 isCollapsed 设置为 true,初始化之后,分组没有自动合起来"是因为上面截图的原因 @boyongjiong 例子可能得优化下,用的还是旧的Group
需要我把这个代码也提交上去吗?
哈哈哈,有点好笑了,我之前开发 dynamic-group 用的是 engine-browser-examples 这个项目,然后代码没有同步过来。。
没事,你不管了,我把这个 PR 过了,我去同步代码
"我这边把 dynamicGroup example 中 group_1 的 isCollapsed 设置为 true,初始化之后,分组没有自动合起来"是因为上面截图的原因 @boyongjiong 例子可能得优化下,用的还是旧的Group