diff --git a/common/changes/@visactor/vgrammar-core/perf-graphic-transform_2023-12-04-03-06.json b/common/changes/@visactor/vgrammar-core/perf-graphic-transform_2023-12-04-03-06.json new file mode 100644 index 000000000..13a009961 --- /dev/null +++ b/common/changes/@visactor/vgrammar-core/perf-graphic-transform_2023-12-04-03-06.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "perf: optimize the performance of `initGraphicItem()`, dont call hooks of adding/creating graphics\n\n", + "type": "none", + "packageName": "@visactor/vgrammar-core" + } + ], + "packageName": "@visactor/vgrammar-core", + "email": "dingling112@gmail.com" +} \ No newline at end of file diff --git a/packages/vgrammar-core/src/graph/attributes/transform.ts b/packages/vgrammar-core/src/graph/attributes/transform.ts index 4045b64b3..88622177b 100644 --- a/packages/vgrammar-core/src/graph/attributes/transform.ts +++ b/packages/vgrammar-core/src/graph/attributes/transform.ts @@ -13,10 +13,20 @@ function storeOriginAttributes( element: IElement, markName: string ): Record { - const prevStoredAttrs = (element as IGlyphElement).getGraphicAttribute(name, false, markName) ?? {}; + const prevStoredAttrs = (element as IGlyphElement).getGraphicAttribute(name, false, markName); + + if (prevStoredAttrs) { + channels.forEach(channel => { + if (channel in nextAttrs) { + prevStoredAttrs[channel] = nextAttrs[channel]; + } + }); + + return prevStoredAttrs; + } const storedAttrs = {}; channels.forEach(channel => { - storedAttrs[channel] = nextAttrs[channel] ?? prevStoredAttrs[channel]; + storedAttrs[channel] = nextAttrs[channel]; }); graphicAttributes[name] = storedAttrs; return storedAttrs; diff --git a/packages/vgrammar-core/src/graph/element.ts b/packages/vgrammar-core/src/graph/element.ts index 818afe494..a9881fd03 100644 --- a/packages/vgrammar-core/src/graph/element.ts +++ b/packages/vgrammar-core/src/graph/element.ts @@ -76,14 +76,16 @@ export class Element implements IElement { // 统一读取mark中是否可交互的配置 const attrTransforms = this.mark.getAttributeTransforms(); - this.graphicItem = this.mark.addGraphicItem(attrTransforms ? {} : attributes, this.groupKey); + this.graphicItem = this.mark.addGraphicItem( + attrTransforms ? transformAttributes(attrTransforms, attributes, this) : attributes, + this.groupKey + ); if (!this.graphicItem) { return; } // 统一读取mark中是否可交互的配置 this.graphicItem[BridgeElementKey] = this; - if (attrTransforms) { this.graphicItem.onBeforeAttributeUpdate = (attributes: any) => { // mark might be released @@ -93,14 +95,12 @@ export class Element implements IElement { const graphicAttributes = transformAttributes(attrTransforms, attributes, this); return graphicAttributes; }; - this.graphicItem.setAttributes(attributes); } // transform initial attributes - this.clearGraphicAttributes(); if (this.mark.needAnimate()) { - this.setPrevGraphicAttributes({}); + this.setPrevGraphicAttributes(null); this.setNextGraphicAttributes(attributes); this.setFinalGraphicAttributes(attributes); } @@ -459,11 +459,11 @@ export class Element implements IElement { let nextAttrs = item?.nextAttrs; if ( + isPointsMarkType(markType) && items && items.length && isNil(item.nextAttrs?.points) && - (computePoints === true || isValidPointsChannel(Object.keys(item.nextAttrs), this.mark.markType)) && - isPointsMarkType(markType) + (computePoints === true || isValidPointsChannel(Object.keys(item.nextAttrs), this.mark.markType)) ) { const lastPoints = this.getGraphicAttribute('points', false); const lastSegments = this.getGraphicAttribute('segments', false); @@ -641,16 +641,16 @@ export class Element implements IElement { clearChangedGraphicAttributes() { if (this.graphicItem) { - this.setPrevGraphicAttributes({}); - this.setNextGraphicAttributes({}); + this.setPrevGraphicAttributes(null); + this.setNextGraphicAttributes(null); } } clearGraphicAttributes() { if (this.graphicItem) { - this.setPrevGraphicAttributes({}); - this.setNextGraphicAttributes({}); - this.setFinalGraphicAttributes({}); + (this.graphicItem as any).prevAttrs && this.setPrevGraphicAttributes(null); + (this.graphicItem as any).nextAttrs && this.setNextGraphicAttributes(null); + (this.graphicItem as any).finalAttrs && this.setFinalGraphicAttributes(null); } } diff --git a/packages/vgrammar-core/src/graph/util/graphic.ts b/packages/vgrammar-core/src/graph/util/graphic.ts index 94363418c..daf32b6b9 100644 --- a/packages/vgrammar-core/src/graph/util/graphic.ts +++ b/packages/vgrammar-core/src/graph/util/graphic.ts @@ -2,7 +2,7 @@ import type { IGlyphMeta, IMark } from '../../types'; import type { IGraphic } from '@visactor/vrender-core'; // eslint-disable-next-line no-duplicate-imports -import { HOOK_EVENT, GrammarMarkType } from '../enums'; +import { GrammarMarkType } from '../enums'; import { BridgeElementKey } from '../constants'; import { Factory } from '../../core/factory'; import { Logger } from '@visactor/vutils'; @@ -12,8 +12,6 @@ export const isMarkType = (type: string) => { }; export function createGraphicItem(mark: IMark, markType: string, attrs: any = {}) { - mark.emit(HOOK_EVENT.BEFORE_CREATE_VRENDER_MARK); - const graphicItem: IGraphic = Factory.getGraphicType(markType) ? Factory.createGraphic(markType, attrs) : Factory.createGraphicComponent(markType, attrs, { @@ -25,12 +23,10 @@ export function createGraphicItem(mark: IMark, markType: string, attrs: any = {} logger.error(`create ${markType} graphic failed!`); } - mark.emit(HOOK_EVENT.AFTER_CREATE_VRENDER_MARK); return graphicItem; } export function createGlyphGraphicItem(mark: IMark, glyphMeta: IGlyphMeta, attrs: any = {}) { - mark.emit(HOOK_EVENT.BEFORE_CREATE_VRENDER_MARK); if (!Factory.getGraphicType(GrammarMarkType.glyph)) { return; } @@ -46,8 +42,6 @@ export function createGlyphGraphicItem(mark: IMark, glyphMeta: IGlyphMeta, attrs } }); graphicItem.setSubGraphic(subGraphics); - - mark.emit(HOOK_EVENT.AFTER_CREATE_VRENDER_MARK); return graphicItem; } diff --git a/packages/vgrammar-core/src/semantic-marks/text.ts b/packages/vgrammar-core/src/semantic-marks/text.ts index 52da9de69..e24382d2b 100644 --- a/packages/vgrammar-core/src/semantic-marks/text.ts +++ b/packages/vgrammar-core/src/semantic-marks/text.ts @@ -12,6 +12,7 @@ export class Text extends Mark { addGraphicItem(initAttrs: any, groupKey?: string) { const textConfig = initAttrs?.text; const isRich = textConfig?.type === 'rich'; + const graphicItem = createGraphicItem( this as IMark, isRich ? GrammarMarkType.richtext : GrammarMarkType.text, diff --git a/packages/vgrammar-core/src/view/mark.ts b/packages/vgrammar-core/src/view/mark.ts index a4529c481..6eb3d2e03 100644 --- a/packages/vgrammar-core/src/view/mark.ts +++ b/packages/vgrammar-core/src/view/mark.ts @@ -388,9 +388,7 @@ export class Mark extends GrammarBase implements IMark { this._isReentered = true; } - if (!this.spec.encode[state]) { - this.spec.encode[state] = {}; - } else { + if (this.spec.encode[state]) { const lastEncoder = this.spec.encode[state]; // detach last dependencies if (isFunctionType(lastEncoder)) { @@ -414,21 +412,27 @@ export class Mark extends GrammarBase implements IMark { } } } - // update encode & append new dependencies - if (isString(channel)) { - this.spec.encode[state][channel] = value; - this.attach(parseEncodeType(value, this.view)); - } else if (isFunctionType(channel)) { - this.spec.encode[state] = channel; - this.attach(parseEncodeType(channel, this.view)); - } else { - Object.assign(this.spec.encode[state], channel); - if (channel) { + + if (channel) { + if (!this.spec.encode[state]) { + this.spec.encode[state] = {}; + } + + // update encode & append new dependencies + if (isString(channel)) { + this.spec.encode[state][channel] = value; + this.attach(parseEncodeType(value, this.view)); + } else if (isFunctionType(channel)) { + this.spec.encode[state] = channel; + this.attach(parseEncodeType(channel, this.view)); + } else if (channel) { + Object.assign(this.spec.encode[state], channel); Object.values(channel).forEach(channelEncoder => { this.attach(parseEncodeType(channelEncoder, this.view)); }); } } + this.commit(); return this; } @@ -629,9 +633,7 @@ export class Mark extends GrammarBase implements IMark { const group = getGrammarOutput(this.spec.group, parameters) as IGroupMark; this.group = group; if (group) { - this.emit(HOOK_EVENT.BEFORE_ADD_VRENDER_MARK); group.appendChild(this); - this.emit(HOOK_EVENT.AFTER_ADD_VRENDER_MARK); } } @@ -853,7 +855,6 @@ export class Mark extends GrammarBase implements IMark { return; } - this.emit(HOOK_EVENT.BEFORE_ADD_VRENDER_MARK); if (this.renderContext?.progressive) { let group: IGroup; @@ -876,8 +877,6 @@ export class Mark extends GrammarBase implements IMark { } else { (this.graphicParent as any).appendChild(graphicItem); } - this.emit(HOOK_EVENT.AFTER_ADD_VRENDER_MARK); - return graphicItem; }