Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "feat: support vchart rotate plugin\n\n",
"type": "none",
"packageName": "@visactor/vchart"
}
],
"packageName": "@visactor/vchart",
"email": "lixuef1313@163.com"
}
9 changes: 9 additions & 0 deletions packages/vchart/src/core/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type { IBaseTriggerOptions, ITriggerConstructor } from '../interaction/in
import type { IComposedEventConstructor } from '../index-harmony-simple';
import type { ITooltipProcessorConstructor } from '../component/tooltip/processor/interface';
import type { ITooltip } from '../component';
import type { IVChartPluginConstructor } from '../plugin/vchart';

export class Factory {
private static _charts: { [key: string]: IChartConstructor } = {};
Expand All @@ -42,6 +43,7 @@ export class Factory {
private static _animations: { [key: string]: (params?: any, preset?: any) => MarkAnimationSpec } = {};
private static _implements: { [key: string]: (...args: any) => void } = {};
private static _chartPlugin: { [key: string]: IChartPluginConstructor } = {};
private static _vChartPlugin: { [key: string]: IVChartPluginConstructor } = {};
private static _componentPlugin: { [key: string]: IComponentPluginConstructor } = {};
private static _formatter: (
text: string | number | string[] | number[],
Expand Down Expand Up @@ -117,6 +119,9 @@ export class Factory {
static registerChartPlugin(key: string, plugin: IChartPluginConstructor) {
Factory._chartPlugin[key] = plugin;
}
static registerVChartPlugin(key: string, plugin: IVChartPluginConstructor) {
Factory._vChartPlugin[key] = plugin;
}
static registerComponentPlugin(key: string, plugin: IComponentPluginConstructor) {
Factory._componentPlugin[key] = plugin;
}
Expand Down Expand Up @@ -269,6 +274,10 @@ export class Factory {
return Object.values(Factory._chartPlugin);
}

static getVChartPlugins() {
return Object.values(Factory._vChartPlugin);
}

static getComponentPlugins() {
return Object.values(Factory._componentPlugin);
}
Expand Down
6 changes: 6 additions & 0 deletions packages/vchart/src/core/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,12 @@ export interface IVChart {

/** 获取当前容器宽高 */
getCurrentSize: () => IContainerSize;

/** 旋转图表 需要注册插件 */
rotate90WithTransform?: (rotateDom: HTMLElement) => void;

/** 取消图表旋转 */
cancelTransform?: (rotateDom: HTMLElement) => void;
}

export interface IGlobalConfig {
Expand Down
12 changes: 12 additions & 0 deletions packages/vchart/src/core/vchart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ import type { IGeoCoordinate } from '../component/geo';
import { registerGesturePlugin } from '../plugin/other';
import { registerElementHighlight } from '../interaction/triggers/element-highlight';
import { registerElementSelect } from '../interaction/triggers/element-select';
import type { IVChartPluginService } from '../plugin/vchart/interface';
import { VChartPluginService } from '../plugin/vchart/plugin-service';

export class VChart implements IVChart {
readonly id = createID();
Expand Down Expand Up @@ -357,6 +359,8 @@ export class VChart implements IVChart {
private _isReleased: boolean;

private _chartPlugin?: IChartPluginService;
private _vChartPlugin?: IVChartPluginService;

private _onResize?: () => void;

constructor(spec: ISpec, options: IInitOption) {
Expand Down Expand Up @@ -2148,6 +2152,14 @@ export class VChart implements IVChart {
// 插件生命周期
this._chartPluginApply('onInit', this._spec);
}

const vChartPluginList = Factory.getVChartPlugins();
if (vChartPluginList.length > 0) {
this._vChartPlugin = new VChartPluginService(this);
this._vChartPlugin.load(vChartPluginList.map(p => new p()));
// 插件生命周期
this._vChartPlugin.onInit();
}
}

private _chartPluginApply(funcName: keyof IChartPluginService, ...args: any[]) {
Expand Down
2 changes: 2 additions & 0 deletions packages/vchart/src/plugin/vchart/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './register';
export * from './interface';
19 changes: 19 additions & 0 deletions packages/vchart/src/plugin/vchart/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { IVChart } from '../../core/interface';
import type { IBasePlugin, IBasePluginService, MaybePromise } from '../base/interface';

export interface IVChartPlugin<T extends IVChartPluginService = any> extends IBasePlugin<T> {
specKey?: string;
onInit?: (service: T) => MaybePromise<void>;
}

export interface IVChartPluginConstructor {
readonly pluginType: 'vchart';
readonly specKey?: string;
readonly type: string;
new (): IVChartPlugin;
}

export interface IVChartPluginService<T extends IVChartPlugin = any> extends IBasePluginService<T> {
globalInstance: IVChart;
onInit?: () => MaybePromise<void>;
}
26 changes: 26 additions & 0 deletions packages/vchart/src/plugin/vchart/plugin-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { IVChartPlugin, IVChartPluginService } from './interface';
import type { IVChart } from '../../core';
import { BasePluginService } from '../base/base-plugin-service';

export class VChartPluginService<T extends IVChartPlugin = IVChartPlugin>
extends BasePluginService<T>
implements IVChartPluginService<T>
{
globalInstance: IVChart;

constructor(globalInstance: IVChart) {
super();
this.globalInstance = globalInstance;
}

onInit() {
this._plugins.forEach(plugin => {
plugin.onInit && plugin.onInit(this);
});
}

releaseAll(): void {
super.releaseAll();
this.globalInstance = null;
}
}
6 changes: 6 additions & 0 deletions packages/vchart/src/plugin/vchart/register.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Factory } from '../../core/factory';
import type { IVChartPluginConstructor } from './interface';

export const registerVChartPlugin = (plugin: IVChartPluginConstructor) => {
Factory.registerVChartPlugin(plugin.type, plugin);
};
1 change: 1 addition & 0 deletions packages/vchart/src/plugin/vchart/rotate/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './rotate';
158 changes: 158 additions & 0 deletions packages/vchart/src/plugin/vchart/rotate/rotate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import type { IAABBBounds, Matrix } from '@visactor/vutils';
import { BasePlugin } from '../../base/base-plugin';
import type { IVChartPlugin, IVChartPluginService } from '../interface';
import { registerVChartPlugin } from '../register';
import type { IVChart } from '../../../core/interface';
import {
matrixAllocate,
transformPointForCanvas,
mapToCanvasPointForCanvas,
registerGlobalEventTransformer,
registerWindowEventTransformer,
vglobal
} from '@visactor/vrender-core';

export class RotatePlugin extends BasePlugin implements IVChartPlugin {
static readonly pluginType: 'vchart' = 'vchart';

static readonly specKey = 'rotate';

static readonly type: string = 'rotatePlugin';
readonly type: string = 'rotatePlugin';

private rotateDegree: number;
private matrix: Matrix;
private vglobal_mapToCanvasPoint: any; // 保存vrender中vglobal的mapToCanvasPoint原方法
private _vchart: IVChart;

constructor() {
super(RotatePlugin.type);
}

onInit(service: IVChartPluginService) {
const { globalInstance: vchart } = service;
if (!vchart) {
return;
}
this._vchart = vchart;
//将函数rotate90WithTransform绑定到table实例上,一般情况下插件不需要将api绑定到table实例上,可以直接自身实现某个api功能
vchart.rotate90WithTransform = this.rotate90WithTransform;
vchart.cancelTransform = this.cancelTransform;
}

rotate90WithTransform = (rotateDom: HTMLElement) => {
this.rotateDegree = 90;
const rotateCenter =
rotateDom.clientWidth < rotateDom.clientHeight
? Math.max(rotateDom.clientWidth, rotateDom.clientHeight) / 2
: Math.min(rotateDom.clientWidth, rotateDom.clientHeight) / 2;
const domRect = this._vchart.getContainer().getBoundingClientRect();
const x1 = domRect.left;
const y1 = domRect.top;
const x2 = domRect.right;
const y2 = domRect.bottom;

rotateDom.style.transform = 'rotate(90deg)';
rotateDom.style.transformOrigin = `${rotateCenter}px ${rotateCenter}px`;
const getRect = () => {
return {
x1,
y1,
x2,
y2
} as IAABBBounds;
};
// 获取视口尺寸的通用方法
const getViewportDimensions = () => {
// 浏览器环境
if (typeof window !== 'undefined') {
return {
width: window.innerWidth || document.documentElement.clientWidth,
height: window.innerHeight || document.documentElement.clientHeight
};
}
// 如果有 vglobal 上的方法可以使用
if (vglobal && 'getViewportSize' in vglobal && vglobal.getViewportSize) {
// @ts-ignore
return vglobal.getViewportSize();
}
// 默认使用容器的尺寸
return rotateDom.getBoundingClientRect();
};

const getMatrix = () => {
const viewPortWidth = getViewportDimensions().width; //获取整个视口的尺寸
const domRect = this._vchart.getContainer().getBoundingClientRect(); //TODO 这个地方应该获取窗口的宽高 最好能从vglobal上直接获取
const x1 = domRect.top;
const y1 = viewPortWidth - domRect.right;

const matrix = matrixAllocate.allocate(1, 0, 0, 1, 0, 0);
matrix.translate(x1, y1);
const centerX = rotateCenter - x1;
const centerY = rotateCenter - y1;
matrix.translate(centerX, centerY);
matrix.rotate(Math.PI / 2);
matrix.translate(-centerX, -centerY);
this.matrix = matrix;
return matrix;
};
registerGlobalEventTransformer(vglobal, this._vchart.getContainer(), getMatrix, getRect, transformPointForCanvas);
registerWindowEventTransformer(
this._vchart.getStage().window,
this._vchart.getContainer(),
getMatrix,
getRect,
transformPointForCanvas
);
this.vglobal_mapToCanvasPoint = vglobal.mapToCanvasPoint;
vglobal.mapToCanvasPoint = mapToCanvasPointForCanvas;
//transformPointForCanvas和mapToCanvasPointForCanvas时相对应的
//具体逻辑在 VRender/packages/vrender-core/src/common/event-transformer.ts中
// 可以自定义这两个函数 来修改事件属性,transformPointForCanvas中将坐标转换后存放了_canvasX _canvasY,mapToCanvasPointForCanvas中加以利用
// 在VTable的touch文件中,利用到了_canvasX _canvasY 所以如果自定义上面两个函数也需提供_canvasX _canvasY
};
cancelTransform = (rotateDom: HTMLElement) => {
this.rotateDegree = 0;
rotateDom.style.transform = 'none';
rotateDom.style.transformOrigin = 'none';
const domRect = this._vchart.getContainer().getBoundingClientRect();
const x1 = domRect.left;
const y1 = domRect.top;
const x2 = domRect.right;
const y2 = domRect.bottom;

const getRect = () => {
return {
x1,
y1,
x2,
y2
} as IAABBBounds;
};
const getMatrix = () => {
const matrix = matrixAllocate.allocate(1, 0, 0, 1, 0, 0);
matrix.translate(x1, y1);
return matrix;
};
registerGlobalEventTransformer(vglobal, this._vchart.getContainer(), getMatrix, getRect, transformPointForCanvas);
registerWindowEventTransformer(
this._vchart.getStage().window,
this._vchart.getContainer(),
getMatrix,
getRect,
transformPointForCanvas
);
vglobal.mapToCanvasPoint = this.vglobal_mapToCanvasPoint;
};

release() {
this._vchart = null;
this.vglobal_mapToCanvasPoint = null;
this.matrix = null;
super.release();
}
}

export const registerRotatePlugin = () => {
registerVChartPlugin(RotatePlugin);
};
Loading