diff --git a/packages/vchart-extension/src/charts/image-cloud/series/image-cloud.ts b/packages/vchart-extension/src/charts/image-cloud/series/image-cloud.ts index f735060db0..8f1c438b09 100644 --- a/packages/vchart-extension/src/charts/image-cloud/series/image-cloud.ts +++ b/packages/vchart-extension/src/charts/image-cloud/series/image-cloud.ts @@ -192,7 +192,12 @@ export class ImageCloudSeries extends BaseSerie if (maskImage && (this._spec.layoutConfig as GridLayoutConfig)?.placement === 'masked') { this._rootMark.getProduct().setAttribute('background', maskImage); } - }).bind(this) + }).bind(this), + onLayoutFinished: () => + this._option.globalInstance + .getChart() + .getOption() + .performanceHook?.afterWordcloudShapeDraw?.(this._option.globalInstance) }; } diff --git a/packages/vchart/__tests__/runtime/browser/test-page/debug.ts b/packages/vchart/__tests__/runtime/browser/test-page/debug.ts index 34662bdbb4..fe15a1a0f5 100644 --- a/packages/vchart/__tests__/runtime/browser/test-page/debug.ts +++ b/packages/vchart/__tests__/runtime/browser/test-page/debug.ts @@ -5,7 +5,18 @@ const run = async () => { const spec = { direction: 'vertical', type: 'common', - color: ['#00295C', '#2568BD', '#9F9F9F', '#C5C5C5', '#00B0F0', '#4BCFFF', '#C2C2C2', '#D7D7D7'], + color: [ + '#D1E6FB', + '#A3CDF7', + '#76B4F3', + '#489BEF', + '#1A82EB', + '#1568BC', + '#104E8D', + '#0A345E', + '#051A2F', + '#030D18' + ], series: [ { type: 'bar', @@ -63,7 +74,8 @@ const run = async () => { fontWeight: 'bold', pickMode: 'imprecise' }, - interactive: true + interactive: true, + alwayCalculateTotal: true }, seriesLabel: { visible: true, @@ -76,14 +88,17 @@ const run = async () => { fontSize: 16, fontWeight: 'bold' }, - space: 10 + space: 10, + formatConfig: { + content: 'series' + } } }, xField: '_editor_dimension_field', yField: '_editor_value_field', dataId: '0', id: 'series-0', - EDITOR_SERIES_DATA_KEY: 'a', + EDITOR_SERIES_DATA_KEY: '系列 1', seriesField: '_editor_type_field' }, { @@ -142,7 +157,8 @@ const run = async () => { fontWeight: 'bold', pickMode: 'imprecise' }, - interactive: true + interactive: true, + alwayCalculateTotal: true }, seriesLabel: { visible: true, @@ -155,14 +171,100 @@ const run = async () => { fontSize: 16, fontWeight: 'bold' }, - space: 10 + space: 10, + formatConfig: { + content: 'series' + } } }, xField: '_editor_dimension_field', yField: '_editor_value_field', dataId: '1', id: 'series-1', - EDITOR_SERIES_DATA_KEY: 'b', + EDITOR_SERIES_DATA_KEY: '系列 2', + seriesField: '_editor_type_field' + }, + { + type: 'bar', + stack: true, + direction: 'vertical', + bar: { + style: { + stroke: '', + lineWidth: 1 + }, + state: { + hover: { + stroke: '#000', + lineWidth: 1 + } + } + }, + barBackground: { + style: { + stroke: '', + lineWidth: 1 + } + }, + label: { + visible: true, + position: 'inside', + style: { + lineHeight: '100%', + fontSize: 16, + fontWeight: 'bold', + pickMode: 'imprecise' + }, + overlap: { + strategy: [] + }, + smartInvert: true, + formatConfig: {}, + interactive: true + }, + totalLabel: { + visible: true, + position: 'top', + overlap: false, + clampForce: false, + formatConfig: { + fixed: 0, + content: 'value' + }, + style: { + lineHeight: '100%', + lineWidth: 1, + fill: '#1F2329', + stroke: '#ffffff', + fontSize: 16, + fontWeight: 'bold', + pickMode: 'imprecise' + }, + interactive: true, + alwayCalculateTotal: true + }, + seriesLabel: { + visible: true, + position: 'end', + label: { + style: { + lineHeight: '100%', + lineWidth: 1, + stroke: '#ffffff', + fontSize: 16, + fontWeight: 'bold' + }, + space: 10, + formatConfig: { + content: 'series' + } + } + }, + xField: '_editor_dimension_field', + yField: '_editor_value_field', + dataId: '2', + id: 'series-2', + EDITOR_SERIES_DATA_KEY: '系列 3', seriesField: '_editor_type_field' } ], @@ -190,7 +292,7 @@ const run = async () => { } ], tooltip: { - visible: true, + visible: false, mark: { content: [{}], title: {} @@ -212,6 +314,7 @@ const run = async () => { type: 'linear', label: { autoLimit: false, + autoHide: true, style: { fill: '#1F2329', fontSize: 16, @@ -243,6 +346,7 @@ const run = async () => { } }, autoIndent: false, + sampling: true, maxWidth: null, maxHeight: null }, @@ -252,6 +356,7 @@ const run = async () => { type: 'band', label: { autoLimit: false, + autoHide: true, style: { fill: '#1F2329', fontSize: 16, @@ -284,6 +389,7 @@ const run = async () => { } }, autoIndent: false, + sampling: true, maxWidth: null, maxHeight: null, trimPadding: false, @@ -294,22 +400,22 @@ const run = async () => { data: [ { id: '0', - sourceKey: 'a', + sourceKey: '系列 1', values: [ { - _editor_value_field: 20, - _editor_type_field: 'a', - _editor_dimension_field: 'x1' + _editor_value_field: 10, + _editor_type_field: '系列 1', + _editor_dimension_field: '类别 A' }, { - _editor_value_field: 23, - _editor_type_field: 'a', - _editor_dimension_field: 'x2' + _editor_value_field: 24, + _editor_type_field: '系列 1', + _editor_dimension_field: '类别 B' }, { - _editor_value_field: 26, - _editor_type_field: 'a', - _editor_dimension_field: 'x3' + _editor_value_field: 40, + _editor_type_field: '系列 1', + _editor_dimension_field: '类别 C' } ], specField: { @@ -329,22 +435,57 @@ const run = async () => { }, { id: '1', - sourceKey: 'b', + sourceKey: '系列 2', + values: [ + { + _editor_value_field: 15, + _editor_type_field: '系列 2', + _editor_dimension_field: '类别 A' + }, + { + _editor_value_field: 25, + _editor_type_field: '系列 2', + _editor_dimension_field: '类别 B' + }, + { + _editor_value_field: 28, + _editor_type_field: '系列 2', + _editor_dimension_field: '类别 C' + } + ], + specField: { + _editor_dimension_field: { + type: 'dimension', + order: 0 + }, + _editor_type_field: { + type: 'series', + order: 0 + }, + _editor_value_field: { + type: 'value', + order: 0 + } + } + }, + { + id: '2', + sourceKey: '系列 3', values: [ { _editor_value_field: 20, - _editor_type_field: 'b', - _editor_dimension_field: 'x1' + _editor_type_field: '系列 3', + _editor_dimension_field: '类别 A' }, { - _editor_value_field: 24, - _editor_type_field: 'b', - _editor_dimension_field: 'x2' + _editor_value_field: 30, + _editor_type_field: '系列 3', + _editor_dimension_field: '类别 B' }, { - _editor_value_field: 29, - _editor_type_field: 'b', - _editor_dimension_field: 'x3' + _editor_value_field: 35, + _editor_type_field: '系列 3', + _editor_dimension_field: '类别 C' } ], specField: { @@ -363,80 +504,138 @@ const run = async () => { } } ], - markArea: [ + labelLayout: 'region', + markLine: [ { - id: '17bd0d32-3070-4679-bc39-b0ce3426add8', - name: 'v-area', - interactive: true, - x: '0%', - x1: '50%', - area: { - style: { - fill: '#005DFF', - fillOpacity: '0.1' + _editor_marker_format_: false, + _editor_marker_index: 0, + _editor_marker_label_: '+129%', + _originValue_: [45, 103], + connectDirection: 'top', + coordinates: [ + { + __VCHART_DEFAULT_DATA_INDEX: 0, + __VCHART_DEFAULT_DATA_KEY: 0, + __VCHART_STACK_END: 45, + __VCHART_STACK_END_PERCENT: 1, + __VCHART_STACK_KEY: '类别 A', + __VCHART_STACK_START: 35, + __VCHART_STACK_START_PERCENT: 0.7777777777777778, + __VCHART_STACK_TOTAL: 45, + __VCHART_STACK_TOTAL_PERCENT: 1, + __VCHART_STACK_TOTAL_TOP: true, + _editor_dimension_field: '类别 A', + _editor_type_field: '系列 1', + _editor_value_field: 45, + refRelativeSeriesId: 'series-0' + }, + { + __VCHART_DEFAULT_DATA_INDEX: 2, + __VCHART_DEFAULT_DATA_KEY: 2, + __VCHART_STACK_END: 103, + __VCHART_STACK_END_PERCENT: 1, + __VCHART_STACK_KEY: '类别 C', + __VCHART_STACK_START: 63, + __VCHART_STACK_START_PERCENT: 0.6116504854368932, + __VCHART_STACK_TOTAL: 103, + __VCHART_STACK_TOTAL_PERCENT: 1, + __VCHART_STACK_TOTAL_TOP: true, + _editor_dimension_field: '类别 C', + _editor_type_field: '系列 1', + _editor_value_field: 103, + refRelativeSeriesId: 'series-0' } - }, - label: [ + ], + coordinatesOffset: [ { - position: 'top', - text: 'x1 - x2', - labelBackground: { - visible: true, - style: { - fill: '#fff', - fillOpacity: 1, - stroke: '#000', - lineWidth: 1, - cornerRadius: 4 - } - }, - style: { - fill: '#1F2329', - fontSize: 16, - fontWeight: 'bold' - }, - dy: -6, - pickable: true, - childrenPickable: false + x: 0, + y: -26 }, { - position: 'left', - text: 'x1 - x2', - labelBackground: { - visible: true, - style: { - fill: '#fff', - fillOpacity: 1, - stroke: '#000', - lineWidth: 1, - cornerRadius: 4 - } + x: 0, + y: -26 + } + ], + endSymbol: { + originSymbolType: 'solidArrow', + refX: 0, + size: 8, + style: { + color: '#000', + fill: '#000', + fillOpacity: 1, + lineWidth: 0, + stroke: '#000' + }, + symbolType: 'M -1 1.5 L 0 0 L 1 1.5 Z', + visible: true + }, + expandDistance: 30, + id: 'c7457c72-48da-48ab-9a5c-c13e1d00999a', + interactive: true, + label: { + childrenPickable: false, + labelBackground: { + padding: { + bottom: 2, + left: 4, + right: 4, + top: 2 }, style: { - fill: '#1F2329', - fontSize: 16, - fontWeight: 'bold' - }, - dx: -6, - pickable: true, - childrenPickable: false + cornerRadius: 4, + fill: '#fff', + fillOpacity: 1, + lineWidth: 1, + stroke: '#000' + } + }, + pickable: true, + position: 'middle', + refX: 0, + refY: 0, + style: { + fill: '#1F2329', + fontSize: 16, + fontStyle: 'normal', + fontWeight: 'bold' + }, + text: ['+129%'] + }, + line: { + style: { + cornerRadius: 6, + lineDash: [0], + lineWidth: 1, + pickStrokeBuffer: 10, + stroke: '#000' } - ], - _originValue_: ['x1', 'x2'], - _editor_marker_label_: 'x1 - x2', - zIndex: 500 + }, + name: 'total-diff-line', + startSymbol: { + size: 10, + style: { + fill: '#606773', + lineWidth: 0, + stroke: null + }, + symbolType: 'triangle', + visible: false + }, + type: 'type-step', + zIndex: 510 } ], - markLine: [], - markPoint: [], - labelLayout: 'region', + totalLabel: { + alwayCalculateTotal: true + }, customMark: [ { type: 'component', componentType: 'seriesLabel', interactive: false, style: { - id: '13643b25-e719-4de6-874c-a4587015f1aa', + id: '866ab6c8-0beb-4a50-a253-6989e1b017e7', position: 'end', label: { space: 10, @@ -446,14 +645,36 @@ const run = async () => { stroke: '#ffffff', fontSize: 16, fontWeight: 'bold' + }, + formatConfig: { + content: 'series' } } } } - ] + ], + padding: 0, + animation: false, + markArea: [], + animationAppear: false }; - const vchart = new VChart(spec, { dom: CONTAINER_ID }); + + const vchart = new VChart(spec, { + dom: CONTAINER_ID, + performanceHook: { + afterVRenderDraw: () => { + console.log('------'); + } + }, + renderHooks: { + afterRender: () => { + console.log('afterRender'); + } + } + }); vchart.renderSync(); + + // Just for the convenience of console debugging, DO NOT COPY! window['vchart'] = vchart; }; run(); diff --git a/packages/vchart/src/compile/compiler.ts b/packages/vchart/src/compile/compiler.ts index ec51be9e31..e5599642d4 100644 --- a/packages/vchart/src/compile/compiler.ts +++ b/packages/vchart/src/compile/compiler.ts @@ -308,6 +308,7 @@ export class Compiler implements ICompiler { vchart: this._compileChart.getOption()?.globalInstance }); } + this._option.performanceHook?.afterVRenderDraw?.(this._compileChart.getOption().globalInstance); }; private _doRender(immediately: boolean) { @@ -329,12 +330,15 @@ export class Compiler implements ICompiler { ); }); + this._option.performanceHook?.beforeVRenderDraw?.(this._compileChart.getOption().globalInstance); // 全量渲染的时候先关闭dirty bounds 提升性能 this._stage.disableDirtyBounds(); this._stage.afterNextRender(this._handleAfterNextRender); if (immediately) { this._stage.render(); + + this._option.performanceHook?.afterVRenderDraw?.(this._compileChart.getOption().globalInstance); } } } diff --git a/packages/vchart/src/event/event-dispatcher.ts b/packages/vchart/src/event/event-dispatcher.ts index 0d7c18adc5..d0bd20dd7d 100644 --- a/packages/vchart/src/event/event-dispatcher.ts +++ b/packages/vchart/src/event/event-dispatcher.ts @@ -279,23 +279,6 @@ export class EventDispatcher implements IEventDispatcher { return true; } - private _prepareParams( - filter: EventFilter, - params: EventParamsDefinition[Evt] - ): EventParamsDefinition[Evt] { - // 如果针对于 mark 做了筛选,则事件参数转为筛选器制定的父级 mark - if (filter.markName && params.mark) { - const markGraphic = params.mark.getGraphics?.()?.[0]; - - return { - ...params, - item: markGraphic, - datum: getDatumOfGraphic(markGraphic) - }; - } - return { ...params }; - } - /** * 代理语法层事件的监听回调 */ @@ -360,7 +343,7 @@ export class EventDispatcher implements IEventDispatcher { const filter = handler.filter as EventFilter; if (!handler.prevented && (!handler.query || this._filter(filter, type, params))) { const callback = handler.wrappedCallback || handler.callback; - const stopBubble = callback.call(null, this._prepareParams(filter, params)); + const stopBubble = callback.call(null, { ...params }); const doStopBubble = stopBubble ?? handler.query?.consume; if (doStopBubble) { (params as BaseEventParams).event?.stopPropagation(); diff --git a/packages/vchart/src/series/word-cloud/base.ts b/packages/vchart/src/series/word-cloud/base.ts index 7129e1389c..5ecf9af519 100644 --- a/packages/vchart/src/series/word-cloud/base.ts +++ b/packages/vchart/src/series/word-cloud/base.ts @@ -383,6 +383,11 @@ export class BaseWordCloudSeries + this._option.globalInstance + .getChart() + .getOption() + .performanceHook?.afterWordcloudShapeDraw?.(this._option.globalInstance), dataIndexKey: DEFAULT_DATA_KEY, text: wordSpec.formatMethod ? (datum: Datum) => { diff --git a/packages/vchart/src/typings/spec/common.ts b/packages/vchart/src/typings/spec/common.ts index 2f1761fb57..b9eaddd4ce 100644 --- a/packages/vchart/src/typings/spec/common.ts +++ b/packages/vchart/src/typings/spec/common.ts @@ -702,6 +702,9 @@ export interface IPerformanceHook { // VRender Draw 时间 beforeVRenderDraw?: (vchart?: IVChart) => void; afterVRenderDraw?: (vchart?: IVChart) => void; + + // 词云 + afterWordcloudShapeDraw?: (vchart?: IVChart) => void; } export type IBuildinMarkSpec = { diff --git a/packages/vchart/src/util/style.ts b/packages/vchart/src/util/style.ts index c66dac0bb6..5f90fe88e9 100644 --- a/packages/vchart/src/util/style.ts +++ b/packages/vchart/src/util/style.ts @@ -88,7 +88,10 @@ export function transformIndicatorStyle(style: any, datum: any) { export function transformToGraphic(style: any) { if (style?.angle) { - style.angle = degreeToRadian(style.angle); + return { + ...style, + angle: degreeToRadian(style.angle) + }; } return style;