fix: fix part of element lost when set visibility in animation

This commit is contained in:
antv 2024-08-19 19:42:09 +08:00
parent ab97b6a84c
commit 596d024aed
9 changed files with 174 additions and 9 deletions

View File

@ -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');
});
});

View File

@ -0,0 +1,40 @@
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" style="background: transparent; position: absolute; outline: none;" color-interpolation-filters="sRGB" tabindex="1">
<defs/>
<g >
<g fill="none">
<g fill="none" class="elements">
<g fill="none" x="100" y="100" transform="matrix(1,0,0,1,100,100)">
<g>
<circle fill="rgba(23,131,255,1)" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" r="16"/>
</g>
<g fill="none" class="label" transform="matrix(1,0,0,1,0,18)">
<g>
<text fill="rgba(0,0,0,1)" dominant-baseline="central" paint-order="stroke" dx="0.5" dy="11.5px" class="text" font-size="12" font-family="system-ui, sans-serif" text-anchor="middle" fill-opacity="0.85" font-weight="400">
label
</text>
</g>
</g>
<g fill="none" class="icon" width="25.6" height="25.6">
<g>
<text fill="rgba(255,255,255,1)" dominant-baseline="central" paint-order="stroke" dx="0.5" class="icon" text-anchor="middle" width="25.6" height="25.6" font-size="16">
icon
</text>
</g>
</g>
<g fill="none" class="badge-0" transform="matrix(1,0,0,1,16,-16)">
<g fill="none" class="label">
<g>
<path fill="rgba(126,146,181,1)" d="M 4.5,0 l 0.879999999999999,0 a 8.5,8.5,0,0,1,8.5,8.5 l 0,0 a 8.5,8.5,0,0,1,-8.5,8.5 l -0.879999999999999,0 a 8.5,8.5,0,0,1,-8.5,-8.5 l 0,0 a 8.5,8.5,0,0,1,8.5,-8.5 z" class="background" stroke-width="0" x="-4" y="0" width="17.88" height="17"/>
</g>
<g>
<text fill="rgba(255,255,255,1)" dominant-baseline="central" paint-order="stroke" dx="0.5" dy="8.5px" class="text" font-size="8" font-family="system-ui, sans-serif" text-anchor="left">
b1
</text>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,40 @@
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" style="background: transparent; position: absolute; outline: none;" color-interpolation-filters="sRGB" tabindex="1">
<defs/>
<g >
<g fill="none">
<g fill="none" class="elements">
<g fill="none" x="100" y="100" transform="matrix(1,0,0,1,100,100)">
<g>
<circle fill="rgba(23,131,255,1)" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" r="16" opacity="0" visibility="hidden"/>
</g>
<g fill="none" class="label" transform="matrix(1,0,0,1,0,18)">
<g>
<text fill="rgba(0,0,0,1)" dominant-baseline="central" paint-order="stroke" dx="0.5" dy="11.5px" class="text" font-size="12" font-family="system-ui, sans-serif" text-anchor="middle" fill-opacity="0.85" font-weight="400" opacity="0" visibility="hidden">
label
</text>
</g>
</g>
<g fill="none" class="icon" width="25.6" height="25.6">
<g>
<text fill="rgba(255,255,255,1)" dominant-baseline="central" paint-order="stroke" dx="0.5" class="icon" text-anchor="middle" width="25.6" height="25.6" font-size="16" opacity="0" visibility="hidden">
icon
</text>
</g>
</g>
<g fill="none" class="badge-0" transform="matrix(1,0,0,1,16,-16)">
<g fill="none" class="label">
<g>
<path fill="rgba(126,146,181,1)" d="M 4.5,0 l 0.879999999999999,0 a 8.5,8.5,0,0,1,8.5,8.5 l 0,0 a 8.5,8.5,0,0,1,-8.5,8.5 l -0.879999999999999,0 a 8.5,8.5,0,0,1,-8.5,-8.5 l 0,0 a 8.5,8.5,0,0,1,8.5,-8.5 z" class="background" stroke-width="0" x="-4" y="0" width="17.88" height="17" opacity="0" visibility="hidden"/>
</g>
<g>
<text fill="rgba(255,255,255,1)" dominant-baseline="central" paint-order="stroke" dx="0.5" dy="8.5px" class="text" font-size="8" font-family="system-ui, sans-serif" text-anchor="left" opacity="0" visibility="hidden">
b1
</text>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,40 @@
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" style="background: transparent; position: absolute; outline: none;" color-interpolation-filters="sRGB" tabindex="1">
<defs/>
<g >
<g fill="none">
<g fill="none" class="elements">
<g fill="none" x="100" y="100" transform="matrix(1,0,0,1,100,100)">
<g>
<circle fill="rgba(23,131,255,1)" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" r="16" visibility="visible"/>
</g>
<g fill="none" class="label" transform="matrix(1,0,0,1,0,18)">
<g>
<text fill="rgba(0,0,0,1)" dominant-baseline="central" paint-order="stroke" dx="0.5" dy="11.5px" class="text" font-size="12" font-family="system-ui, sans-serif" text-anchor="middle" fill-opacity="0.85" font-weight="400" visibility="visible">
label
</text>
</g>
</g>
<g fill="none" class="icon" width="25.6" height="25.6">
<g>
<text fill="rgba(255,255,255,1)" dominant-baseline="central" paint-order="stroke" dx="0.5" class="icon" text-anchor="middle" width="25.6" height="25.6" font-size="16" visibility="visible">
icon
</text>
</g>
</g>
<g fill="none" class="badge-0" transform="matrix(1,0,0,1,16,-16)">
<g fill="none" class="label">
<g>
<path fill="rgba(126,146,181,1)" d="M 4.5,0 l 0.879999999999999,0 a 8.5,8.5,0,0,1,8.5,8.5 l 0,0 a 8.5,8.5,0,0,1,-8.5,8.5 l -0.879999999999999,0 a 8.5,8.5,0,0,1,-8.5,-8.5 l 0,0 a 8.5,8.5,0,0,1,8.5,-8.5 z" class="background" stroke-width="0" x="-4" y="0" width="17.88" height="17" visibility="visible"/>
</g>
<g>
<text fill="rgba(255,255,255,1)" dominant-baseline="central" paint-order="stroke" dx="0.5" dy="8.5px" class="text" font-size="8" font-family="system-ui, sans-serif" text-anchor="left" visibility="visible">
b1
</text>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -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');

View File

@ -72,6 +72,7 @@ export class OptimizeViewportTransform extends BaseBehavior<OptimizeViewportTran
setVisibility(
element,
visibility,
false,
(shape) => !!shape.className && !excludedClassnames?.includes(shape.className),
);
});

View File

@ -222,7 +222,7 @@ export abstract class BaseShape<StyleProps extends BaseShapeStyleProps> extends
private setVisibility() {
const { visibility } = this.attributes;
setVisibility(this, visibility);
setVisibility(this, visibility, true);
}
public destroy(): void {

View File

@ -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<Element, BaseStyleProps['visibility']>();
/**
* <zh/>
*

View File

@ -8,6 +8,7 @@ const ORIGINAL_MAP = new WeakMap<DisplayObject, BaseStyleProps['visibility']>();
* <en/> Set the visibility of the shape instance
* @param shape - <zh/> | <en/> shape instance
* @param value - <zh/> | <en/> visibility
* @param inherited - <zh/> | <en/> Whether it is from inherited styles
* @param filter - <zh/> | <en/> Filter out the shapes that need to set visibility
* @remarks
* <zh/> enableCSSParsing false
@ -17,6 +18,7 @@ const ORIGINAL_MAP = new WeakMap<DisplayObject, BaseStyleProps['visibility']>();
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);