Skip to content

Commit 0039f33

Browse files
authored
feat(node-more): optimize node more awareness rectangle to avoid flickering in the mouse movement (#1123)
* feat(node-more): optimize node more awareness rectangle to avoid flickering in the mouse movement * chore: remove unnecessary code segments
1 parent d84070a commit 0039f33

File tree

3 files changed

+136
-81
lines changed

3 files changed

+136
-81
lines changed

.changeset/nine-tips-fold.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@plait/mind': minor
3+
---
4+
5+
optimize node more awareness rectangle to avoid flickering in the mouse movement

packages/mind/src/generators/node-more.generator.ts

Lines changed: 64 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import {
22
Direction,
33
PlaitBoard,
44
Point,
5+
RectangleClient,
56
createG,
67
createText,
78
getSelectedElements,
89
isDragging,
910
isMovingElements,
10-
isSelectedElement,
1111
isSelectionMoving,
1212
rgbaToHEX,
1313
setStrokeLinecap
@@ -29,13 +29,23 @@ import { MindQueries } from '../queries';
2929
import { getBranchColorByMindElement } from '../utils/node-style/branch';
3030
import { getLayoutDirection, getPointByPlacement, transformPlacement } from '../utils/point-placement';
3131
import { HorizontalPlacement, PointPlacement, VerticalPlacement } from '../interfaces/types';
32-
import { buildText, DEFAULT_FONT_FAMILY, Generator, isResizing, measureElement, moveXOfPoint, TRANSPARENT } from '@plait/common';
32+
import {
33+
buildText,
34+
DEFAULT_FONT_FAMILY,
35+
Generator,
36+
isResizing,
37+
measureElement,
38+
moveXOfPoint,
39+
moveYOfPoint,
40+
TRANSPARENT
41+
} from '@plait/common';
3342
import { getChildrenCount } from '../utils/mind';
3443
import { FontSizes } from '@plait/text-plugins';
3544

3645
export interface NodeMoreExtraData {
3746
isSelected: boolean;
3847
isHovered?: boolean;
48+
isHoveredAwarenessRectangle?: boolean | null;
3949
isHoveredCollapseArea?: boolean;
4050
isHoveredExpandArea?: boolean;
4151
isHoveredAddArea?: boolean;
@@ -52,7 +62,7 @@ export class NodeMoreGenerator extends Generator<MindElement, NodeMoreExtraData>
5262

5363
canDraw(element: MindElement<BaseData>, extraData: NodeMoreExtraData): boolean {
5464
if (
55-
((extraData?.isHovered || extraData?.isHoveredCollapseArea || extraData?.isHoveredAddArea) && canHandleNodeMore(this.board)) ||
65+
((extraData?.isHovered || extraData?.isHoveredAwarenessRectangle) && canHandleNodeMore(this.board)) ||
5666
(extraData?.isSelected && isLastSelectedMindElement(this.board, element) && canHandleNodeMore(this.board)) ||
5767
element.isCollapsed
5868
) {
@@ -64,48 +74,32 @@ export class NodeMoreGenerator extends Generator<MindElement, NodeMoreExtraData>
6474
draw(element: MindElement<BaseData>, extraData: NodeMoreExtraData): SVGGElement {
6575
const moreGContainer = createG();
6676
const stroke = getBranchColorByMindElement(this.board, element);
67-
const layoutDirection = getNodeMoreLayoutDirection(this.board, element);
68-
const moreStartAndEnd = getMoreStartAndEnd(this.board, element, layoutDirection);
69-
const collapseOrExpandCenter = moveXOfPoint(
70-
moreStartAndEnd[1],
71-
NODE_MORE_ICON_DIAMETER / 2,
72-
layoutDirection as unknown as Direction
73-
);
74-
const hasChildren = element.children.length > 0;
75-
const isShowCollapseOrAdd =
76-
!element.isCollapsed &&
77-
(isSelectedElement(this.board, element) ||
78-
!!extraData?.isHovered ||
79-
!!extraData?.isHoveredCollapseArea ||
80-
!!extraData?.isHoveredAddArea);
81-
const isShowCollapse = isShowCollapseOrAdd && hasChildren && !PlaitMind.isMind(element);
82-
const isShowAdd = isShowCollapseOrAdd && !PlaitBoard.isReadonly(this.board);
83-
const addCenter =
84-
(isShowCollapseOrAdd && getAddCenterByCollapseOrExpandCenter(element, collapseOrExpandCenter, layoutDirection)) || null;
85-
this.toggleCollapseOrAdd(
86-
collapseOrExpandCenter,
77+
const { startPoint, endPoint, hasCollapsedIcon, hasExpandedIcon, hasAddIcon, collapsedIconCenter, expandedIconCenter, addCenter } =
78+
getNodeMoreKeyPosition(this.board, element);
79+
this.toggleCollapseAddAdd(
80+
collapsedIconCenter!,
8781
addCenter,
8882
stroke,
8983
moreGContainer,
90-
isShowCollapse,
91-
isShowAdd,
84+
hasCollapsedIcon,
85+
hasAddIcon,
9286
!!extraData?.isHoveredAddArea,
9387
!!extraData?.isShowCollapseAnimation,
9488
!!extraData?.isShowAddAnimation
9589
);
9690
this.toggleExpandBadge(
9791
element,
98-
moreStartAndEnd,
99-
collapseOrExpandCenter,
92+
[startPoint, endPoint],
93+
expandedIconCenter!,
10094
stroke,
10195
moreGContainer,
102-
!!element.isCollapsed,
96+
!!hasExpandedIcon,
10397
!!extraData?.isHoveredExpandArea
10498
);
10599
return moreGContainer;
106100
}
107101

108-
toggleCollapseOrAdd(
102+
toggleCollapseAddAdd(
109103
center: Point,
110104
addCenter: Point | null,
111105
stroke: string,
@@ -256,24 +250,50 @@ export class NodeMoreGenerator extends Generator<MindElement, NodeMoreExtraData>
256250
}
257251
}
258252

259-
export const getCollapseAndAddCenterPoint = (board: PlaitBoard, element: MindElement) => {
253+
export const getNodeMoreKeyPosition = (board: PlaitBoard, element: MindElement) => {
260254
const layoutDirection = getNodeMoreLayoutDirection(board, element);
261255
const [startPoint, endPoint] = getMoreStartAndEnd(board, element, layoutDirection);
262-
const collapseCenter = moveXOfPoint(endPoint, NODE_MORE_ICON_DIAMETER / 2, layoutDirection as unknown as Direction);
263-
const addCenter = getAddCenterByCollapseOrExpandCenter(element, collapseCenter, layoutDirection);
264-
return { collapseCenter, addCenter };
265-
};
266-
267-
export const getAddCenterByCollapseOrExpandCenter = (
268-
target: MindElement,
269-
collapseOrExpandCenter: Point,
270-
layoutDirection: LayoutDirection
271-
) => {
272-
let addCenter = collapseOrExpandCenter;
273-
if (target.children?.length > 0 && !PlaitMind.isMind(target)) {
274-
addCenter = moveXOfPoint(addCenter, NODE_MORE_LINE_DISTANCE + NODE_MORE_ICON_DIAMETER, layoutDirection as unknown as Direction);
256+
const hasCollapsedIcon = element.children?.length > 0 && !PlaitMind.isMind(element) && !element.isCollapsed;
257+
const hasExpandedIcon = element.children?.length > 0 && !PlaitMind.isMind(element) && element.isCollapsed;
258+
const hasAddIcon = !hasExpandedIcon;
259+
const firstIconCenter = moveXOfPoint(endPoint, NODE_MORE_ICON_DIAMETER / 2, layoutDirection as unknown as Direction);
260+
const collapsedIconCenter = hasCollapsedIcon ? firstIconCenter : null;
261+
const expandedIconCenter = hasExpandedIcon ? firstIconCenter : null;
262+
let addCenter = null;
263+
if (hasAddIcon) {
264+
addCenter = hasCollapsedIcon
265+
? moveXOfPoint(firstIconCenter, NODE_MORE_LINE_DISTANCE + NODE_MORE_ICON_DIAMETER, layoutDirection as unknown as Direction)
266+
: firstIconCenter;
267+
}
268+
let awarenessRectangle = null;
269+
if (hasAddIcon) {
270+
const addIconEndPoint = moveXOfPoint(addCenter!, NODE_MORE_ICON_DIAMETER / 2, layoutDirection as unknown as Direction);
271+
awarenessRectangle = RectangleClient.getRectangleByPoints([
272+
moveYOfPoint(startPoint, -NODE_MORE_ICON_DIAMETER / 2, layoutDirection as unknown as Direction),
273+
moveYOfPoint(addIconEndPoint, NODE_MORE_ICON_DIAMETER / 2, layoutDirection as unknown as Direction)
274+
]);
275+
} else if (hasExpandedIcon) {
276+
const expandedIconEndPoint = moveXOfPoint(
277+
expandedIconCenter!,
278+
NODE_MORE_ICON_DIAMETER / 2,
279+
layoutDirection as unknown as Direction
280+
);
281+
awarenessRectangle = RectangleClient.getRectangleByPoints([
282+
moveYOfPoint(startPoint, -NODE_MORE_ICON_DIAMETER / 2, layoutDirection as unknown as Direction),
283+
moveYOfPoint(expandedIconEndPoint, NODE_MORE_ICON_DIAMETER / 2, layoutDirection as unknown as Direction)
284+
]);
275285
}
276-
return addCenter;
286+
return {
287+
startPoint,
288+
endPoint,
289+
hasCollapsedIcon,
290+
hasExpandedIcon,
291+
hasAddIcon,
292+
collapsedIconCenter,
293+
expandedIconCenter,
294+
addCenter,
295+
awarenessRectangle
296+
};
277297
};
278298

279299
export const getNodeMoreLayoutDirection = (board: PlaitBoard, element: MindElement) => {

packages/mind/src/plugins/with-node-more.ts

Lines changed: 67 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,36 @@ import {
1111
toViewBoxPoint,
1212
Transforms
1313
} from '@plait/core';
14-
import { MindElement, PlaitMind } from '../interfaces';
14+
import { MindElement } from '../interfaces';
1515
import { findNewChildNodePath, insertMindElement, isHitMindElement } from '../utils';
1616
import { PlaitCommonElementRef } from '@plait/common';
17-
import { canHandleNodeMore, getCollapseAndAddCenterPoint, NodeMoreGenerator } from '../generators/node-more.generator';
17+
import { canHandleNodeMore, getNodeMoreKeyPosition, NodeMoreGenerator } from '../generators/node-more.generator';
1818
import { NODE_MORE_ICON_DIAMETER } from '../constants/default';
1919
import { PlaitMindBoard } from './with-mind.board';
2020

2121
export interface NodeMoreRef {
2222
target: MindElement;
2323
isHovered: boolean;
24+
isHoveredAwarenessRectangle: boolean;
2425
isHoveredCollapseArea: boolean;
2526
isHoveredExpandArea: boolean;
2627
isHoveredAddArea: boolean;
2728
}
2829

30+
export const isSameNodeMoreRef = (ref1: NodeMoreRef | null, ref2: NodeMoreRef | null) => {
31+
if (!ref1 || !ref2) {
32+
return false;
33+
}
34+
const result =
35+
ref1.target === ref2.target &&
36+
ref1.isHovered === ref2.isHovered &&
37+
ref1.isHoveredAwarenessRectangle === ref2.isHoveredAwarenessRectangle &&
38+
ref1.isHoveredCollapseArea === ref2.isHoveredCollapseArea &&
39+
ref1.isHoveredExpandArea === ref2.isHoveredExpandArea &&
40+
ref1.isHoveredAddArea === ref2.isHoveredAddArea;
41+
return result;
42+
};
43+
2944
export const withNodeMore = (board: PlaitBoard) => {
3045
const { pointerMove, pointerLeave, pointerUp } = board;
3146
let nodeMoreRef: NodeMoreRef | null = null;
@@ -38,32 +53,27 @@ export const withNodeMore = (board: PlaitBoard) => {
3853
nodeMoreRef = null;
3954
}
4055
const newNodeMoreRef = getNodeMoreRef(board, event.x, event.y);
41-
42-
if (nodeMoreRef && newNodeMoreRef && nodeMoreRef.target === newNodeMoreRef.target) {
56+
if (nodeMoreRef && newNodeMoreRef && isSameNodeMoreRef(nodeMoreRef, newNodeMoreRef)) {
4357
return;
4458
}
45-
46-
if (nodeMoreRef) {
59+
if (nodeMoreRef && (!newNodeMoreRef || newNodeMoreRef.target !== nodeMoreRef.target)) {
4760
const element = getElementById<MindElement>(board, nodeMoreRef.target.id);
4861
// maybe element has been changed
4962
if (element && element === nodeMoreRef.target) {
5063
toggleHoveredNodeCallback({
5164
target: nodeMoreRef.target,
5265
isHovered: false,
66+
isHoveredAwarenessRectangle: false,
5367
isHoveredCollapseArea: false,
5468
isHoveredExpandArea: false,
5569
isHoveredAddArea: false
5670
});
5771
}
5872
}
5973
if (newNodeMoreRef) {
60-
toggleHoveredNodeCallback(newNodeMoreRef);
61-
if (nodeMoreRef) {
62-
nodeMoreRef.target = newNodeMoreRef.target;
63-
} else {
64-
nodeMoreRef = newNodeMoreRef;
65-
}
66-
} else {
74+
toggleHoveredNodeCallback(newNodeMoreRef, nodeMoreRef);
75+
nodeMoreRef = newNodeMoreRef;
76+
} else if (nodeMoreRef) {
6777
nodeMoreRef = null;
6878
}
6979
});
@@ -98,19 +108,22 @@ export const withNodeMore = (board: PlaitBoard) => {
98108
pointerUp(event);
99109
};
100110

101-
const toggleHoveredNodeCallback = (ref: NodeMoreRef) => {
111+
const toggleHoveredNodeCallback = (ref: NodeMoreRef, oldRef?: NodeMoreRef | null) => {
102112
const elementRef = PlaitElement.getElementRef<PlaitCommonElementRef>(ref.target);
103113
const nodeMoreGenerator = elementRef?.getGenerator<NodeMoreGenerator>(NodeMoreGenerator.key);
104114
if (nodeMoreGenerator) {
115+
const isSameTarget = oldRef?.target === ref.target;
105116
const g = PlaitElement.getElementG(ref.target);
106117
nodeMoreGenerator.processDrawing(ref.target, g, {
107118
isHovered: ref.isHovered,
119+
isHoveredAwarenessRectangle: ref.isHoveredAwarenessRectangle,
108120
isHoveredCollapseArea: ref.isHoveredCollapseArea,
109121
isHoveredExpandArea: ref.isHoveredExpandArea,
110-
isSelected: isSelectedElement(board, ref.target),
111122
isHoveredAddArea: ref.isHoveredAddArea,
112-
isShowCollapseAnimation: (ref.isHovered || ref.isHoveredCollapseArea) && !isSelectedElement(board, ref.target),
113-
isShowAddAnimation: (ref.isHovered || ref.isHoveredAddArea) && !isSelectedElement(board, ref.target)
123+
isSelected: isSelectedElement(board, ref.target),
124+
isShowCollapseAnimation:
125+
(ref.isHovered || ref.isHoveredCollapseArea) && !isSelectedElement(board, ref.target) && !isSameTarget,
126+
isShowAddAnimation: (ref.isHovered || ref.isHoveredAddArea) && !isSelectedElement(board, ref.target) && !isSameTarget
114127
});
115128
}
116129
};
@@ -120,6 +133,7 @@ export const withNodeMore = (board: PlaitBoard) => {
120133
toggleHoveredNodeCallback({
121134
target: nodeMoreRef.target,
122135
isHovered: false,
136+
isHoveredAwarenessRectangle: false,
123137
isHoveredCollapseArea: false,
124138
isHoveredExpandArea: false,
125139
isHoveredAddArea: false
@@ -135,6 +149,7 @@ export const withNodeMore = (board: PlaitBoard) => {
135149
const getNodeMoreRef = (board: PlaitBoard, x: number, y: number) => {
136150
let target: MindElement | null = null;
137151
let isHovered = false;
152+
let isHoveredAwarenessRectangle = false;
138153
let isHoveredCollapseArea = false;
139154
let isHoveredExpandArea = false;
140155
let isHoveredAddArea = false;
@@ -148,29 +163,43 @@ const getNodeMoreRef = (board: PlaitBoard, x: number, y: number) => {
148163
if (!MindElement.isMindElement(board, element)) {
149164
return;
150165
}
151-
const isMind = PlaitMind.isMind(element);
152166
const isHitElement = isHitMindElement(board, point, element);
153-
let isHitCollapseOrExpand = false;
154-
let isHitAdd = false;
155-
const { collapseCenter, addCenter } = getCollapseAndAddCenterPoint(board, element);
156-
const collapseOrExpandIconRectangle =
157-
!isMind && RectangleClient.getRectangleByCenterPoint(collapseCenter, NODE_MORE_ICON_DIAMETER, NODE_MORE_ICON_DIAMETER);
158-
isHitCollapseOrExpand =
159-
collapseOrExpandIconRectangle &&
160-
RectangleClient.isHit(RectangleClient.getRectangleByPoints([point, point]), collapseOrExpandIconRectangle);
161-
const addIconRectangle = RectangleClient.getRectangleByCenterPoint(addCenter, NODE_MORE_ICON_DIAMETER, NODE_MORE_ICON_DIAMETER);
162-
isHitAdd = RectangleClient.isHit(RectangleClient.getRectangleByPoints([point, point]), addIconRectangle);
163-
if (isHitElement || isHitCollapseOrExpand || isHitAdd) {
167+
const {
168+
hasCollapsedIcon,
169+
hasExpandedIcon,
170+
hasAddIcon,
171+
collapsedIconCenter,
172+
expandedIconCenter,
173+
addCenter,
174+
awarenessRectangle
175+
} = getNodeMoreKeyPosition(board, element);
176+
const isHitAwarenessRectangle =
177+
awarenessRectangle && RectangleClient.isHit(RectangleClient.getRectangleByPoints([point, point]), awarenessRectangle);
178+
const isHitCollapsedIcon =
179+
hasCollapsedIcon &&
180+
RectangleClient.isHit(
181+
RectangleClient.getRectangleByPoints([point, point]),
182+
RectangleClient.getRectangleByCenterPoint(collapsedIconCenter!, NODE_MORE_ICON_DIAMETER, NODE_MORE_ICON_DIAMETER)
183+
);
184+
const isHitExpandedIcon =
185+
hasExpandedIcon &&
186+
RectangleClient.isHit(
187+
RectangleClient.getRectangleByPoints([point, point]),
188+
RectangleClient.getRectangleByCenterPoint(expandedIconCenter!, NODE_MORE_ICON_DIAMETER, NODE_MORE_ICON_DIAMETER)
189+
);
190+
const isHitAddIcon =
191+
hasAddIcon &&
192+
RectangleClient.isHit(
193+
RectangleClient.getRectangleByPoints([point, point]),
194+
RectangleClient.getRectangleByCenterPoint(addCenter!, NODE_MORE_ICON_DIAMETER, NODE_MORE_ICON_DIAMETER)
195+
);
196+
if (isHitElement || isHitAwarenessRectangle) {
164197
isHovered = isHitElement;
165-
if (element.children.length > 0) {
166-
if (element.isCollapsed) {
167-
isHoveredExpandArea = isHitCollapseOrExpand;
168-
} else {
169-
isHoveredCollapseArea = isHitCollapseOrExpand;
170-
}
171-
}
172-
isHoveredAddArea = isHitAdd;
198+
isHoveredAwarenessRectangle = !!isHitAwarenessRectangle;
173199
target = element;
200+
isHoveredCollapseArea = isHitCollapsedIcon;
201+
isHoveredExpandArea = !!isHitExpandedIcon;
202+
isHoveredAddArea = isHitAddIcon;
174203
}
175204
},
176205
getIsRecursionFunc(board),
@@ -182,6 +211,7 @@ const getNodeMoreRef = (board: PlaitBoard, x: number, y: number) => {
182211
return {
183212
target,
184213
isHovered,
214+
isHoveredAwarenessRectangle,
185215
isHoveredCollapseArea,
186216
isHoveredExpandArea,
187217
isHoveredAddArea

0 commit comments

Comments
 (0)