From 9bfdab6479695f166b2a3d6387583b97a1884c5d Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Tue, 2 Jul 2024 10:45:46 +0800 Subject: [PATCH 1/7] feat: richtext edit action --- packages/vstory/src/story/character/chart/character.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/vstory/src/story/character/chart/character.ts b/packages/vstory/src/story/character/chart/character.ts index 2d7da1b..cdbd8d9 100644 --- a/packages/vstory/src/story/character/chart/character.ts +++ b/packages/vstory/src/story/character/chart/character.ts @@ -121,9 +121,10 @@ export class CharacterChart extends CharacterVisactor { if (!(event.detailPath ?? event.path).some(g => g === this._graphic)) { return false; } - if (!this._graphic.pointInVChart((event as any).canvasX, (event as any).canvasY)) { - return false; - } + // 超出vchart viewBox 外的图表元素,依然会绘制并且能被选中 + // if (!this._graphic.pointInVChart((event as any).canvasX, (event as any).canvasY)) { + // return false; + // } const chartPath = event.detailPath[event.detailPath.length - 1]; return { part: chartPath?.[chartPath.length - 1]?.type, From c2a52f0c7bac3b74d0950807d35625418471e4e5 Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Tue, 16 Jul 2024 17:49:49 +0800 Subject: [PATCH 2/7] feat: optimization vchart graphic --- packages/vstory/demo/src/App.tsx | 5 + packages/vstory/demo/src/demos/StoryEdit.tsx | 2 +- .../vstory/demo/src/demos/VChartGraphic.tsx | 205 ++++++++++++++++++ packages/vstory/src/edit/const.ts | 2 +- packages/vstory/src/edit/edit-action.ts | 4 +- .../src/edit/edit-component/base-selection.ts | 2 +- .../vstory/src/edit/edit-component/index.ts | 2 +- packages/vstory/src/edit/edit.ts | 1 - .../src/story/character/chart/character.ts | 48 ++-- .../character/chart/graphic/vchart-graphic.ts | 42 +++- .../character/chart/runtime/common-spec.ts | 9 +- .../character/chart/runtime/component-spec.ts | 58 +++-- .../character/chart/temp/templates/bar.ts | 26 +++ .../chart/temp/templates/base-temp.ts | 8 +- .../story/character/component/character.ts | 4 +- .../src/story/character/visactor/interface.ts | 2 +- 16 files changed, 366 insertions(+), 54 deletions(-) create mode 100644 packages/vstory/demo/src/demos/VChartGraphic.tsx create mode 100644 packages/vstory/src/story/character/chart/temp/templates/bar.ts diff --git a/packages/vstory/demo/src/App.tsx b/packages/vstory/demo/src/App.tsx index f12daf1..92d7acb 100644 --- a/packages/vstory/demo/src/App.tsx +++ b/packages/vstory/demo/src/App.tsx @@ -18,6 +18,7 @@ import { Pictogram } from './demos/infographics/Pictogram'; import { LV_BAR1 } from './demos/lv/bar1'; import { BarLineSeries } from './demos/BarLineSeries'; import { wordcloud } from './demos/wordcloud'; +import { VChartGraphic } from './demos/VChartGraphic'; import { BaseComponent } from './demos/BaseComponent'; import { BarLineSeriesSelector } from './demos/BarLineSeriesSelector'; import { RankingBar } from './demos/template/RankingBar'; @@ -121,6 +122,10 @@ const App = () => { { name: 'RankingBar', component: RankingBar + }, + { + name: 'VChartGraphic', + component: VChartGraphic } ]; const getSelectedMenu = useCallback<(menus: MenusType) => any>( diff --git a/packages/vstory/demo/src/demos/StoryEdit.tsx b/packages/vstory/demo/src/demos/StoryEdit.tsx index f4f07e4..00ab751 100644 --- a/packages/vstory/demo/src/demos/StoryEdit.tsx +++ b/packages/vstory/demo/src/demos/StoryEdit.tsx @@ -1,11 +1,11 @@ import React, { useEffect } from 'react'; import { IStorySpec } from '../../../src/story/interface'; import { Story } from '../../../src/story/story'; -import { Edit } from '../../../src/edit/edit'; import '../../../src/story/index'; import { cloneDeep } from '@visactor/vutils'; import Scene3ChartImage2 from '../assets/scene3/chart-2.png'; import { loadAllSelection } from '../../../src/edit/edit-component'; +import { Edit } from '../../../src/edit/edit'; // Edit.registerEditComponent('common', CommonEditComponent); loadAllSelection(); diff --git a/packages/vstory/demo/src/demos/VChartGraphic.tsx b/packages/vstory/demo/src/demos/VChartGraphic.tsx new file mode 100644 index 0000000..8d699d4 --- /dev/null +++ b/packages/vstory/demo/src/demos/VChartGraphic.tsx @@ -0,0 +1,205 @@ +import React, { useEffect } from 'react'; +import { IStorySpec } from '../../../src/story/interface'; +import { Story } from '../../../src/story/story'; +import '../../../src/story/index'; +import { Edit } from '../../../src/edit/edit'; +import { CommonEditComponent } from '../../../src/edit/edit-component/common'; +import { BoxSelection } from '../../../src/edit/edit-component/box-selection'; +import { TextSelection } from '../../../src/edit/edit-component/text-selection'; +import { RichTextSelection } from '../../../src/edit/edit-component/richtext-selection'; + +Edit.registerEditComponent('common', CommonEditComponent); +Edit.registerEditComponent('text', TextSelection); +Edit.registerEditComponent('richtext', RichTextSelection); +Edit.registerEditComponent('box-selection', BoxSelection); + +export const VChartGraphic = () => { + const id = 'storyBar'; + + const chartData = [ + { year: '2017', value: 129 }, + { year: '2018', value: 150 }, + { year: '2019', value: 130 }, + { year: '2020', value: 126 }, + { year: '2021', value: 117 }, + { year: '2022', value: 180 }, + { year: 'Target', value: 200 } + ]; + + useEffect(() => { + // 准备一个图表 + const tempSpec: IStorySpec = { + characters: [ + { + type: 'RectComponent', + id: 'test-graphics-0', + zIndex: 10, + position: { + top: 40, + left: 50, + width: 250, + height: 100 + }, + options: { + graphic: { + fill: 'red' + }, + text: { + text: 'haha', + fill: 'black' + }, + angle: 0, + shapePoints: [] + } + }, + { + type: 'BarChart', + id: 'test-chart-0', + zIndex: 9, + position: { + top: 200, + left: 100, + width: 400, + height: 400 + }, + options: { + title: { + text: 'Timeline Chart', + orient: 'bottom', + align: 'center', + textStyle: { + fontSize: 10, + lineHeight: 10 + } + }, + padding: 12, + data: [ + { + id: 'id0', + values: chartData + } + ], + direction: 'vertical', + seriesSpec: [ + { + matchInfo: { specIndex: 'all' }, + spec: { + type: 'bar', + xField: 'year', + yField: 'value' + } + } + ], + componentSpec: [ + { + specKey: 'axes', + matchInfo: { orient: 'left' }, + spec: { + label: { + style: { + fontSize: 20 + } + } + } + }, + { + specKey: 'axes', + matchInfo: { orient: 'bottom' }, + spec: { + type: 'band' + } + }, + { + specKey: 'markLine', + matchInfo: { orient: 'left' }, + spec: { + coordinates: [chartData[0], chartData[5]], + line: { + style: { + lineDash: [0], + lineWidth: 2, + stroke: '#000' + } + }, + label: { + position: 'middle', + text: `asdadsasd% CARG`, + labelBackground: { + padding: 8, + style: { + fill: '#fff', + fillOpacity: 1, + stroke: '#3CC780', + lineWidth: 2, + cornerRadius: 8 + } + }, + style: { + fill: '#3CC780' + } + }, + endSymbol: { + size: 12, + refX: -4 + }, + offsetY: -100 + } + } + ] + } + } + ], + acts: [ + { + id: 'default-chapter', + scenes: [ + { + id: 'scene0', + actions: [ + { + characterId: 'test-graphics-0', + characterActions: [ + { + startTime: 0, + duration: 0, + action: 'appear', + payload: { + style: {}, + animation: { + duration: 0, + easing: 'linear', + effect: 'fadeIn' + } as any + } + } + ] + }, + { + characterId: 'test-chart-0', + characterActions: [ + { + startTime: 0, + duration: 0, + action: 'appear' + } + ] + } + ] + } + ] + } + ] + }; + const story = new Story(tempSpec, { dom: id }); + story.play(); + const edit = new Edit(story); + edit.emitter.on('startEdit', msg => { + if (msg.type === 'commonEdit' && msg.actionInfo.character) { + msg.updateCharacter({ options: { graphic: { fill: 'green' } } }); + story.play(); + } + }); + }, []); + + return
; +}; diff --git a/packages/vstory/src/edit/const.ts b/packages/vstory/src/edit/const.ts index 8d24e1d..89b2e45 100644 --- a/packages/vstory/src/edit/const.ts +++ b/packages/vstory/src/edit/const.ts @@ -1,4 +1,4 @@ -export const PickEventType = { +export const PickEventType: { [key: string]: boolean } = { pointerup: true, click: true, pointerdown: true, diff --git a/packages/vstory/src/edit/edit-action.ts b/packages/vstory/src/edit/edit-action.ts index e0f183c..b13e903 100644 --- a/packages/vstory/src/edit/edit-action.ts +++ b/packages/vstory/src/edit/edit-action.ts @@ -9,13 +9,13 @@ import type { ContinuousActionType, IEditActionInfo } from './interface'; import { EditActionEnum } from './interface'; import type { StoryEvent } from '../story/interface/runtime-interface'; -const IgnoreEvent = { +const IgnoreEvent: { [key: string]: boolean } = { mousemove: true, mouseout: true, mouseover: true }; -const PointerOverEvent = { +const PointerOverEvent: { [key: string]: boolean } = { pointerover: true, pointerout: true, pointermove: true diff --git a/packages/vstory/src/edit/edit-component/base-selection.ts b/packages/vstory/src/edit/edit-component/base-selection.ts index fdaebaf..e8bc0a1 100644 --- a/packages/vstory/src/edit/edit-component/base-selection.ts +++ b/packages/vstory/src/edit/edit-component/base-selection.ts @@ -168,7 +168,7 @@ export abstract class BaseSelection implements IEditComponent { protected handlerTransformChange(data: IUpdateParams, event?: VRenderPointerEvent): void { if (this._activeCharacter) { - this._activeCharacter.setAttributes(data); + this._activeCharacter.setAttributes({ position: data }); } } } diff --git a/packages/vstory/src/edit/edit-component/index.ts b/packages/vstory/src/edit/edit-component/index.ts index 76e015d..4bacb7a 100644 --- a/packages/vstory/src/edit/edit-component/index.ts +++ b/packages/vstory/src/edit/edit-component/index.ts @@ -4,8 +4,8 @@ import { ImageSelection } from './image-selection'; import { TextSelection } from './text-selection'; import { RichTextSelection } from './richtext-selection'; import { RectSelection } from './rect-selection'; -import { Edit } from '../edit'; import { ChartSelection } from './chart-selection'; +import { Edit } from '../edit'; import { ShapeSelection } from './shape-selection'; export function loadAllSelection() { diff --git a/packages/vstory/src/edit/edit.ts b/packages/vstory/src/edit/edit.ts index fb0f6b1..a5a9cb5 100644 --- a/packages/vstory/src/edit/edit.ts +++ b/packages/vstory/src/edit/edit.ts @@ -65,7 +65,6 @@ export class Edit { } onStoryEvent(event: StoryEvent, type: string) { - // const pathTarget = event.path?.[event.path?.length - 1]; // 如果交互到编辑元素忽略 if (event.path.find(g => g === this._editGroup || g === this._overGraphicGroup)) { // 具体判断是否编辑到交互元素,如果pick到group,就不算 diff --git a/packages/vstory/src/story/character/chart/character.ts b/packages/vstory/src/story/character/chart/character.ts index cdbd8d9..f670152 100644 --- a/packages/vstory/src/story/character/chart/character.ts +++ b/packages/vstory/src/story/character/chart/character.ts @@ -1,8 +1,8 @@ import { CommonSpecRuntime } from './runtime/common-spec'; import { ComponentSpecRuntime } from './runtime/component-spec'; import type { IChartCharacterRuntimeConstructor } from './runtime/interface'; -import { cloneDeep } from '@visactor/vutils'; -import VChart from '@visactor/vchart'; +import { cloneDeep, isValid } from '@visactor/vutils'; +import { VChart } from '@visactor/vchart'; import type { IChartCharacterSpec } from '../dsl-interface'; import { Chart } from './graphic/vchart-graphic'; import { getLayoutFromWidget } from '../../utils/layout'; @@ -49,21 +49,11 @@ export class CharacterChart extends CharacterVisactor { this._specProcess.updateConfig(this._spec); } protected _initGraphics(): void { - // this._ticker = new ManualTicker([]); - const layout = getLayoutFromWidget(this._spec.position); - const viewBox = { - x1: layout.x, - x2: layout.x + layout.width, - y1: layout.y, - y2: layout.y + layout.height - }; - const spec = cloneDeep(this._specProcess.getVisSpec()); - spec.width = layout.width; - spec.height = layout.height; + const { spec, viewBox } = this._getChartOption(); // @ts-ignore this._graphic = new Chart({ renderCanvas: this._option.canvas.getCanvas(), - spec: spec, + spec, ClassType: VChart, vchart: null, zIndex: this._spec.zIndex, @@ -98,6 +88,27 @@ export class CharacterChart extends CharacterVisactor { this._graphic.setAttributes(attr); // this._text.updateAttribute({}); } + getViewBoxFromSpec() { + const layout = getLayoutFromWidget(this._spec.position); + const viewBox = { + x1: layout.x, + x2: layout.x + layout.width, + y1: layout.y, + y2: layout.y + layout.height + }; + return { layout, viewBox }; + } + + private _getChartOption() { + const { layout, viewBox } = this.getViewBoxFromSpec(); + const spec = cloneDeep(this._specProcess.getVisSpec() ?? this._spec.options.spec); + spec.width = layout.width; + spec.height = layout.height; + return { + viewBox, + spec + }; + } protected _afterRender(): void { // console.log('afterRender'); @@ -136,4 +147,13 @@ export class CharacterChart extends CharacterVisactor { this.option.graphicParent.removeChild(this._graphic as any); this._graphic.release && this._graphic.release(); } + + private _reflow() { + if (!this._graphic) { + this._initGraphics(); + return; + } + const { spec } = this._getChartOption(); + this._graphic.updateSpec(spec); + } } diff --git a/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts b/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts index 7efeeab..50b6775 100644 --- a/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts +++ b/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts @@ -1,5 +1,5 @@ import type { IVisactorGraphic } from '../../visactor/interface'; -import type { IBoundsLike } from '@visactor/vutils'; +import type { AABBBounds, IBoundsLike } from '@visactor/vutils'; import type { ISpec, IVChart } from '@visactor/vchart'; import type { GraphicType, IGroupGraphicAttribute, ITicker } from '@visactor/vrender'; import { genNumberType, Group } from '@visactor/vrender'; @@ -32,6 +32,8 @@ export class Chart extends Group implements IVisactorGraphic { protected _vchart: IVChart; // 是否试一次空render,目的是只生成场景树,不会真实渲染 // protected _emptyRenderCall: boolean; + protected declare _AABBBounds: AABBBounds; + declare valid: boolean; get vchart() { return this._vchart; } @@ -40,6 +42,43 @@ export class Chart extends Group implements IVisactorGraphic { } drawTag = false; + protected _boundsChangeTag: boolean = true; + + doUpdateAABBBounds(full?: boolean): AABBBounds { + if (!this._vchart) { + return super.doUpdateAABBBounds(); + } + const stage = this._vchart.getStage(); + // console.log( + // 'vchart graphic doUpdateAABBBounds', + // stage.defaultLayer.getChildByName('root').AABBBounds.clone().translate(this.attribute.x, this.attribute.y) + // ); + return stage.defaultLayer.getChildByName('root').AABBBounds.clone().translate(this.attribute.x, this.attribute.y); + } + + shouldUpdateAABBBounds(): boolean { + if (super.shouldUpdateAABBBounds()) { + return true; + } + if (this._boundsChangeTag) { + this._boundsChangeTag = false; + return true; + } + return false; + } + + protected tryUpdateAABBBounds(full?: boolean): AABBBounds { + if (!this.shouldUpdateAABBBounds()) { + return this._AABBBounds; + } + if (!this.valid) { + this._AABBBounds.clear(); + return this._AABBBounds; + } + + const bounds = this.doUpdateAABBBounds(full); + return bounds; + } constructor(params: IChartGraphicAttribute) { super(params); @@ -153,6 +192,7 @@ export class Chart extends Group implements IVisactorGraphic { } private _updateViewBox(_viewBox: IBoundsLike) { + this._boundsChangeTag = true; const viewBox = { ..._viewBox }; // viewBox.x2 -= viewBox.x1; diff --git a/packages/vstory/src/story/character/chart/runtime/common-spec.ts b/packages/vstory/src/story/character/chart/runtime/common-spec.ts index 3889fc6..932567b 100644 --- a/packages/vstory/src/story/character/chart/runtime/common-spec.ts +++ b/packages/vstory/src/story/character/chart/runtime/common-spec.ts @@ -1,6 +1,6 @@ import { merge } from '@visactor/vutils'; -import { CharacterChart } from '../character'; -import { IChartCharacterRuntime } from './interface'; +import type { CharacterChart } from '../character'; +import type { IChartCharacterRuntime } from './interface'; export class CommonSpecRuntime implements IChartCharacterRuntime { type = 'CommonSpec'; @@ -19,9 +19,4 @@ export class CommonSpecRuntime implements IChartCharacterRuntime { } merge(rawSpec, { color: options.color, theme: options.theme, padding: options.padding, title: options.title }); } - - afterInitializeChart() { - // - } - afterVRenderDraw() {} } diff --git a/packages/vstory/src/story/character/chart/runtime/component-spec.ts b/packages/vstory/src/story/character/chart/runtime/component-spec.ts index e9517cd..2ccc94f 100644 --- a/packages/vstory/src/story/character/chart/runtime/component-spec.ts +++ b/packages/vstory/src/story/character/chart/runtime/component-spec.ts @@ -1,7 +1,7 @@ import { merge, isValid } from '@visactor/vutils'; -import { IComponentSpec } from '../../dsl-interface'; -import { CharacterChart } from '../character'; -import { IChartCharacterRuntime } from './interface'; +import type { IComponentSpec } from '../../dsl-interface'; +import type { CharacterChart } from '../character'; +import type { IChartCharacterRuntime } from './interface'; import { ChartSpecMatch } from './utils'; export class ComponentSpecRuntime implements IChartCharacterRuntime { @@ -23,32 +23,52 @@ export class ComponentSpecRuntime implements IChartCharacterRuntime { componentSpec?.forEach(cSpec => { if (cSpec.specKey === 'axes') { this._mergeAxesSpec(rawSpec, cSpec); + } else { + this._mergeComponentSpec(rawSpec, cSpec, cSpec.specKey); } }); } protected _mergeAxesSpec(rawSpec: any, componentSpec: IComponentSpec) { - if (!rawSpec.axes) { - rawSpec.axes = [{ ...componentSpec }]; - return; - } else { - const s = rawSpec.axes.find((a: any, index: number) => { - if (ChartSpecMatch(a, index, componentSpec.matchInfo)) { - return true; - } else { - return a.orient === componentSpec.matchInfo.orient; - } - }); - if (s) { - merge(s, componentSpec.spec); - } else { - rawSpec.axes.push(componentSpec.spec); + this._mergeComponentSpec( + rawSpec, + componentSpec, + 'axes', + (a: any, index: number, _componentSpec: IComponentSpec) => { + return a.orient === componentSpec.matchInfo.orient; } + ); + } + + protected _mergeComponentSpec( + rawSpec: any, + componentSpec: IComponentSpec, + key: string, + additionalMatch?: (rawComponentSpec: any, index: number, componentSpec: IComponentSpec) => boolean + ) { + if (!rawSpec[key]) { + rawSpec[key] = []; + } + const s = rawSpec[key].find((a: any, index: number) => { + if (ChartSpecMatch(a, index, componentSpec.matchInfo)) { + return true; + } + if (additionalMatch) { + return additionalMatch(a, index, componentSpec); + } + return false; + }); + if (s) { + merge(s, componentSpec.spec); + } else { + rawSpec[key].push(componentSpec.spec); } } afterInitializeChart() { // } - afterVRenderDraw() {} + afterVRenderDraw() { + // + } } diff --git a/packages/vstory/src/story/character/chart/temp/templates/bar.ts b/packages/vstory/src/story/character/chart/temp/templates/bar.ts new file mode 100644 index 0000000..a2b6627 --- /dev/null +++ b/packages/vstory/src/story/character/chart/temp/templates/bar.ts @@ -0,0 +1,26 @@ +import { ICharacter } from './../../../runtime-interface'; +import { TemplateChartType } from '../constant'; +import { CartesianSingleSeriesTemp } from './cartesian-single'; +import type { CharacterChart } from '../../character'; + +export class BarTemp extends CartesianSingleSeriesTemp { + static type: string = TemplateChartType.bar; + type: string = BarTemp.type; + // 唯一系列类型 + seriesType = 'bar'; + // 默认是否展示总计标签 + defaultTotalLabel = true; + + protected _getSeriesSpec() { + return { + type: 'bar', + stack: true, + direction: this.direction + }; + } + + afterInitializeChart(ctx: { character: CharacterChart }): void { + // eslint-disable-next-line no-console + console.log('afterInitializeChart'); + } +} diff --git a/packages/vstory/src/story/character/chart/temp/templates/base-temp.ts b/packages/vstory/src/story/character/chart/temp/templates/base-temp.ts index c4092c1..b7878d4 100644 --- a/packages/vstory/src/story/character/chart/temp/templates/base-temp.ts +++ b/packages/vstory/src/story/character/chart/temp/templates/base-temp.ts @@ -1,6 +1,6 @@ -import { IChartTemp } from '../interface'; -import { DataInfo, StandardData } from '../../data/interface'; -import { CharacterChart } from '../../character'; +import type { IChartTemp } from '../interface'; +import type { DataInfo, StandardData } from '../../data/interface'; +import type { CharacterChart } from '../../character'; export const EDITOR_SERIES_MARK_SINGLE = '_editor_series_mark_single'; export const EDITOR_SERIES_MARK_SINGLE_LEVEL = 100; @@ -17,7 +17,7 @@ export abstract class BaseTemp implements IChartTemp { return this.type; } - afterInitializeChart() { + afterInitializeChart(ctx: { character: CharacterChart }) { // do nothing } diff --git a/packages/vstory/src/story/character/component/character.ts b/packages/vstory/src/story/character/component/character.ts index 9910ccd..59f70ef 100644 --- a/packages/vstory/src/story/character/component/character.ts +++ b/packages/vstory/src/story/character/component/character.ts @@ -60,7 +60,9 @@ export abstract class CharacterComponent extends CharacterBase { this.hide(); } - setAttributes(attr: Record): void { + setAttributes(updateAttr: Record): void { + const { position, ...rest } = updateAttr; + const attr = { ...(position ?? {}), ...rest }; this.group.setAttributes(attr); this._graphic.setAttributes({ ...attr, x: 0, y: 0, angle: 0 }); this._text.updateAttribute({}); diff --git a/packages/vstory/src/story/character/visactor/interface.ts b/packages/vstory/src/story/character/visactor/interface.ts index 8cf0b43..db455fa 100644 --- a/packages/vstory/src/story/character/visactor/interface.ts +++ b/packages/vstory/src/story/character/visactor/interface.ts @@ -1,5 +1,5 @@ import type { IChartCharacterSpec } from '../dsl-interface'; -import type { EventEmitter } from '@visactor/vutils'; +import type { EventEmitter, IBoundsLike } from '@visactor/vutils'; import type { ICharacter } from '../runtime-interface'; import type { ISpec, IVChart } from '@visactor/vchart'; import type { IGroup } from '@visactor/vrender'; From 09462715abcf604be1a98e3e07db4ca8c870934d Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Mon, 22 Jul 2024 15:39:28 +0800 Subject: [PATCH 3/7] feat: optimization vchart graphic --- packages/vstory/src/story/canvas/canvas.ts | 4 -- .../vstory/src/story/character/base/base.ts | 2 + .../src/story/character/chart/character.ts | 2 +- .../chart/graphic/vchart-graphic-render.ts | 3 +- .../character/chart/graphic/vchart-graphic.ts | 53 ++++++++++++++----- .../story/character/component/character.ts | 4 ++ .../src/story/character/runtime-interface.ts | 5 +- .../src/story/character/visactor/character.ts | 19 ++++++- 8 files changed, 70 insertions(+), 22 deletions(-) diff --git a/packages/vstory/src/story/canvas/canvas.ts b/packages/vstory/src/story/canvas/canvas.ts index 804017e..19ded92 100644 --- a/packages/vstory/src/story/canvas/canvas.ts +++ b/packages/vstory/src/story/canvas/canvas.ts @@ -59,10 +59,6 @@ export class StoryCanvas implements IStoryCanvas { }); // @ts-ignore this._stage = stage; - // stage.on('*', (e, type) => { - // // eslint-disable-next-line no-console - // type === 'click' && console.log('canvas', e); - // }); } getEventDetail(event: StoryEvent) { diff --git a/packages/vstory/src/story/character/base/base.ts b/packages/vstory/src/story/character/base/base.ts index f8335e4..61d6655 100644 --- a/packages/vstory/src/story/character/base/base.ts +++ b/packages/vstory/src/story/character/base/base.ts @@ -1,3 +1,4 @@ +import type { IBoundsLike } from '@visactor/vutils'; import { isValid, merge } from '@visactor/vutils'; import type { ICharacterInitOption, ICharacterPickInfo } from '../runtime-interface'; import type { ICharacter, ICharacterSpec } from '..'; @@ -72,6 +73,7 @@ export abstract class CharacterBase implements ICharacter { } abstract getGraphicParent(): IGroup; + abstract getLayoutBounds(): IBoundsLike; abstract clearCharacter(): void; diff --git a/packages/vstory/src/story/character/chart/character.ts b/packages/vstory/src/story/character/chart/character.ts index f670152..0d849fd 100644 --- a/packages/vstory/src/story/character/chart/character.ts +++ b/packages/vstory/src/story/character/chart/character.ts @@ -5,7 +5,6 @@ import { cloneDeep, isValid } from '@visactor/vutils'; import { VChart } from '@visactor/vchart'; import type { IChartCharacterSpec } from '../dsl-interface'; import { Chart } from './graphic/vchart-graphic'; -import { getLayoutFromWidget } from '../../utils/layout'; import { CharacterVisactor } from '../visactor/character'; import { SpecProcess } from './spec-process/spec-process'; import { ChartDataTempTransform } from './spec-process/data-temp-transform'; @@ -14,6 +13,7 @@ import type { IChartTemp } from './temp/interface'; import { SeriesSpecRuntime } from './runtime/series-spec'; import type { StoryEvent } from '../../interface/runtime-interface'; import type { ICharacterPickInfo } from '../runtime-interface'; +import { getLayoutFromWidget } from '../../utils/layout'; export class CharacterChart extends CharacterVisactor { static type = 'CharacterChart'; diff --git a/packages/vstory/src/story/character/chart/graphic/vchart-graphic-render.ts b/packages/vstory/src/story/character/chart/graphic/vchart-graphic-render.ts index 37186b9..5b07b81 100644 --- a/packages/vstory/src/story/character/chart/graphic/vchart-graphic-render.ts +++ b/packages/vstory/src/story/character/chart/graphic/vchart-graphic-render.ts @@ -6,8 +6,7 @@ import type { IGraphicRenderDrawParams, IMarkAttribute, IRenderService, - IThemeAttribute, - IGraphic + IThemeAttribute } from '@visactor/vrender'; import { injectable, DefaultCanvasRectRender } from '@visactor/vrender'; import type { Chart } from './vchart-graphic'; diff --git a/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts b/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts index 50b6775..b8133ed 100644 --- a/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts +++ b/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts @@ -41,19 +41,23 @@ export class Chart extends Group implements IVisactorGraphic { return this._vchart; } + private _chartViewBox: IBoundsLike = { x1: 0, y1: 0, x2: 100, y2: 100 }; + private _BoundsViewBox: IBoundsLike = { x1: 0, y1: 0, x2: 100, y2: 100 }; + private _offset: { x: number; y: number } = { x: 0, y: 0 }; + drawTag = false; protected _boundsChangeTag: boolean = true; + private _getVChartRootMarkBounds() { + const stage = this._vchart.getStage(); + return stage.defaultLayer.getChildByName('root').AABBBounds.clone(); + } + doUpdateAABBBounds(full?: boolean): AABBBounds { if (!this._vchart) { return super.doUpdateAABBBounds(); } - const stage = this._vchart.getStage(); - // console.log( - // 'vchart graphic doUpdateAABBBounds', - // stage.defaultLayer.getChildByName('root').AABBBounds.clone().translate(this.attribute.x, this.attribute.y) - // ); - return stage.defaultLayer.getChildByName('root').AABBBounds.clone().translate(this.attribute.x, this.attribute.y); + return this._getVChartRootMarkBounds().translate(this.attribute.x, this.attribute.y); } shouldUpdateAABBBounds(): boolean { @@ -183,17 +187,35 @@ export class Chart extends Group implements IVisactorGraphic { }); } - updateSpec(spec: ISpec, forceMerge = false, morphConfig = false) { - this._vchart.updateSpecSync(spec, forceMerge, morphConfig as any); + updateSpec(spec: ISpec, viewBox?: IBoundsLike, forceMerge = false, morphConfig = false) { + this._boundsChangeTag = true; + viewBox && this.updateViewBox(viewBox); + this._vchart.updateSpecSync(spec, forceMerge, { reuse: false, morph: morphConfig }); + this._updateViewBox(); } - protected updateViewBox(viewBox: IBoundsLike) { - this._updateViewBox(viewBox); + updateViewBox(viewBox: IBoundsLike) { + // 图表的设置大小 + this._chartViewBox = { ...viewBox }; + this._updateViewBox(); } - private _updateViewBox(_viewBox: IBoundsLike) { + private _updateViewBox() { + if (!this._vchart) { + return; + } this._boundsChangeTag = true; - const viewBox = { ..._viewBox }; + const rootBounds = this._getVChartRootMarkBounds(); + this._vchart.getStage().defaultLayer.translateTo(-rootBounds.x1, -rootBounds.y1); + this._BoundsViewBox = rootBounds; + + const viewBox = { ...this._chartViewBox }; + this.setAttributes({ + x: viewBox.x1 + rootBounds.x1, + y: viewBox.y1 + rootBounds.y1, + width: rootBounds.x2 - rootBounds.x1, + height: rootBounds.y2 - rootBounds.y1 + }); // viewBox.x2 -= viewBox.x1; viewBox.y2 -= viewBox.y1; @@ -201,6 +223,13 @@ export class Chart extends Group implements IVisactorGraphic { viewBox.y1 = 0; this._vchart.resize(viewBox.x2 - viewBox.x1, viewBox.y2 - viewBox.y1); this._vchart.updateViewBox(viewBox); + const renderViewBox = { ...rootBounds }; + renderViewBox.x2 -= renderViewBox.x1; + renderViewBox.y2 -= renderViewBox.y1; + renderViewBox.x1 = 0; + renderViewBox.y1 = 0; + // @ts-ignore + this._vchart._compiler._view.renderer.setViewBox(renderViewBox, true); } release() { diff --git a/packages/vstory/src/story/character/component/character.ts b/packages/vstory/src/story/character/component/character.ts index 59f70ef..871caa7 100644 --- a/packages/vstory/src/story/character/component/character.ts +++ b/packages/vstory/src/story/character/component/character.ts @@ -98,6 +98,10 @@ export abstract class CharacterComponent extends CharacterBase { return this._group; } + getLayoutBounds() { + return this._group.AABBBounds; + } + checkEvent(event: StoryEvent): false | ICharacterPickInfo { if (!(event.detailPath ?? event.path).some(g => g === this._group)) { return false; diff --git a/packages/vstory/src/story/character/runtime-interface.ts b/packages/vstory/src/story/character/runtime-interface.ts index 08a8f5b..477363f 100644 --- a/packages/vstory/src/story/character/runtime-interface.ts +++ b/packages/vstory/src/story/character/runtime-interface.ts @@ -1,5 +1,5 @@ import type { IGroup } from '@visactor/vrender'; -import type { IPointLike } from '@visactor/vutils'; +import type { IBoundsLike, IPointLike } from '@visactor/vutils'; import type { StoryCanvas } from '../canvas/canvas'; import type { IStory, IStoryCanvas, StoryEvent } from '../interface/runtime-interface'; import type { ICharacterSpec } from './dsl-interface'; @@ -14,13 +14,14 @@ export interface ICharacter { type: string; visActorType: string; spec: ICharacterSpec; + graphic: Graphic | IGroup; init: () => void; reset: () => void; show: () => void; hide: () => void; getGraphicParent: () => IGroup; - graphic: Graphic | IGroup; + getLayoutBounds: () => IBoundsLike; tickTo: (t: number) => void; checkEvent: (event: StoryEvent) => false | (ICharacterPickInfo & any); diff --git a/packages/vstory/src/story/character/visactor/character.ts b/packages/vstory/src/story/character/visactor/character.ts index 5708543..c192d73 100644 --- a/packages/vstory/src/story/character/visactor/character.ts +++ b/packages/vstory/src/story/character/visactor/character.ts @@ -1,9 +1,10 @@ /* eslint-disable no-console */ import type { ICharacterSpec } from 'src/story/character/dsl-interface'; import { CharacterBase } from '../base/base'; -import type { ISpecProcess, ICharacterVisactor, IVisactorGraphic } from './interface'; +import type { ISpecProcess, ICharacterVisactor } from './interface'; import type { ICharacterInitOption } from '../runtime-interface'; import type { IChartCharacterRuntime } from '../chart/runtime/interface'; +import { getLayoutFromWidget } from '../../utils/layout'; export abstract class CharacterVisactor extends CharacterBase implements ICharacterVisactor { protected declare _specProcess: ISpecProcess; @@ -62,6 +63,22 @@ export abstract class CharacterVisactor extends CharacterBase implements ICharac return this._graphic; } + getViewBoxFromSpec() { + const layout = getLayoutFromWidget(this._spec.position); + const viewBox = { + x1: layout.x, + x2: layout.x + layout.width, + y1: layout.y, + y2: layout.y + layout.height + }; + return { layout, viewBox }; + } + + getLayoutBounds() { + const { viewBox } = this.getViewBoxFromSpec(); + return viewBox; + } + tickTo(t: number): void { return; } From cba75ffeaa87c92c4ea07886cc15f5c7e0f9b39c Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Mon, 19 Aug 2024 11:16:11 +0800 Subject: [PATCH 4/7] feat: user expend aabbbounds --- .../character/chart/graphic/vchart-graphic.ts | 34 ++++--------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts b/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts index b8133ed..f06a2a5 100644 --- a/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts +++ b/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts @@ -1,5 +1,5 @@ import type { IVisactorGraphic } from '../../visactor/interface'; -import type { AABBBounds, IBoundsLike } from '@visactor/vutils'; +import { Bounds, type AABBBounds, type IBoundsLike } from '@visactor/vutils'; import type { ISpec, IVChart } from '@visactor/vchart'; import type { GraphicType, IGroupGraphicAttribute, ITicker } from '@visactor/vrender'; import { genNumberType, Group } from '@visactor/vrender'; @@ -43,7 +43,6 @@ export class Chart extends Group implements IVisactorGraphic { private _chartViewBox: IBoundsLike = { x1: 0, y1: 0, x2: 100, y2: 100 }; private _BoundsViewBox: IBoundsLike = { x1: 0, y1: 0, x2: 100, y2: 100 }; - private _offset: { x: number; y: number } = { x: 0, y: 0 }; drawTag = false; protected _boundsChangeTag: boolean = true; @@ -57,31 +56,12 @@ export class Chart extends Group implements IVisactorGraphic { if (!this._vchart) { return super.doUpdateAABBBounds(); } - return this._getVChartRootMarkBounds().translate(this.attribute.x, this.attribute.y); - } - - shouldUpdateAABBBounds(): boolean { - if (super.shouldUpdateAABBBounds()) { - return true; - } - if (this._boundsChangeTag) { - this._boundsChangeTag = false; - return true; - } - return false; - } - - protected tryUpdateAABBBounds(full?: boolean): AABBBounds { - if (!this.shouldUpdateAABBBounds()) { - return this._AABBBounds; - } - if (!this.valid) { - this._AABBBounds.clear(); - return this._AABBBounds; - } - - const bounds = this.doUpdateAABBBounds(full); - return bounds; + const b = new Bounds(); + b.x1 = this.attribute.x; + b.x2 = this.attribute.x + this.attribute.width; + b.y1 = this.attribute.y; + b.y2 = this.attribute.y + this.attribute.height; + return b; } constructor(params: IChartGraphicAttribute) { From 3ec08c8fe857f14de6c07d139bfb11daf7b7308f Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Tue, 20 Aug 2024 21:16:28 +0800 Subject: [PATCH 5/7] feat: support vchart model pick --- .../vstory/demo/src/demos/VChartGraphic.tsx | 368 +++++++++++++----- .../edit/edit-component/image-selection.ts | 1 - .../src/story/character/chart/character.ts | 12 +- .../src/story/character/runtime-interface.ts | 5 +- .../vstory/src/story/utils/vchart-pick.ts | 166 ++++++++ 5 files changed, 450 insertions(+), 102 deletions(-) create mode 100644 packages/vstory/src/story/utils/vchart-pick.ts diff --git a/packages/vstory/demo/src/demos/VChartGraphic.tsx b/packages/vstory/demo/src/demos/VChartGraphic.tsx index 8d699d4..d5f08a7 100644 --- a/packages/vstory/demo/src/demos/VChartGraphic.tsx +++ b/packages/vstory/demo/src/demos/VChartGraphic.tsx @@ -13,6 +13,256 @@ Edit.registerEditComponent('text', TextSelection); Edit.registerEditComponent('richtext', RichTextSelection); Edit.registerEditComponent('box-selection', BoxSelection); +const goldenMedals = { + 2000: [ + { country: 'USA', value: 37 }, + { country: 'Russia', value: 32 }, + { country: 'China', value: 28 }, + { country: 'Australia', value: 16 }, + { country: 'Germany', value: 13 }, + { country: 'France', value: 13 }, + { country: 'Italy', value: 13 }, + { country: 'Netherlands', value: 12 }, + { country: 'Cuba', value: 11 }, + { country: 'U.K.', value: 11 } + ], + 2004: [ + { country: 'USA', value: 36 }, + { country: 'China', value: 32 }, + { country: 'Russia', value: 28 }, + { country: 'Australia', value: 17 }, + { country: 'Japan', value: 16 }, + { country: 'Germany', value: 13 }, + { country: 'France', value: 11 }, + { country: 'Italy', value: 10 }, + { country: 'South Korea', value: 9 }, + { country: 'U.K.', value: 9 } + ], + 2008: [ + { country: 'China', value: 48 }, + { country: 'USA', value: 36 }, + { country: 'Russia', value: 24 }, + { country: 'U.K.', value: 19 }, + { country: 'Germany', value: 16 }, + { country: 'Australia', value: 14 }, + { country: 'South Korea', value: 13 }, + { country: 'Japan', value: 9 }, + { country: 'Italy', value: 8 }, + { country: 'France', value: 7 } + ], + 2012: [ + { country: 'USA', value: 46 }, + { country: 'China', value: 39 }, + { country: 'U.K.', value: 29 }, + { country: 'Russia', value: 19 }, + { country: 'South Korea', value: 13 }, + { country: 'Germany', value: 11 }, + { country: 'France', value: 11 }, + { country: 'Australia', value: 8 }, + { country: 'Italy', value: 8 }, + { country: 'Hungary', value: 8 } + ], + 2016: [ + { country: 'USA', value: 46 }, + { country: 'U.K.', value: 27 }, + { country: 'China', value: 26 }, + { country: 'Russia', value: 19 }, + { country: 'Germany', value: 17 }, + { country: 'Japan', value: 12 }, + { country: 'France', value: 10 }, + { country: 'South Korea', value: 9 }, + { country: 'Italy', value: 8 }, + { country: 'Australia', value: 8 } + ], + 2020: [ + { country: 'USA', value: 39 }, + { country: 'China', value: 38 }, + { country: 'Japan', value: 27 }, + { country: 'U.K.', value: 22 }, + { country: 'Russian Olympic Committee', value: 20 }, + { country: 'Australia', value: 17 }, + { country: 'Netherlands', value: 10 }, + { country: 'France', value: 10 }, + { country: 'Germany', value: 10 }, + { country: 'Italy', value: 10 } + ] +}; + +const colors = { + China: '#d62728', + USA: '#1664FF', + Russia: '#B2CFFF', + 'U.K.': '#1AC6FF', + Australia: '#94EFFF', + Japan: '#FF8A00', + Cuba: '#FFCE7A', + Germany: '#3CC780', + France: '#B9EDCD', + Italy: '#7442D4', + 'South Korea': '#DDC5FA', + 'Russian Olympic Committee': '#B2CFFF', + Netherlands: '#FFC400', + Hungary: '#FAE878' +}; + +const dataSpecs = Object.keys(goldenMedals).map(year => { + return { + data: [ + { + id: 'id', + values: goldenMedals[year] + .sort((a, b) => b.value - a.value) + .map(v => { + return { ...v, fill: colors[v.country] }; + }) + }, + { + id: 'year', + values: [{ year }] + } + ] + }; +}); +const duration = 1000; +const exchangeDuration = 600; + +const spec = { + type: 'bar', + padding: { + top: 12, + right: 100, + bottom: 12 + }, + data: dataSpecs[0].data, + direction: 'horizontal', + yField: 'country', + xField: 'value', + seriesField: 'country', + bar: { + style: { + fill: datum => datum.fill + } + }, + axes: [ + { + animation: true, + orient: 'bottom', + type: 'linear', + visible: true, + max: 50, + grid: { + visible: true + } + }, + { + animation: true, + id: 'axis-left', + orient: 'left', + width: 130, + tick: { visible: false }, + label: { visible: true }, + type: 'band' + } + ], + title: { + visible: true, + text: 'Top 10 Olympic Gold Medals by Country Since 2000' + }, + animationUpdate: { + bar: [ + { + type: 'update', + options: { excludeChannels: ['y'] }, + easing: 'linear', + duration + }, + { + channel: ['y'], + easing: 'circInOut', + duration: exchangeDuration + } + ], + axis: { + duration: exchangeDuration, + easing: 'circInOut' + } + }, + animationEnter: { + bar: [ + { + type: 'moveIn', + duration: exchangeDuration, + easing: 'circInOut', + options: { + direction: 'y', + orient: 'negative' + } + } + ] + }, + animationExit: { + bar: [ + { + type: 'fadeOut', + duration: exchangeDuration + } + ] + }, + customMark: [ + { + type: 'text', + dataId: 'year', + style: { + textBaseline: 'bottom', + fontSize: 200, + textAlign: 'right', + fontFamily: 'PingFang SC', + fontWeight: 600, + text: datum => datum.year, + x: (datum, ctx) => { + return ctx.vchart.getChart().getCanvasRect()?.width - 50; + }, + y: (datum, ctx) => { + return ctx.vchart.getChart().getCanvasRect()?.height - 50; + }, + fill: 'grey', + fillOpacity: 0.5 + } + } + ], + player: { + type: 'continuous', + orient: 'bottom', + auto: true, + loop: true, + dx: 80, + position: 'middle', + interval: duration, + specs: dataSpecs, + slider: { + railStyle: { + height: 6 + } + }, + controller: { + backward: { + style: { + size: 12 + } + }, + forward: { + style: { + size: 12 + } + }, + start: { + order: 1, + position: 'end' + } + } + } +}; + export const VChartGraphic = () => { const id = 'storyBar'; @@ -31,7 +281,7 @@ export const VChartGraphic = () => { const tempSpec: IStorySpec = { characters: [ { - type: 'RectComponent', + type: 'Rect', id: 'test-graphics-0', zIndex: 10, position: { @@ -53,99 +303,17 @@ export const VChartGraphic = () => { } }, { - type: 'BarChart', + type: 'VChart', id: 'test-chart-0', - zIndex: 9, + zIndex: 10, position: { - top: 200, - left: 100, - width: 400, - height: 400 + top: 40, + left: 50, + width: 500, + height: 500 }, options: { - title: { - text: 'Timeline Chart', - orient: 'bottom', - align: 'center', - textStyle: { - fontSize: 10, - lineHeight: 10 - } - }, - padding: 12, - data: [ - { - id: 'id0', - values: chartData - } - ], - direction: 'vertical', - seriesSpec: [ - { - matchInfo: { specIndex: 'all' }, - spec: { - type: 'bar', - xField: 'year', - yField: 'value' - } - } - ], - componentSpec: [ - { - specKey: 'axes', - matchInfo: { orient: 'left' }, - spec: { - label: { - style: { - fontSize: 20 - } - } - } - }, - { - specKey: 'axes', - matchInfo: { orient: 'bottom' }, - spec: { - type: 'band' - } - }, - { - specKey: 'markLine', - matchInfo: { orient: 'left' }, - spec: { - coordinates: [chartData[0], chartData[5]], - line: { - style: { - lineDash: [0], - lineWidth: 2, - stroke: '#000' - } - }, - label: { - position: 'middle', - text: `asdadsasd% CARG`, - labelBackground: { - padding: 8, - style: { - fill: '#fff', - fillOpacity: 1, - stroke: '#3CC780', - lineWidth: 2, - cornerRadius: 8 - } - }, - style: { - fill: '#3CC780' - } - }, - endSymbol: { - size: 12, - refX: -4 - }, - offsetY: -100 - } - } - ] + spec } } ], @@ -161,14 +329,13 @@ export const VChartGraphic = () => { characterActions: [ { startTime: 0, - duration: 0, action: 'appear', + selector: '*', payload: { style: {}, animation: { - duration: 0, - easing: 'linear', - effect: 'fadeIn' + duration: 1000, + easing: 'linear' } as any } } @@ -179,8 +346,15 @@ export const VChartGraphic = () => { characterActions: [ { startTime: 0, - duration: 0, - action: 'appear' + action: 'appear', + selector: '*', + payload: { + style: {}, + animation: { + duration: 1000, + easing: 'linear' + } as any + } } ] } @@ -191,7 +365,7 @@ export const VChartGraphic = () => { ] }; const story = new Story(tempSpec, { dom: id }); - story.play(); + story.play(false); const edit = new Edit(story); edit.emitter.on('startEdit', msg => { if (msg.type === 'commonEdit' && msg.actionInfo.character) { diff --git a/packages/vstory/src/edit/edit-component/image-selection.ts b/packages/vstory/src/edit/edit-component/image-selection.ts index 5e4880f..a501b2c 100644 --- a/packages/vstory/src/edit/edit-component/image-selection.ts +++ b/packages/vstory/src/edit/edit-component/image-selection.ts @@ -1,4 +1,3 @@ -// import { StoryGraphicType } from '../../dsl/constant'; import { StoryComponentType } from '../../constants/character'; import { type IEditComponent } from '../interface'; import { BaseSelection } from './base-selection'; diff --git a/packages/vstory/src/story/character/chart/character.ts b/packages/vstory/src/story/character/chart/character.ts index 0d849fd..77443d7 100644 --- a/packages/vstory/src/story/character/chart/character.ts +++ b/packages/vstory/src/story/character/chart/character.ts @@ -14,6 +14,7 @@ import { SeriesSpecRuntime } from './runtime/series-spec'; import type { StoryEvent } from '../../interface/runtime-interface'; import type { ICharacterPickInfo } from '../runtime-interface'; import { getLayoutFromWidget } from '../../utils/layout'; +import { getChartModelWithEvent } from '../../utils/vchart-pick'; export class CharacterChart extends CharacterVisactor { static type = 'CharacterChart'; @@ -137,9 +138,16 @@ export class CharacterChart extends CharacterVisactor { // return false; // } const chartPath = event.detailPath[event.detailPath.length - 1]; + const result = getChartModelWithEvent(this._graphic.vProduct, event); + if (!result) { + return false; + } + const graphic = chartPath?.[chartPath.length - 1]; return { - part: chartPath?.[chartPath.length - 1]?.type, - graphicType: chartPath?.[chartPath.length - 1]?.type + part: result.type, + modelInfo: result, + graphic, + graphicType: graphic.type }; } diff --git a/packages/vstory/src/story/character/runtime-interface.ts b/packages/vstory/src/story/character/runtime-interface.ts index 477363f..25eb7e9 100644 --- a/packages/vstory/src/story/character/runtime-interface.ts +++ b/packages/vstory/src/story/character/runtime-interface.ts @@ -1,13 +1,14 @@ -import type { IGroup } from '@visactor/vrender'; +import type { IGroup, IGraphic } from '@visactor/vrender'; import type { IBoundsLike, IPointLike } from '@visactor/vutils'; -import type { StoryCanvas } from '../canvas/canvas'; import type { IStory, IStoryCanvas, StoryEvent } from '../interface/runtime-interface'; import type { ICharacterSpec } from './dsl-interface'; import type { Graphic } from './component/graphic/graphic'; export interface ICharacterPickInfo { part: string; + graphic: IGraphic; graphicType: string; + modelInfo: any; } export interface ICharacter { id: string; diff --git a/packages/vstory/src/story/utils/vchart-pick.ts b/packages/vstory/src/story/utils/vchart-pick.ts new file mode 100644 index 0000000..0adaa6e --- /dev/null +++ b/packages/vstory/src/story/utils/vchart-pick.ts @@ -0,0 +1,166 @@ +import type { IGraphic, IGraphicAttribute } from '@visactor/vrender'; +import type { VChart } from '@visactor/vchart'; +import type { StoryEvent } from '../interface/runtime-interface'; + +function commonModelInfo(model: any) { + return { + type: model.type, + model, + specKey: model.specKey, + specIndex: model.getSpecIndex() + }; +} + +export const seriesMarkPick = { + check: (graphic: IGraphic, graphicPath: IGraphic[]) => { + return graphic.name?.startsWith('seriesGroup_'); + }, + modelInfo: (chart: VChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) => { + const nameInfo = graphic.name.split('_'); + const seriesId = +nameInfo[2]; + const markGraphic = graphicPath[index + 1]; + const markId = +markGraphic.name.split('_')[1]; + const series = chart.getChart().getSeriesInIds([seriesId])[0]; + const datum = graphicPath[graphicPath.length - 1].__vgrammar_scene_item__.data; + return { + type: 'seriesMark', + model: series, + mark: series.getMarkInId(markId), + specKey: series.specKey, + specIndex: series.getSpecIndex(), + datum + }; + } +}; + +export const axisMarkPick = { + check: (graphic: IGraphic, graphicPath: IGraphic[]) => { + return graphic.name === 'axis' || graphic.name === 'axis-grid'; + }, + modelInfo: (chart: VChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) => { + const axisGroup = graphicPath[index - 1]; + const axisId = +axisGroup.name.split('_')[1]; + const axis = chart + .getChart() + .getAllComponents() + // @ts-ignore + .find(c => c._axisMark?.id === axisId || c._gridMark?.id === axisId); + return commonModelInfo(axis); + } +}; + +const MarkerClassName: { [key: string]: boolean } = { + MarkLine: true, + MarkArea: true, + MarkPoint: true, + MarkArcLine: true, + MarkArcArea: true +}; +export const markerMarkPick = { + check: (graphic: IGraphic, graphicPath: IGraphic[]) => { + return !!MarkerClassName[graphic.constructor.name]; + }, + modelInfo: (chart: VChart, graphic: IGraphic, graphicPath: IGraphic[]) => { + const markerId = +graphic.id.split('-')[1]; + const model = chart + .getChart() + .getAllComponents() + // @ts-ignore + .find(c => c.id === markerId); + return commonModelInfo(model); + } +}; + +export const labelMarkPick = { + check: (graphic: IGraphic, graphicPath: IGraphic[]) => { + return graphic.name === 'data-label'; + }, + modelInfo: (chart: VChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) => { + const id = +graphicPath[index - 1].name.split('_')[1]; + const model = chart + .getChart() + .getAllComponents() + // @ts-ignore + .find(c => { + if (c.type !== 'label' && c.type !== 'totalLabel') { + return false; + } + return c.getMarks().some(m => m.id === id); + }); + // @ts-ignore + return { ...commonModelInfo(model), datum: graphicPath[graphicPath.length - 1].attribute.data }; + } +}; + +function commonModePick(vrenderGraphicClassName: string, modelName: string) { + return { + check: (graphic: IGraphic, graphicPath: IGraphic[]) => { + return graphic.constructor.name === vrenderGraphicClassName; + }, + modelInfo: (chart: VChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) => { + return commonModelInfo( + chart + .getChart() + .getAllComponents() + // @ts-ignore + .find(c => c.type === modelName) + ); + } + }; +} + +export const discreteLegendMarkPick = commonModePick('DiscreteLegend', 'discreteLegend'); +export const colorLegendMarkPick = commonModePick('ColorContinuousLegend', 'colorLegend'); +export const sizeLegendMarkPick = commonModePick('SizeContinuousLegend', 'sizeLegend'); +export const scrollBarMarkPick = commonModePick('ScrollBar', 'scrollBar'); +export const titleBarMarkPick = commonModePick('Title', 'title'); +export const continuousPlayerMarkPick = commonModePick('ContinuousPlayer', 'player'); +export const discretePlayerMarkPick = commonModePick('DiscretePlayer', 'player'); + +const modelCheck: { + check: (graphic: IGraphic, graphicPath: IGraphic[]) => boolean; + modelInfo: (chart: VChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) => any; +}[] = [ + seriesMarkPick, + axisMarkPick, + discreteLegendMarkPick, + colorLegendMarkPick, + sizeLegendMarkPick, + markerMarkPick, + scrollBarMarkPick, + labelMarkPick, + titleBarMarkPick, + continuousPlayerMarkPick, + discretePlayerMarkPick +]; + +/** + * 从event属性上,读取当前pick到的图表模块内容 + * @param event + */ +export function getChartModelWithEvent(chart: VChart, event: StoryEvent) { + const graphicPath = event.detailPath[event.detailPath.length - 1] as unknown as IGraphic< + Partial + >[]; + if (!graphicPath) { + return null; + } + const pickGraphic = graphicPath?.[graphicPath.length - 1]; + if (!pickGraphic) { + return null; + } + return getGraphicModelMark(chart, pickGraphic, graphicPath, 0); +} + +export function getGraphicModelMark(chart: VChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) { + const modelPick = modelCheck.find(mc => mc.check(graphic, graphicPath)); + if (modelPick) { + return modelPick.modelInfo(chart, graphic, graphicPath, index); + } + // @ts-ignore + if (index >= graphicPath.length - 1) { + return null; + } + + return getGraphicModelMark(chart, graphicPath[index + 1], graphicPath, index + 1); +} From c6a3852ba84b977718b5c587018af10a6b735e0a Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Thu, 22 Aug 2024 17:48:27 +0800 Subject: [PATCH 6/7] feat: support vchart model pick --- packages/vstory/demo/src/demos/Playground.tsx | 1 - .../vstory/demo/src/demos/VChartGraphic.tsx | 334 +++++------------- packages/vstory/demo/src/demos/lv/bar1.tsx | 1 - .../edit/edit-component/chart-selection.ts | 13 +- .../edit-control/transform-control.ts | 9 + .../edit-control/transform-drag.ts | 9 +- .../src/story/character/chart/character.ts | 25 +- .../character/chart/graphic/vchart-graphic.ts | 56 +-- packages/vstory/src/story/utils/layout.ts | 4 +- 9 files changed, 159 insertions(+), 293 deletions(-) diff --git a/packages/vstory/demo/src/demos/Playground.tsx b/packages/vstory/demo/src/demos/Playground.tsx index e3e1817..07900d9 100644 --- a/packages/vstory/demo/src/demos/Playground.tsx +++ b/packages/vstory/demo/src/demos/Playground.tsx @@ -4,7 +4,6 @@ import { Story } from '../../../src/story/story'; import { Edit } from '../../../src/edit/edit'; import '../../../src/story/index'; import { cloneDeep } from '@visactor/vutils'; -import { CommonEditComponent } from '../../../src/edit/edit-component/common'; import { BoxSelection } from '../../../src/edit/edit-component/box-selection'; import { TextSelection } from '../../../src/edit/edit-component/text-selection'; import { RichTextSelection } from '../../../src/edit/edit-component/richtext-selection'; diff --git a/packages/vstory/demo/src/demos/VChartGraphic.tsx b/packages/vstory/demo/src/demos/VChartGraphic.tsx index d5f08a7..b1b0a30 100644 --- a/packages/vstory/demo/src/demos/VChartGraphic.tsx +++ b/packages/vstory/demo/src/demos/VChartGraphic.tsx @@ -3,263 +3,115 @@ import { IStorySpec } from '../../../src/story/interface'; import { Story } from '../../../src/story/story'; import '../../../src/story/index'; import { Edit } from '../../../src/edit/edit'; -import { CommonEditComponent } from '../../../src/edit/edit-component/common'; import { BoxSelection } from '../../../src/edit/edit-component/box-selection'; import { TextSelection } from '../../../src/edit/edit-component/text-selection'; import { RichTextSelection } from '../../../src/edit/edit-component/richtext-selection'; -Edit.registerEditComponent('common', CommonEditComponent); Edit.registerEditComponent('text', TextSelection); Edit.registerEditComponent('richtext', RichTextSelection); Edit.registerEditComponent('box-selection', BoxSelection); -const goldenMedals = { - 2000: [ - { country: 'USA', value: 37 }, - { country: 'Russia', value: 32 }, - { country: 'China', value: 28 }, - { country: 'Australia', value: 16 }, - { country: 'Germany', value: 13 }, - { country: 'France', value: 13 }, - { country: 'Italy', value: 13 }, - { country: 'Netherlands', value: 12 }, - { country: 'Cuba', value: 11 }, - { country: 'U.K.', value: 11 } - ], - 2004: [ - { country: 'USA', value: 36 }, - { country: 'China', value: 32 }, - { country: 'Russia', value: 28 }, - { country: 'Australia', value: 17 }, - { country: 'Japan', value: 16 }, - { country: 'Germany', value: 13 }, - { country: 'France', value: 11 }, - { country: 'Italy', value: 10 }, - { country: 'South Korea', value: 9 }, - { country: 'U.K.', value: 9 } - ], - 2008: [ - { country: 'China', value: 48 }, - { country: 'USA', value: 36 }, - { country: 'Russia', value: 24 }, - { country: 'U.K.', value: 19 }, - { country: 'Germany', value: 16 }, - { country: 'Australia', value: 14 }, - { country: 'South Korea', value: 13 }, - { country: 'Japan', value: 9 }, - { country: 'Italy', value: 8 }, - { country: 'France', value: 7 } - ], - 2012: [ - { country: 'USA', value: 46 }, - { country: 'China', value: 39 }, - { country: 'U.K.', value: 29 }, - { country: 'Russia', value: 19 }, - { country: 'South Korea', value: 13 }, - { country: 'Germany', value: 11 }, - { country: 'France', value: 11 }, - { country: 'Australia', value: 8 }, - { country: 'Italy', value: 8 }, - { country: 'Hungary', value: 8 } - ], - 2016: [ - { country: 'USA', value: 46 }, - { country: 'U.K.', value: 27 }, - { country: 'China', value: 26 }, - { country: 'Russia', value: 19 }, - { country: 'Germany', value: 17 }, - { country: 'Japan', value: 12 }, - { country: 'France', value: 10 }, - { country: 'South Korea', value: 9 }, - { country: 'Italy', value: 8 }, - { country: 'Australia', value: 8 } - ], - 2020: [ - { country: 'USA', value: 39 }, - { country: 'China', value: 38 }, - { country: 'Japan', value: 27 }, - { country: 'U.K.', value: 22 }, - { country: 'Russian Olympic Committee', value: 20 }, - { country: 'Australia', value: 17 }, - { country: 'Netherlands', value: 10 }, - { country: 'France', value: 10 }, - { country: 'Germany', value: 10 }, - { country: 'Italy', value: 10 } - ] -}; - -const colors = { - China: '#d62728', - USA: '#1664FF', - Russia: '#B2CFFF', - 'U.K.': '#1AC6FF', - Australia: '#94EFFF', - Japan: '#FF8A00', - Cuba: '#FFCE7A', - Germany: '#3CC780', - France: '#B9EDCD', - Italy: '#7442D4', - 'South Korea': '#DDC5FA', - 'Russian Olympic Committee': '#B2CFFF', - Netherlands: '#FFC400', - Hungary: '#FAE878' -}; - -const dataSpecs = Object.keys(goldenMedals).map(year => { - return { - data: [ - { - id: 'id', - values: goldenMedals[year] - .sort((a, b) => b.value - a.value) - .map(v => { - return { ...v, fill: colors[v.country] }; - }) - }, - { - id: 'year', - values: [{ year }] - } - ] - }; -}); -const duration = 1000; -const exchangeDuration = 600; - const spec = { type: 'bar', - padding: { - top: 12, - right: 100, - bottom: 12 - }, - data: dataSpecs[0].data, + data: [ + { + id: 'barData', + values: [ + { + name: 'Apple', + value: 214480 + }, + { + name: 'Google', + value: 155506 + }, + { + name: 'Amazon', + value: 100764 + }, + { + name: 'Microsoft', + value: 92715 + }, + { + name: 'Coca-Cola', + value: 66341 + }, + { + name: 'Samsung', + value: 59890 + }, + { + name: 'Toyota', + value: 53404 + }, + { + name: 'Mercedes-Benz', + value: 48601 + }, + { + name: 'Facebook', + value: 45168 + }, + { + name: "McDonald's", + value: 43417 + }, + { + name: 'Intel', + value: 43293 + }, + { + name: 'IBM', + value: 42972 + }, + { + name: 'BMW', + value: 41006 + }, + { + name: 'Disney', + value: 39874 + }, + { + name: 'Cisco', + value: 34575 + }, + { + name: 'GE', + value: 32757 + }, + { + name: 'Nike', + value: 30120 + }, + { + name: 'Louis Vuitton', + value: 28152 + }, + { + name: 'Oracle', + value: 26133 + }, + { + name: 'Honda', + value: 23682 + } + ] + } + ], direction: 'horizontal', - yField: 'country', xField: 'value', - seriesField: 'country', - bar: { - style: { - fill: datum => datum.fill - } - }, + yField: 'name', axes: [ { - animation: true, orient: 'bottom', - type: 'linear', - visible: true, - max: 50, - grid: { - visible: true - } - }, - { - animation: true, - id: 'axis-left', - orient: 'left', - width: 130, - tick: { visible: false }, - label: { visible: true }, - type: 'band' + visible: false } ], - title: { + label: { visible: true, - text: 'Top 10 Olympic Gold Medals by Country Since 2000' - }, - animationUpdate: { - bar: [ - { - type: 'update', - options: { excludeChannels: ['y'] }, - easing: 'linear', - duration - }, - { - channel: ['y'], - easing: 'circInOut', - duration: exchangeDuration - } - ], - axis: { - duration: exchangeDuration, - easing: 'circInOut' - } - }, - animationEnter: { - bar: [ - { - type: 'moveIn', - duration: exchangeDuration, - easing: 'circInOut', - options: { - direction: 'y', - orient: 'negative' - } - } - ] - }, - animationExit: { - bar: [ - { - type: 'fadeOut', - duration: exchangeDuration - } - ] - }, - customMark: [ - { - type: 'text', - dataId: 'year', - style: { - textBaseline: 'bottom', - fontSize: 200, - textAlign: 'right', - fontFamily: 'PingFang SC', - fontWeight: 600, - text: datum => datum.year, - x: (datum, ctx) => { - return ctx.vchart.getChart().getCanvasRect()?.width - 50; - }, - y: (datum, ctx) => { - return ctx.vchart.getChart().getCanvasRect()?.height - 50; - }, - fill: 'grey', - fillOpacity: 0.5 - } - } - ], - player: { - type: 'continuous', - orient: 'bottom', - auto: true, - loop: true, - dx: 80, - position: 'middle', - interval: duration, - specs: dataSpecs, - slider: { - railStyle: { - height: 6 - } - }, - controller: { - backward: { - style: { - size: 12 - } - }, - forward: { - style: { - size: 12 - } - }, - start: { - order: 1, - position: 'end' - } - } + animation: false } }; @@ -334,6 +186,8 @@ export const VChartGraphic = () => { payload: { style: {}, animation: { + effect: 'move', + move: { pos: 'top' }, duration: 1000, easing: 'linear' } as any @@ -351,6 +205,8 @@ export const VChartGraphic = () => { payload: { style: {}, animation: { + effect: 'fade', + move: { pos: 'top' }, duration: 1000, easing: 'linear' } as any diff --git a/packages/vstory/demo/src/demos/lv/bar1.tsx b/packages/vstory/demo/src/demos/lv/bar1.tsx index f18f28c..3655d44 100644 --- a/packages/vstory/demo/src/demos/lv/bar1.tsx +++ b/packages/vstory/demo/src/demos/lv/bar1.tsx @@ -4,7 +4,6 @@ import { Story } from '../../../../src/story/story'; import { Edit } from '../../../../src/edit/edit'; import '../../../../src/story/index'; import { cloneDeep } from '@visactor/vutils'; -import { CommonEditComponent } from '../../../../src/edit/edit-component/common'; import { BoxSelection } from '../../../../src/edit/edit-component/box-selection'; import { TextSelection } from '../../../../src/edit/edit-component/text-selection'; import { RichTextSelection } from '../../../../src/edit/edit-component/richtext-selection'; diff --git a/packages/vstory/src/edit/edit-component/chart-selection.ts b/packages/vstory/src/edit/edit-component/chart-selection.ts index 28e33ec..507742b 100644 --- a/packages/vstory/src/edit/edit-component/chart-selection.ts +++ b/packages/vstory/src/edit/edit-component/chart-selection.ts @@ -20,18 +20,9 @@ export class ChartSelection extends BaseSelection implements IEditComponent { return; } - const viewBox = (actionInfo.character.graphic as Chart).vchart.getStage().viewBox; const group = actionInfo.character.getGraphicParent(); - const { angle, x, y } = group.attribute; - this._layoutComponent.updateBoundsAndAngle( - { - x1: viewBox.x1 + x, - y1: viewBox.y1 + y, - x2: viewBox.x2 + x, - y2: viewBox.y2 + y - }, - angle - ); + const { angle } = group.attribute; + this._layoutComponent.updateBoundsAndAngle(actionInfo.character.getLayoutBounds(), angle); // this._layoutComponent.updateBoundsAndAngle(actionInfo.character.getGraphicParent().AABBBounds, 0); } diff --git a/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts b/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts index 5947932..39c32c1 100644 --- a/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts +++ b/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts @@ -891,7 +891,16 @@ export class TransformControl extends AbstractComponent): void { - // this.group.setAttributes(attr); - this._graphic.setAttributes(attr); - // this._text.updateAttribute({}); + // character 的属性 + if (attr.position) { + this._spec.position = attr.position; + // 位置属性 + this._graphic.updateViewBox(this.getViewBoxFromSpec().viewBox); + } } getViewBoxFromSpec() { const layout = getLayoutFromWidget(this._spec.position); @@ -116,7 +119,6 @@ export class CharacterChart extends CharacterVisactor { return; } protected _updateVisactorSpec(): void { - // console.log('_updateVisactorSpec', this._specProcess.getVisSpec()); this._graphic?.updateSpec(this._specProcess.getVisSpec()); } @@ -133,13 +135,18 @@ export class CharacterChart extends CharacterVisactor { if (!(event.detailPath ?? event.path).some(g => g === this._graphic)) { return false; } - // 超出vchart viewBox 外的图表元素,依然会绘制并且能被选中 - // if (!this._graphic.pointInVChart((event as any).canvasX, (event as any).canvasY)) { - // return false; - // } const chartPath = event.detailPath[event.detailPath.length - 1]; const result = getChartModelWithEvent(this._graphic.vProduct, event); if (!result) { + // 点击到图表的空白区域 + if (this._graphic.pointInViewBox((event as any).canvasX, (event as any).canvasY)) { + return { + part: 'null', + graphic: null, + modelInfo: null, + graphicType: 'null' + }; + } return false; } const graphic = chartPath?.[chartPath.length - 1]; diff --git a/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts b/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts index f06a2a5..8d613af 100644 --- a/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts +++ b/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts @@ -41,7 +41,10 @@ export class Chart extends Group implements IVisactorGraphic { return this._vchart; } - private _chartViewBox: IBoundsLike = { x1: 0, y1: 0, x2: 100, y2: 100 }; + // 设置的 viewBox 是全局值 + private _globalViewBox: IBoundsLike = { x1: 0, y1: 0, x2: 100, y2: 100 }; + // 设置的 viewBox 相对于 vchart-graphic 的位置 + private _localViewBox: IBoundsLike = { x1: 0, y1: 0, x2: 100, y2: 100 }; private _BoundsViewBox: IBoundsLike = { x1: 0, y1: 0, x2: 100, y2: 100 }; drawTag = false; @@ -121,17 +124,13 @@ export class Chart extends Group implements IVisactorGraphic { stage.pauseTriggerEvent(); } if (params.viewBox) { - const x1 = params.viewBox.x1 ?? 0; - const y1 = params.viewBox.y1 ?? 0; - const x2 = params.viewBox.x2 ?? 0; - const y2 = params.viewBox.y2 ?? 0; - this.setAttributes({ - x: x1, - y: y1, - width: x2 - x1, - height: y2 - y1 - }); + this.updateViewBox(params.viewBox); } + + this.setAttributes({ + fill: 'red', + fillOpacity: 0.5 + }); } /** @@ -150,21 +149,15 @@ export class Chart extends Group implements IVisactorGraphic { return isPointInBounds(target, vchart.getStage().viewBox); } - setAttributes(attrs: Partial) { - super.setAttributes(attrs); - const vchart = this._vchart; - const viewBox = vchart.getStage().viewBox; - - const x1 = attrs.x ?? viewBox.x1; - const y1 = attrs.y ?? viewBox.y1; - const width = attrs.width ?? viewBox.width(); - const height = attrs.height ?? viewBox.height(); - this.updateViewBox({ - x1, - y1, - x2: x1 + width, - y2: y1 + height - }); + /** + * 判定点是否在设置 viewBox 内。设置 viewBox 会小于展示 bounds + * @param canvasX + * @param canvasY + */ + pointInViewBox(canvasX: number, canvasY: number): boolean { + const target = { x: 0, y: 0 }; + this.globalTransMatrix.transformPoint({ x: canvasX, y: canvasY }, target); + return isPointInBounds(target, this._localViewBox); } updateSpec(spec: ISpec, viewBox?: IBoundsLike, forceMerge = false, morphConfig = false) { @@ -176,7 +169,9 @@ export class Chart extends Group implements IVisactorGraphic { updateViewBox(viewBox: IBoundsLike) { // 图表的设置大小 - this._chartViewBox = { ...viewBox }; + this._globalViewBox = { ...viewBox }; + this._localViewBox = { x1: 0, y1: 0, x2: viewBox.x2 - viewBox.x1, y2: viewBox.y2 - viewBox.y1 }; + this._updateViewBox(); } @@ -189,13 +184,18 @@ export class Chart extends Group implements IVisactorGraphic { this._vchart.getStage().defaultLayer.translateTo(-rootBounds.x1, -rootBounds.y1); this._BoundsViewBox = rootBounds; - const viewBox = { ...this._chartViewBox }; + const viewBox = { ...this._globalViewBox }; this.setAttributes({ x: viewBox.x1 + rootBounds.x1, y: viewBox.y1 + rootBounds.y1, width: rootBounds.x2 - rootBounds.x1, height: rootBounds.y2 - rootBounds.y1 }); + // viewBox 在展示 bounds 下的位置 + this._localViewBox.x1 = -rootBounds.x1; + this._localViewBox.y1 = -rootBounds.y1; + this._localViewBox.x2 += -rootBounds.x1; + this._localViewBox.y2 += -rootBounds.y1; // viewBox.x2 -= viewBox.x1; viewBox.y2 -= viewBox.y1; diff --git a/packages/vstory/src/story/utils/layout.ts b/packages/vstory/src/story/utils/layout.ts index 265c675..210a998 100644 --- a/packages/vstory/src/story/utils/layout.ts +++ b/packages/vstory/src/story/utils/layout.ts @@ -2,8 +2,8 @@ import type { ILayoutAttribute, IWidgetData } from '../character'; export function getLayoutFromWidget(w: Partial): Partial { return { - x: w.left, - y: w.top, + x: w.x ?? w.left, + y: w.y ?? w.top, width: 'width' in w ? w.width : (w as any).right - w.left, height: 'height' in w ? w.height : (w as any).bottom - w.top }; From 44551717347b1e424e8b1ab0570e0db763c3cb55 Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Mon, 26 Aug 2024 21:17:50 +0800 Subject: [PATCH 7/7] fix: fix the bugs of lint --- .../vstory/src/edit/edit-component/index.ts | 2 -- .../processor/chart/rankingBar/rankingBar.ts | 4 ++- .../character/chart/graphic/vchart-graphic.ts | 4 +-- .../character/chart/temp/templates/bar.ts | 17 ++++++---- .../story/character/component/character.ts | 4 +-- .../src/story/character/runtime-interface.ts | 6 ++-- packages/vstory/src/story/story.ts | 4 +-- packages/vstory/src/story/utils/layout.ts | 7 ++-- .../vstory/src/story/utils/vchart-pick.ts | 34 +++++++++++++------ 9 files changed, 51 insertions(+), 31 deletions(-) diff --git a/packages/vstory/src/edit/edit-component/index.ts b/packages/vstory/src/edit/edit-component/index.ts index 4bacb7a..ed30aa5 100644 --- a/packages/vstory/src/edit/edit-component/index.ts +++ b/packages/vstory/src/edit/edit-component/index.ts @@ -1,8 +1,6 @@ // import { CommonEditComponent } from './common'; import { BoxSelection } from './box-selection'; import { ImageSelection } from './image-selection'; -import { TextSelection } from './text-selection'; -import { RichTextSelection } from './richtext-selection'; import { RectSelection } from './rect-selection'; import { ChartSelection } from './chart-selection'; import { Edit } from '../edit'; diff --git a/packages/vstory/src/player/processor/chart/rankingBar/rankingBar.ts b/packages/vstory/src/player/processor/chart/rankingBar/rankingBar.ts index d2b7065..9be0f9d 100644 --- a/packages/vstory/src/player/processor/chart/rankingBar/rankingBar.ts +++ b/packages/vstory/src/player/processor/chart/rankingBar/rankingBar.ts @@ -8,7 +8,8 @@ import { isValid } from '@visactor/vutils'; import type { IAction, IActionPayload } from '../../interface/common-action'; // TODO: move to interface folder -export type IRankingBarPlayPayload = Omit; +// export type IRankingBarPlayPayload = Omit; +export type IRankingBarPlayPayload = IActionPayload; export interface IRankingBarPlayAction extends IAction { action: 'play'; @@ -54,6 +55,7 @@ export class RankingBarPlayActionProcessor extends ActionProcessorItem { getStartTimeAndDuration(action: IActionSpec, character?: ICharacter): { startTime: number; duration: number } { const { startTime: globalStartTime = 0 } = action; + // @ts-ignore const { startTime = 0 } = action.payload?.animation ?? ({} as any); const instance = (character.graphic as any)._vchart as IVChart; const interval = instance?.getSpec()?.interval ?? 0; diff --git a/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts b/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts index 8d613af..a75404c 100644 --- a/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts +++ b/packages/vstory/src/story/character/chart/graphic/vchart-graphic.ts @@ -1,5 +1,5 @@ import type { IVisactorGraphic } from '../../visactor/interface'; -import { Bounds, type AABBBounds, type IBoundsLike } from '@visactor/vutils'; +import { Bounds, type AABBBounds, type IAABBBounds, type IBoundsLike } from '@visactor/vutils'; import type { ISpec, IVChart } from '@visactor/vchart'; import type { GraphicType, IGroupGraphicAttribute, ITicker } from '@visactor/vrender'; import { genNumberType, Group } from '@visactor/vrender'; @@ -55,7 +55,7 @@ export class Chart extends Group implements IVisactorGraphic { return stage.defaultLayer.getChildByName('root').AABBBounds.clone(); } - doUpdateAABBBounds(full?: boolean): AABBBounds { + doUpdateAABBBounds(full?: boolean): IAABBBounds { if (!this._vchart) { return super.doUpdateAABBBounds(); } diff --git a/packages/vstory/src/story/character/chart/temp/templates/bar.ts b/packages/vstory/src/story/character/chart/temp/templates/bar.ts index a2b6627..f61addf 100644 --- a/packages/vstory/src/story/character/chart/temp/templates/bar.ts +++ b/packages/vstory/src/story/character/chart/temp/templates/bar.ts @@ -1,10 +1,16 @@ -import { ICharacter } from './../../../runtime-interface'; import { TemplateChartType } from '../constant'; -import { CartesianSingleSeriesTemp } from './cartesian-single'; import type { CharacterChart } from '../../character'; +import { BaseTemp } from './base-temp'; +import type { StandardData, DataInfo } from '../../data/interface'; -export class BarTemp extends CartesianSingleSeriesTemp { - static type: string = TemplateChartType.bar; +export class BarTemp extends BaseTemp { + getSpec(data: StandardData, ctx: { character: CharacterChart }, opt?: any) { + throw new Error('Method not implemented.'); + } + checkDataEnable(data: StandardData, info: DataInfo, opt?: any): boolean { + throw new Error('Method not implemented.'); + } + static type: string = TemplateChartType.vchart; type: string = BarTemp.type; // 唯一系列类型 seriesType = 'bar'; @@ -14,8 +20,7 @@ export class BarTemp extends CartesianSingleSeriesTemp { protected _getSeriesSpec() { return { type: 'bar', - stack: true, - direction: this.direction + stack: true }; } diff --git a/packages/vstory/src/story/character/component/character.ts b/packages/vstory/src/story/character/component/character.ts index 871caa7..698a7b9 100644 --- a/packages/vstory/src/story/character/component/character.ts +++ b/packages/vstory/src/story/character/component/character.ts @@ -1,5 +1,5 @@ +import type { IBoundsLike } from '@visactor/vutils'; import type { IGroup } from '@visactor/vrender'; -import { createGroup } from '@visactor/vrender'; import { GraphicBaseText } from './graphic/graphic-base-text'; import type { IComponentCharacterSpec } from '../dsl-interface'; import { CharacterBase } from '../base/base'; @@ -99,7 +99,7 @@ export abstract class CharacterComponent extends CharacterBase { } getLayoutBounds() { - return this._group.AABBBounds; + return this._group.AABBBounds as IBoundsLike; } checkEvent(event: StoryEvent): false | ICharacterPickInfo { diff --git a/packages/vstory/src/story/character/runtime-interface.ts b/packages/vstory/src/story/character/runtime-interface.ts index 25eb7e9..7c046ab 100644 --- a/packages/vstory/src/story/character/runtime-interface.ts +++ b/packages/vstory/src/story/character/runtime-interface.ts @@ -6,9 +6,9 @@ import type { Graphic } from './component/graphic/graphic'; export interface ICharacterPickInfo { part: string; - graphic: IGraphic; - graphicType: string; - modelInfo: any; + graphic?: IGraphic; + graphicType?: string; + modelInfo?: any; } export interface ICharacter { id: string; diff --git a/packages/vstory/src/story/story.ts b/packages/vstory/src/story/story.ts index 611fe12..bf49fad 100644 --- a/packages/vstory/src/story/story.ts +++ b/packages/vstory/src/story/story.ts @@ -43,11 +43,11 @@ export class Story implements IStory { this._player = new Player(this, option.playerOption); this._characterTree = new CharacterTree(this); - this._spec = spec; - this._spec && this.load(this._spec); + spec && this.load(this._spec); } load(spec: IStorySpec) { + this._spec = spec; this._characterTree.initCharacters(spec.characters); this._player.initActs(spec.acts); } diff --git a/packages/vstory/src/story/utils/layout.ts b/packages/vstory/src/story/utils/layout.ts index 210a998..ec4e573 100644 --- a/packages/vstory/src/story/utils/layout.ts +++ b/packages/vstory/src/story/utils/layout.ts @@ -1,9 +1,10 @@ +import type { IRect } from '../../type/space'; import type { ILayoutAttribute, IWidgetData } from '../character'; -export function getLayoutFromWidget(w: Partial): Partial { +export function getLayoutFromWidget(w: Partial | IRect): Partial { return { - x: w.x ?? w.left, - y: w.y ?? w.top, + x: 'x' in w ? w.x : w.left, + y: 'y' in w ? w.y : w.top, width: 'width' in w ? w.width : (w as any).right - w.left, height: 'height' in w ? w.height : (w as any).bottom - w.top }; diff --git a/packages/vstory/src/story/utils/vchart-pick.ts b/packages/vstory/src/story/utils/vchart-pick.ts index 0adaa6e..fc96f16 100644 --- a/packages/vstory/src/story/utils/vchart-pick.ts +++ b/packages/vstory/src/story/utils/vchart-pick.ts @@ -1,7 +1,16 @@ import type { IGraphic, IGraphicAttribute } from '@visactor/vrender'; -import type { VChart } from '@visactor/vchart'; +import type { IVChart } from '@visactor/vchart'; import type { StoryEvent } from '../interface/runtime-interface'; +export interface IPickModelInfo { + type: string; + model: any; + specKey: string; + specIndex: number; + datum?: any; + mark?: any; +} + function commonModelInfo(model: any) { return { type: model.type, @@ -15,7 +24,7 @@ export const seriesMarkPick = { check: (graphic: IGraphic, graphicPath: IGraphic[]) => { return graphic.name?.startsWith('seriesGroup_'); }, - modelInfo: (chart: VChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) => { + modelInfo: (chart: IVChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) => { const nameInfo = graphic.name.split('_'); const seriesId = +nameInfo[2]; const markGraphic = graphicPath[index + 1]; @@ -37,7 +46,7 @@ export const axisMarkPick = { check: (graphic: IGraphic, graphicPath: IGraphic[]) => { return graphic.name === 'axis' || graphic.name === 'axis-grid'; }, - modelInfo: (chart: VChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) => { + modelInfo: (chart: IVChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) => { const axisGroup = graphicPath[index - 1]; const axisId = +axisGroup.name.split('_')[1]; const axis = chart @@ -60,8 +69,8 @@ export const markerMarkPick = { check: (graphic: IGraphic, graphicPath: IGraphic[]) => { return !!MarkerClassName[graphic.constructor.name]; }, - modelInfo: (chart: VChart, graphic: IGraphic, graphicPath: IGraphic[]) => { - const markerId = +graphic.id.split('-')[1]; + modelInfo: (chart: IVChart, graphic: IGraphic, graphicPath: IGraphic[]) => { + const markerId = +(graphic.id).split('-')[1]; const model = chart .getChart() .getAllComponents() @@ -75,7 +84,7 @@ export const labelMarkPick = { check: (graphic: IGraphic, graphicPath: IGraphic[]) => { return graphic.name === 'data-label'; }, - modelInfo: (chart: VChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) => { + modelInfo: (chart: IVChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) => { const id = +graphicPath[index - 1].name.split('_')[1]; const model = chart .getChart() @@ -97,7 +106,7 @@ function commonModePick(vrenderGraphicClassName: string, modelName: string) { check: (graphic: IGraphic, graphicPath: IGraphic[]) => { return graphic.constructor.name === vrenderGraphicClassName; }, - modelInfo: (chart: VChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) => { + modelInfo: (chart: IVChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) => { return commonModelInfo( chart .getChart() @@ -119,7 +128,7 @@ export const discretePlayerMarkPick = commonModePick('DiscretePlayer', 'player') const modelCheck: { check: (graphic: IGraphic, graphicPath: IGraphic[]) => boolean; - modelInfo: (chart: VChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) => any; + modelInfo: (chart: IVChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) => any; }[] = [ seriesMarkPick, axisMarkPick, @@ -138,7 +147,7 @@ const modelCheck: { * 从event属性上,读取当前pick到的图表模块内容 * @param event */ -export function getChartModelWithEvent(chart: VChart, event: StoryEvent) { +export function getChartModelWithEvent(chart: IVChart, event: StoryEvent) { const graphicPath = event.detailPath[event.detailPath.length - 1] as unknown as IGraphic< Partial >[]; @@ -152,7 +161,12 @@ export function getChartModelWithEvent(chart: VChart, event: StoryEvent) { return getGraphicModelMark(chart, pickGraphic, graphicPath, 0); } -export function getGraphicModelMark(chart: VChart, graphic: IGraphic, graphicPath: IGraphic[], index: number) { +export function getGraphicModelMark( + chart: IVChart, + graphic: IGraphic, + graphicPath: IGraphic[], + index: number +): IPickModelInfo { const modelPick = modelCheck.find(mc => mc.check(graphic, graphicPath)); if (modelPick) { return modelPick.modelInfo(chart, graphic, graphicPath, index);