perf: optimize element dependencies effect trace performance (#6249)

* perf: add baseline performance report

* perf: optmize effect performance

* perf: generate performance report

* test: update test demo

---------

Co-authored-by: antv <antv@antfin.com>
This commit is contained in:
Aaron 2024-08-29 19:45:33 +08:00 committed by GitHub
parent 8553175ce0
commit 2c21154855
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 597 additions and 91 deletions

View File

@ -51,7 +51,7 @@
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-plugin-jsdoc": "^46.10.1", "eslint-plugin-jsdoc": "^46.10.1",
"husky": "^8.0.3", "husky": "^8.0.3",
"iperf": "^0.1.0-beta.11", "iperf": "^0.1.0-beta.13",
"jest": "^29.7.0", "jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0", "jest-environment-jsdom": "^29.7.0",
"jsdom": "^23.2.0", "jsdom": "^23.2.0",

View File

@ -1,6 +1,7 @@
import type { DisplayObject, Group } from '@antv/g'; import type { DisplayObject, Group } from '@antv/g';
import type { BaseComboStyleProps, GraphData, HTMLStyleProps, IElementEvent, NodeData } from '@antv/g6'; import type { BaseComboStyleProps, GraphData, HTMLStyleProps, IElementEvent, NodeData } from '@antv/g6';
import { BaseCombo, effect, ExtensionCategory, Graph, HTML, isCollapsed, register } from '@antv/g6'; import { BaseCombo, ExtensionCategory, Graph, HTML, isCollapsed, register } from '@antv/g6';
import { isEqual } from '@antv/util';
export const elementHTMLSubGraph: TestCase = async (context) => { export const elementHTMLSubGraph: TestCase = async (context) => {
interface CardNodeData { interface CardNodeData {
@ -38,13 +39,14 @@ export const elementHTMLSubGraph: TestCase = async (context) => {
private graph?: Graph; private graph?: Graph;
@effect((self, attributes) => { private previousData?: Record<string, unknown>;
const { data } = self.data;
return { data };
})
private drawSubGraph() { private drawSubGraph() {
if (!this.isConnected) return; if (!this.isConnected) return;
const data = this.data; const data = this.data;
if (isEqual(this.previousData, data)) return;
this.previousData = data;
this.drawGraphNode(data!.data as GraphData); this.drawGraphNode(data!.data as GraphData);
} }

View File

@ -0,0 +1,246 @@
{
"version": "1.0",
"device": {
"os": {
"arch": "arm64",
"distro": "macOS",
"serial": "9821ed36011eee5abf6c71d6fc2c03fb4bf4655e674c56b7f50e2560cb6e924a"
},
"cpu": {
"manufacturer": "Apple",
"brand": "M1 Pro",
"speed": 2.4,
"cores": 10
},
"memory": {
"total": 16384,
"free": 540.28125
},
"gpu": {
"vendor": "Apple",
"model": "Apple M1 Pro",
"cores": "16"
}
},
"repo": "aa87ec67c38f03808c72d82caaa3d064b4ee9c01",
"client": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/128.0.6613.18 Safari/537.36",
"reports": {
"UpdateElementState": {
"time": [
{
"min": 167.09999999403954,
"max": 202.29999999701977,
"median": 171.79999999701977,
"avg": 172.86250000447035,
"variance": 18.562343744896353,
"reliable": true,
"key": "update element state to selected"
},
{
"min": 102.20000000298023,
"max": 116.09999999403954,
"median": 104.30000001192093,
"avg": 105.06250000186265,
"variance": 3.0598437559511513,
"reliable": true,
"key": "update element state to default"
},
{
"min": 86,
"max": 105.8999999910593,
"median": 88.5,
"avg": 88.51249999925494,
"variance": 2.073593743089587,
"reliable": true,
"key": "update element position"
}
],
"status": "passed"
},
"dataDiff1000": {
"time": [
{
"min": 2.7000000029802322,
"max": 11.300000011920929,
"median": 5.5,
"avg": 5.512499997392297,
"variance": 2.638593754386529,
"reliable": false,
"key": "data diff"
}
],
"status": "passed"
},
"dataDiff10000": {
"time": [
{
"min": 2.8999999910593033,
"max": 12.099999994039536,
"median": 6,
"avg": 6.325000004842877,
"variance": 4.859374997671694,
"reliable": false,
"key": "data diff"
}
],
"status": "passed"
},
"dataDiff100000": {
"time": [
{
"min": 11,
"max": 39.29999999701977,
"median": 12.700000002980232,
"avg": 16.387499999254942,
"variance": 60.1160937285237,
"reliable": true,
"key": "data diff"
}
],
"status": "passed"
},
"dataDiff5000": {
"time": [
{
"min": 1.8999999910593033,
"max": 9.5,
"median": 8.099999994039536,
"avg": 6.374999998137355,
"variance": 6.324374991413206,
"reliable": false,
"key": "data diff"
}
],
"status": "passed"
},
"dataDiff50000": {
"time": [
{
"min": 5.700000002980232,
"max": 17.899999991059303,
"median": 8.299999997019768,
"avg": 9.5,
"variance": 16.31750000014901,
"reliable": true,
"key": "data diff"
}
],
"status": "passed"
},
"elementDrawing100": {
"time": [
{
"min": 12.700000002980232,
"max": 24.600000008940697,
"median": 18.899999991059303,
"avg": 19.199999999254942,
"variance": 8.127499995231629,
"reliable": true,
"key": "element drawing"
},
{
"min": 7.9000000059604645,
"max": 14.100000008940697,
"median": 11.900000005960464,
"avg": 11.237500000745058,
"variance": 3.8348437521792946,
"reliable": true,
"key": "grid layout"
}
],
"status": "passed"
},
"elementDrawing1000": {
"time": [
{
"min": 48.099999994039536,
"max": 73.20000000298023,
"median": 61.70000000298023,
"avg": 61.374999998137355,
"variance": 13.14437501249835,
"reliable": true,
"key": "element drawing"
},
{
"min": 39.20000000298023,
"max": 59.1000000089407,
"median": 43.29999999701977,
"avg": 44.212499998509884,
"variance": 14.278593749292193,
"reliable": true,
"key": "grid layout"
}
],
"status": "passed"
},
"elementDrawing10000": {
"time": [
{
"min": 409.3999999910593,
"max": 536.2000000029802,
"median": 434.3999999910593,
"avg": 442.1124999951571,
"variance": 829.2260936078895,
"reliable": true,
"key": "element drawing"
},
{
"min": 372.90000000596046,
"max": 414.20000000298023,
"median": 393.29999999701977,
"avg": 389.8499999977648,
"variance": 108.12999993681908,
"reliable": true,
"key": "grid layout"
}
],
"status": "passed"
},
"elementDrawing500": {
"time": [
{
"min": 28.400000005960464,
"max": 51.099999994039536,
"median": 41.79999999701977,
"avg": 41.07499999925494,
"variance": 15.709374984912573,
"reliable": true,
"key": "element drawing"
},
{
"min": 22.400000005960464,
"max": 29,
"median": 25.599999994039536,
"avg": 25.399999998509884,
"variance": 3.570000005811453,
"reliable": true,
"key": "grid layout"
}
],
"status": "passed"
},
"elementDrawing5000": {
"time": [
{
"min": 213.79999999701977,
"max": 266.5,
"median": 223.09999999403954,
"avg": 230.39999999850988,
"variance": 165.17250003792344,
"reliable": true,
"key": "element drawing"
},
{
"min": 185.79999999701977,
"max": 224.1000000089407,
"median": 195.70000000298023,
"avg": 193.41250000149012,
"variance": 35.76859376054257,
"reliable": true,
"key": "grid layout"
}
],
"status": "passed"
}
}
}

View File

@ -0,0 +1,246 @@
{
"version": "1.0",
"device": {
"os": {
"arch": "arm64",
"distro": "macOS",
"serial": "9821ed36011eee5abf6c71d6fc2c03fb4bf4655e674c56b7f50e2560cb6e924a"
},
"cpu": {
"manufacturer": "Apple",
"brand": "M1 Pro",
"speed": 2.4,
"cores": 10
},
"memory": {
"total": 16384,
"free": 224.8125
},
"gpu": {
"vendor": "Apple",
"model": "Apple M1 Pro",
"cores": "16"
}
},
"repo": "7fbbb77580d806932e7b777b34856243600dbf35",
"client": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/128.0.6613.18 Safari/537.36",
"reports": {
"UpdateElementState": {
"time": [
{
"min": 115.70000000298023,
"max": 146.29999999701977,
"median": 118.5,
"avg": 119.1750000026077,
"variance": 11.989375011604281,
"reliable": true,
"key": "update element state to selected"
},
{
"min": 72.90000000596046,
"max": 81,
"median": 75.5,
"avg": 75.12500000186265,
"variance": 1.299374995883554,
"reliable": true,
"key": "update element state to default"
},
{
"min": 61.29999999701977,
"max": 73.59999999403954,
"median": 63.29999999701977,
"avg": 63.28749999962747,
"variance": 0.2935937537904829,
"reliable": true,
"key": "update element position"
}
],
"status": "passed"
},
"dataDiff1000": {
"time": [
{
"min": 4.399999991059303,
"max": 13.900000005960464,
"median": 6.4000000059604645,
"avg": 6.262500002980232,
"variance": 1.0873437525331973,
"reliable": false,
"key": "data diff"
}
],
"status": "passed"
},
"dataDiff10000": {
"time": [
{
"min": 2.8999999910593033,
"max": 13.700000002980232,
"median": 6.0999999940395355,
"avg": 6.199999999254942,
"variance": 3.767500012814999,
"reliable": false,
"key": "data diff"
}
],
"status": "passed"
},
"dataDiff100000": {
"time": [
{
"min": 10.899999991059303,
"max": 41.70000000298023,
"median": 12.299999997019768,
"avg": 17.51249999552965,
"variance": 96.928593772389,
"reliable": true,
"key": "data diff"
}
],
"status": "passed"
},
"dataDiff5000": {
"time": [
{
"min": 3.0999999940395355,
"max": 13,
"median": 3.6000000089406967,
"avg": 5.262499999254942,
"variance": 10.062343739960342,
"reliable": false,
"key": "data diff"
}
],
"status": "passed"
},
"dataDiff50000": {
"time": [
{
"min": 5.4000000059604645,
"max": 15.400000005960464,
"median": 8.399999991059303,
"avg": 8.549999998882413,
"variance": 8.840000012554228,
"reliable": true,
"key": "data diff"
}
],
"status": "passed"
},
"elementDrawing100": {
"time": [
{
"min": 10.200000002980232,
"max": 30.099999994039536,
"median": 18.600000008940697,
"avg": 19.499999998137355,
"variance": 17.06999999091029,
"reliable": true,
"key": "element drawing"
},
{
"min": 6.5999999940395355,
"max": 11.200000002980232,
"median": 8.400000005960464,
"avg": 8.550000002607703,
"variance": 0.5175000003352761,
"reliable": true,
"key": "grid layout"
}
],
"status": "passed"
},
"elementDrawing1000": {
"time": [
{
"min": 55.3999999910593,
"max": 75.5,
"median": 65.29999999701977,
"avg": 65.09999999962747,
"variance": 7.902500012740493,
"reliable": true,
"key": "element drawing"
},
{
"min": 31.700000002980232,
"max": 47,
"median": 36.20000000298023,
"avg": 36.34999999962747,
"variance": 5.345000000037253,
"reliable": true,
"key": "grid layout"
}
],
"status": "passed"
},
"elementDrawing10000": {
"time": [
{
"min": 404.40000000596046,
"max": 515.7999999970198,
"median": 420.6000000089407,
"avg": 427.75000000186265,
"variance": 282.06500002410263,
"reliable": true,
"key": "element drawing"
},
{
"min": 306.70000000298023,
"max": 341,
"median": 321.6000000089407,
"avg": 319.4125000014901,
"variance": 58.756093712486326,
"reliable": true,
"key": "grid layout"
}
],
"status": "passed"
},
"elementDrawing500": {
"time": [
{
"min": 25,
"max": 59.5,
"median": 40.70000000298023,
"avg": 42.03749999962747,
"variance": 15.694843770219013,
"reliable": true,
"key": "element drawing"
},
{
"min": 16.69999998807907,
"max": 24.299999997019768,
"median": 20.799999997019768,
"avg": 20.25,
"variance": 5.507500002086163,
"reliable": true,
"key": "grid layout"
}
],
"status": "passed"
},
"elementDrawing5000": {
"time": [
{
"min": 203.70000000298023,
"max": 264.30000001192093,
"median": 224.79999999701977,
"avg": 225.87499999813735,
"variance": 117.29687504237518,
"reliable": true,
"key": "element drawing"
},
{
"min": 154.29999999701977,
"max": 174.6000000089407,
"median": 164.19999998807907,
"avg": 161,
"variance": 32.70499999940395,
"reliable": true,
"key": "grid layout"
}
],
"status": "passed"
}
}
}

View File

@ -0,0 +1,38 @@
import { Graph } from '@antv/g6';
import type { Test } from 'iperf';
export const UpdateElementState: Test = async ({ container, perf }) => {
const nodes = Array(1000)
.fill(0)
.map((_, i) => ({ id: `${i}` }));
const edges = Array(999)
.fill(0)
.map((_, i) => ({ id: `edge-${i}`, source: `${i}`, target: `${i + 1}` }));
const graph = new Graph({
container,
animation: false,
data: { nodes, edges },
layout: { type: 'grid' },
});
await graph.render();
const selected = [...nodes, ...edges].map((element) => [element.id, 'selected']);
await perf.evaluate('update element state to selected', async () => {
await graph.setElementState(Object.fromEntries(selected));
});
const none = [...nodes, ...edges].map((element) => [element.id, []]);
await perf.evaluate('update element state to default', async () => {
await graph.setElementState(Object.fromEntries(none));
});
const position = nodes.map((node) => [node.id, [10, 10]]);
await perf.evaluate('update element position', async () => {
await graph.translateElementBy(Object.fromEntries(position), false);
});
};

View File

@ -154,9 +154,10 @@ export abstract class BaseCombo<S extends BaseComboStyleProps = BaseComboStylePr
return getExpandedBBox(childrenBBox, padding); return getExpandedBBox(childrenBBox, padding);
} }
@effect((self, attributes) => self.getCollapsedMarkerStyle(attributes))
protected drawCollapsedMarkerShape(attributes: Required<S>, container: Group): void { protected drawCollapsedMarkerShape(attributes: Required<S>, container: Group): void {
this.upsert('collapsed-marker', Icon, this.getCollapsedMarkerStyle(attributes), container); const style = this.getCollapsedMarkerStyle(attributes);
if (!effect(this, 'collapsedMarker', style)) return;
this.upsert('collapsed-marker', Icon, style, container);
connectImage(this); connectImage(this);
} }

View File

@ -335,6 +335,8 @@ export abstract class BaseEdge extends BaseElement<BaseEdgeStyleProps> implement
if (enable) { if (enable) {
const arrowStyle = this.getArrowStyle(attributes, isStart); const arrowStyle = this.getArrowStyle(attributes, isStart);
if (!effect(this, `arrow-${type}`, arrowStyle)) return;
const [marker, markerOffset, arrowOffset] = isStart const [marker, markerOffset, arrowOffset] = isStart
? (['markerStart', 'markerStartOffset', 'startArrowOffset'] as const) ? (['markerStart', 'markerStartOffset', 'startArrowOffset'] as const)
: (['markerEnd', 'markerEndOffset', 'endArrowOffset'] as const); : (['markerEnd', 'markerEndOffset', 'endArrowOffset'] as const);
@ -376,35 +378,36 @@ export abstract class BaseEdge extends BaseElement<BaseEdgeStyleProps> implement
); );
} }
@effect((self, attributes) => self.getLabelStyle(attributes))
protected drawLabelShape(attributes: ParsedBaseEdgeStyleProps, container: Group) { protected drawLabelShape(attributes: ParsedBaseEdgeStyleProps, container: Group) {
this.upsert('label', Label, this.getLabelStyle(attributes), container); const style = this.getLabelStyle(attributes);
if (!effect(this, 'label', style)) return;
this.upsert('label', Label, style, container);
} }
@effect((self, attributes) => self.getHaloStyle(attributes))
protected drawHaloShape(attributes: ParsedBaseEdgeStyleProps, container: Group) { protected drawHaloShape(attributes: ParsedBaseEdgeStyleProps, container: Group) {
this.upsert('halo', Path, this.getHaloStyle(attributes), container); const style = this.getHaloStyle(attributes);
if (!effect(this, 'halo', style)) return;
this.upsert('halo', Path, style, container);
} }
@effect((self, attributes) => self.getBadgeStyle(attributes))
protected drawBadgeShape(attributes: ParsedBaseEdgeStyleProps, container: Group) { protected drawBadgeShape(attributes: ParsedBaseEdgeStyleProps, container: Group) {
this.upsert('badge', Badge, this.getBadgeStyle(attributes), container); const style = this.getBadgeStyle(attributes);
if (!effect(this, 'badge', style)) return;
this.upsert('badge', Badge, style, container);
} }
@effect((self, attributes) => self.getArrowStyle(attributes, 'start'))
protected drawSourceArrow(attributes: ParsedBaseEdgeStyleProps) { protected drawSourceArrow(attributes: ParsedBaseEdgeStyleProps) {
this.drawArrow(attributes, 'start'); this.drawArrow(attributes, 'start');
} }
@effect((self, attributes) => self.getArrowStyle(attributes, 'end'))
protected drawTargetArrow(attributes: ParsedBaseEdgeStyleProps) { protected drawTargetArrow(attributes: ParsedBaseEdgeStyleProps) {
this.drawArrow(attributes, 'end'); this.drawArrow(attributes, 'end');
} }
@effect((self, attributes) => self.getKeyStyle(attributes))
protected drawKeyShape(attributes: ParsedBaseEdgeStyleProps, container: Group): Path | undefined { protected drawKeyShape(attributes: ParsedBaseEdgeStyleProps, container: Group): Path | undefined {
const key = this.upsert('key', Path, this.getKeyStyle(attributes), container); const style = this.getKeyStyle(attributes);
return key; if (!effect(this, 'key', style)) return;
return this.upsert('key', Path, style, container);
} }
public render(attributes = this.parsedAttributes, container: Group = this): void { public render(attributes = this.parsedAttributes, container: Group = this): void {

View File

@ -1,61 +1,27 @@
import type { Element } from '../types'; import type { Element } from '../types';
import { getCachedStyle, setCacheStyle } from '../utils/cache';
const EFFECT_WEAKMAP = new WeakMap<Element, Record<string, any>>();
/** /**
* <zh/> * <zh/>
* *
* <en/> Optimize the number of method executions, and only execute the function when the style attributes change * <en/> Determine whether the given style are the same as the previous ones
* @param styler - <zh/> | <en/> Get style attribute function * @param target - <zh/> | <en/> Target element
* @returns <zh/> | <en/> Decorator * @param key - <zh/> key | <en/> Cache key
* @remarks * @param style - <zh/> | <en/> Style attribute
* <zh/> getStyle 使 attributes attributes * @returns <zh/> | <en/> Whether to execute the function
*
* shapeKey, attributes getStyle 使
*
* <en/> Only when getStyle is specified, the function will be called with the current attributes and the new attributes respectively. If they are the same, the function will not be executed.
*
* If shapeKey is specified, the attributes of the shape will be directly obtained as the original style attributes, which is usually used when the bounding box of the element is used in the getStyle function.
* @example
* <zh/> value
*
* <en/> Execute the function only when value changes
*
* ```typescript
* class CustomNode extends BaseNode {
*
* @effect((self, attributes) => {
* const { value } = attributes;
* return { value }
* })
* drawCustomShape(attributes, container) {
* this.upsert('custom', 'circle', { ...attributes }, container);
* }
* }
* ```
*/ */
export function effect(styler: (self: any, attributes: Record<string, unknown>) => Record<string, unknown>) { export function effect<T extends false | Record<string, any>>(target: Element, key: string, style: T): boolean {
return function (target: Element, propertyKey: string, descriptor: PropertyDescriptor) { if (!EFFECT_WEAKMAP.has(target)) EFFECT_WEAKMAP.set(target, {});
const fn = descriptor.value; const cache = EFFECT_WEAKMAP.get(target)!;
if (!cache[key]) {
descriptor.value = function (this: Element, attr: Record<string, unknown>, ...rest: unknown[]) { cache[key] = style;
// 初始化后需要执行首次调用 / First call after initialization return true;
const initKey = `${propertyKey}_invoke`; }
if (!getCachedStyle(this, initKey)) { const original = cache[key];
setCacheStyle(this, initKey, true); if (isStyleEqual(original, style)) return false;
return fn.call(this, attr, ...rest); cache[key] = style;
} return true;
const styleKey = `${propertyKey}_style`;
const original = getCachedStyle(this, styleKey);
const modified = styler(this, attr);
setCacheStyle(this, styleKey, modified);
if (isStyleEqual(original, modified)) return null;
return fn.call(this, attr, ...rest);
};
return descriptor;
};
} }
/** /**
@ -71,7 +37,7 @@ export function effect(styler: (self: any, attributes: Record<string, unknown>)
* *
* <en/> Perform a second-level shallow comparison to compare complex shape attributes such as badges and ports * <en/> Perform a second-level shallow comparison to compare complex shape attributes such as badges and ports
*/ */
const isStyleEqual = (a: Record<string, unknown>, b: Record<string, unknown>, depth = 2): boolean => { const isStyleEqual = (a: false | Record<string, unknown>, b: false | Record<string, unknown>, depth = 2): boolean => {
if (typeof a !== 'object' || typeof b !== 'object') return a === b; if (typeof a !== 'object' || typeof b !== 'object') return a === b;
const keys1 = Object.keys(a); const keys1 = Object.keys(a);

View File

@ -361,49 +361,54 @@ export abstract class BaseNode<S extends BaseNodeStyleProps = BaseNodeStyleProps
return getRectIntersectPoint(point, keyShapeBounds); return getRectIntersectPoint(point, keyShapeBounds);
} }
@effect((self, attributes) => self.getHaloStyle(attributes))
protected drawHaloShape(attributes: Required<S>, container: Group): void { protected drawHaloShape(attributes: Required<S>, container: Group): void {
const style = this.getHaloStyle(attributes);
if (!effect(this, 'halo', style)) return;
const keyShape = this.getShape('key'); const keyShape = this.getShape('key');
this.upsert( this.upsert('halo', keyShape.constructor as new (...args: unknown[]) => DisplayObject, style, container);
'halo',
keyShape.constructor as new (...args: unknown[]) => DisplayObject,
this.getHaloStyle(attributes),
container,
);
} }
@effect((self, attributes) => self.getIconStyle(attributes))
protected drawIconShape(attributes: Required<S>, container: Group): void { protected drawIconShape(attributes: Required<S>, container: Group): void {
this.upsert('icon', Icon, this.getIconStyle(attributes), container); const style = this.getIconStyle(attributes);
if (!effect(this, 'icon', style)) return;
this.upsert('icon', Icon, style, container);
connectImage(this); connectImage(this);
} }
@effect((self, attributes) => self.getBadgesStyle(attributes))
protected drawBadgeShapes(attributes: Required<S>, container: Group): void { protected drawBadgeShapes(attributes: Required<S>, container: Group): void {
const badgesStyle = this.getBadgesStyle(attributes); const badgesStyle = this.getBadgesStyle(attributes);
Object.keys(badgesStyle).forEach((key) => { Object.keys(badgesStyle).forEach((key) => {
this.upsert(`badge-${key}`, Badge, badgesStyle[key], container); const style = badgesStyle[key];
if (!effect(this, `badge-${key}`, style)) return;
this.upsert(`badge-${key}`, Badge, style, container);
}); });
} }
@effect((self, attributes) => self.getPortsStyle(attributes))
protected drawPortShapes(attributes: Required<S>, container: Group): void { protected drawPortShapes(attributes: Required<S>, container: Group): void {
const portsStyle = this.getPortsStyle(attributes); const portsStyle = this.getPortsStyle(attributes);
Object.keys(portsStyle).forEach((key) => { Object.keys(portsStyle).forEach((key) => {
this.upsert(`port-${key}`, GCircle, portsStyle[key] as CircleStyleProps, container); const style = portsStyle[key] as CircleStyleProps;
const shapeKey = `port-${key}`;
if (!effect(this, shapeKey, style)) return;
this.upsert(shapeKey, GCircle, style, container);
}); });
} }
@effect((self, attributes) => self.getLabelStyle(attributes))
protected drawLabelShape(attributes: Required<S>, container: Group): void { protected drawLabelShape(attributes: Required<S>, container: Group): void {
this.upsert('label', Label, this.getLabelStyle(attributes), container); const style = this.getLabelStyle(attributes);
if (!effect(this, 'label', style)) return;
this.upsert('label', Label, style, container);
} }
protected abstract drawKeyShape(attributes: Required<S>, container: Group): DisplayObject | undefined; protected abstract drawKeyShape(attributes: Required<S>, container: Group): DisplayObject | undefined;
// 用于装饰抽象方法 / Used to decorate abstract methods // 用于装饰抽象方法 / Used to decorate abstract methods
@effect((self, attributes) => self.getKeyStyle(attributes))
private _drawKeyShape(attributes: Required<S>, container: Group) { private _drawKeyShape(attributes: Required<S>, container: Group) {
return this.drawKeyShape(attributes, container); return this.drawKeyShape(attributes, container);
} }

View File

@ -47,7 +47,7 @@ export abstract class BaseShape<StyleProps extends BaseShapeStyleProps> extends
* <en> create, update or remove shape * <en> create, update or remove shape
* @param className - <zh> | <en> shape name * @param className - <zh> | <en> shape name
* @param Ctor - <zh> | <en> shape type * @param Ctor - <zh> | <en> shape type
* @param style - <zh> | <en> shape style * @param style - <zh> false | <en> shape style. Pass false to remove the shape
* @param container - <zh> | <en> container * @param container - <zh> | <en> container
* @param hooks - <zh> | <en> hooks * @param hooks - <zh> | <en> hooks
* @returns <zh> | <en> shape instance * @returns <zh> | <en> shape instance

View File

@ -28,7 +28,6 @@ export {
} from './constants'; } from './constants';
export { BaseCombo, CircleCombo, RectCombo } from './elements/combos'; export { BaseCombo, CircleCombo, RectCombo } from './elements/combos';
export { BaseEdge, Cubic, CubicHorizontal, CubicVertical, Line, Polyline, Quadratic } from './elements/edges'; export { BaseEdge, Cubic, CubicHorizontal, CubicVertical, Line, Polyline, Quadratic } from './elements/edges';
export { effect } from './elements/effect';
export { export {
BaseNode, BaseNode,
Circle, Circle,