diff --git a/packages/g6/__tests__/bugs/utils-set-visibility.spec.ts b/packages/g6/__tests__/bugs/utils-set-visibility.spec.ts new file mode 100644 index 00000000000..1d08abe9dd6 --- /dev/null +++ b/packages/g6/__tests__/bugs/utils-set-visibility.spec.ts @@ -0,0 +1,35 @@ +import { createGraph } from '@@/utils'; + +describe('bug: utils-set-visibility', () => { + it('should set correct', async () => { + const graph = createGraph({ + animation: true, + data: { + nodes: [ + { + id: 'node-0', + style: { + x: 100, + y: 100, + labelText: 'label', + iconText: 'icon', + badges: [{ text: 'b1', placement: 'right-top' }], + }, + }, + ], + }, + }); + + await graph.render(); + + await expect(graph).toMatchSnapshot(__filename); + + await graph.hideElement('node-0'); + + await expect(graph).toMatchSnapshot(__filename, 'hidden'); + + await graph.showElement('node-0'); + + await expect(graph).toMatchSnapshot(__filename, 'visible'); + }); +}); diff --git a/packages/g6/__tests__/snapshots/bugs/utils-set-visibility/default.svg b/packages/g6/__tests__/snapshots/bugs/utils-set-visibility/default.svg new file mode 100644 index 00000000000..ee65fef81cc --- /dev/null +++ b/packages/g6/__tests__/snapshots/bugs/utils-set-visibility/default.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + label + + + + + + + icon + + + + + + + + + + + b1 + + + + + + + + + \ No newline at end of file diff --git a/packages/g6/__tests__/snapshots/bugs/utils-set-visibility/hidden.svg b/packages/g6/__tests__/snapshots/bugs/utils-set-visibility/hidden.svg new file mode 100644 index 00000000000..9adc07d8001 --- /dev/null +++ b/packages/g6/__tests__/snapshots/bugs/utils-set-visibility/hidden.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + label + + + + + + + icon + + + + + + + + + + + b1 + + + + + + + + + \ No newline at end of file diff --git a/packages/g6/__tests__/snapshots/bugs/utils-set-visibility/visible.svg b/packages/g6/__tests__/snapshots/bugs/utils-set-visibility/visible.svg new file mode 100644 index 00000000000..01d1e7059a4 --- /dev/null +++ b/packages/g6/__tests__/snapshots/bugs/utils-set-visibility/visible.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + label + + + + + + + icon + + + + + + + + + + + b1 + + + + + + + + + \ No newline at end of file diff --git a/packages/g6/__tests__/unit/utils/visibility.spec.ts b/packages/g6/__tests__/unit/utils/visibility.spec.ts index 26d196beb5d..67e6037f4f5 100644 --- a/packages/g6/__tests__/unit/utils/visibility.spec.ts +++ b/packages/g6/__tests__/unit/utils/visibility.spec.ts @@ -19,12 +19,12 @@ describe('visibility', () => { expect(vShape.style.visibility).toBe(undefined); expect(hShape.style.visibility).toBe('hidden'); - shape.update({ visibility: 'hidden' }); + setVisibility(shape, 'hidden'); expect(shape.style.visibility).toBe('hidden'); expect(vShape.style.visibility).toBe('hidden'); expect(hShape.style.visibility).toBe('hidden'); - shape.update({ visibility: 'visible' }); + setVisibility(shape, 'visible'); expect(shape.style.visibility).toBe('visible'); expect(vShape.style.visibility).toBe('visible'); expect(hShape.style.visibility).toBe('hidden'); @@ -39,7 +39,7 @@ describe('visibility', () => { expect(vShape.style.visibility).toBe('hidden'); expect(hShape.style.visibility).toBe('hidden'); - shape.update({ visibility: 'visible' }); + setVisibility(shape, 'visible'); expect(shape.style.visibility).toBe('visible'); expect(vShape.style.visibility).toBe('visible'); expect(hShape.style.visibility).toBe('hidden'); diff --git a/packages/g6/src/behaviors/optimize-viewport-transform.ts b/packages/g6/src/behaviors/optimize-viewport-transform.ts index 7b29be3468a..61be23027c8 100644 --- a/packages/g6/src/behaviors/optimize-viewport-transform.ts +++ b/packages/g6/src/behaviors/optimize-viewport-transform.ts @@ -72,6 +72,7 @@ export class OptimizeViewportTransform extends BaseBehavior !!shape.className && !excludedClassnames?.includes(shape.className), ); }); diff --git a/packages/g6/src/elements/shapes/base-shape.ts b/packages/g6/src/elements/shapes/base-shape.ts index 5994a71b4f2..c5609985d13 100644 --- a/packages/g6/src/elements/shapes/base-shape.ts +++ b/packages/g6/src/elements/shapes/base-shape.ts @@ -222,7 +222,7 @@ export abstract class BaseShape extends private setVisibility() { const { visibility } = this.attributes; - setVisibility(this, visibility); + setVisibility(this, visibility, true); } public destroy(): void { diff --git a/packages/g6/src/runtime/element.ts b/packages/g6/src/runtime/element.ts index cf6691797ac..0d4c121a848 100644 --- a/packages/g6/src/runtime/element.ts +++ b/packages/g6/src/runtime/element.ts @@ -22,7 +22,7 @@ import type { State, StyleIterationContext, } from '../types'; -import { cacheStyle, getCachedStyle, hasCachedStyle, setCacheStyle } from '../utils/cache'; +import { cacheStyle, hasCachedStyle } from '../utils/cache'; import { reduceDataChanges } from '../utils/change'; import { isCollapsed } from '../utils/collapsibility'; import { markToBeDestroyed, updateStyle } from '../utils/element'; @@ -34,6 +34,7 @@ import { positionOf } from '../utils/position'; import { print } from '../utils/print'; import { computeElementCallbackStyle } from '../utils/style'; import { themeOf } from '../utils/theme'; +import { setVisibility } from '../utils/visibility'; import type { RuntimeContext } from './types'; export class ElementController { @@ -451,6 +452,10 @@ export class ElementController { const exactStage = stage !== 'visibility' ? stage : style.visibility === 'hidden' ? 'hide' : 'show'; + // 避免立即将 visibility 设置为 hidden,导致元素不可见,而是在 after 阶段再设置 + // Avoid setting visibility to hidden immediately, causing the element to be invisible, but set it in the after phase + if (exactStage === 'hide') delete style['visibility']; + this.context.animation?.add( { element, @@ -470,14 +475,14 @@ export class ElementController { // 缓存原始透明度 / Cache original opacity // 会在 animation controller 中访问该缓存值 / The cached value will be accessed in the animation controller if (!hasCachedStyle(element, 'opacity')) cacheStyle(element, 'opacity'); - setCacheStyle(element, 'visibility', exactStage === 'show' ? 'visible' : 'hidden'); - if (exactStage === 'show') updateStyle(element, { visibility: 'visible' }); + this.visibilityCache.set(element, exactStage === 'show' ? 'visible' : 'hidden'); + if (exactStage === 'show') setVisibility(element, 'visible'); } }, after: () => { const element = this.elementMap[id]; if (stage === 'collapse') updateStyle(element, style); - if (exactStage === 'hide') updateStyle(element, { visibility: getCachedStyle(element, 'visibility') }); + if (exactStage === 'hide') setVisibility(element, this.visibilityCache.get(element)); this.emit(new ElementLifeCycleEvent(GraphEvent.AFTER_ELEMENT_UPDATE, elementType, datum), context); element.onUpdate?.(); }, @@ -498,6 +503,8 @@ export class ElementController { }); } + private visibilityCache = new WeakMap(); + /** * 标记销毁元素 * diff --git a/packages/g6/src/utils/visibility.ts b/packages/g6/src/utils/visibility.ts index a09575c555c..7b24ecb870d 100644 --- a/packages/g6/src/utils/visibility.ts +++ b/packages/g6/src/utils/visibility.ts @@ -8,6 +8,7 @@ const ORIGINAL_MAP = new WeakMap(); * Set the visibility of the shape instance * @param shape - 图形实例 | shape instance * @param value - 可见性 | visibility + * @param inherited - 是否是来自继承样式 | Whether it is from inherited styles * @param filter - 筛选出需要设置可见性的图形 | Filter out the shapes that need to set visibility * @remarks * 在设置 enableCSSParsing 为 false 的情况下,复合图形无法继承父属性,因此需要对所有子图形应用相同的可见性 @@ -17,6 +18,7 @@ const ORIGINAL_MAP = new WeakMap(); export function setVisibility( shape: DisplayObject, value: BaseStyleProps['visibility'], + inherited = false, filter?: (shape: DisplayObject) => boolean, ) { if (value === undefined) return; @@ -26,7 +28,7 @@ export function setVisibility( if (filter && !filter(current)) return walk(); - if (current === shape) { + if (!inherited && current === shape) { shape.style.visibility = value; ORIGINAL_MAP.delete(shape); walk(value);