Skip to content

Commit

Permalink
feat(data-transform): pass createCanvas
Browse files Browse the repository at this point in the history
  • Loading branch information
pearmini committed Jul 11, 2024
1 parent 921e242 commit a169d5c
Show file tree
Hide file tree
Showing 10 changed files with 57 additions and 31 deletions.
14 changes: 6 additions & 8 deletions __tests__/plots/api/chart-word-cloud-canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,23 @@ import { Chart } from '../../../src';
export function chartWordCloudCanvas(context) {
const { container, canvas } = context;

const ref = { called: false };

const chart = new Chart({
container,
canvas,
createCanvas: () => {
ref.called = true;
return document.createElement('canvas');
},
});

const ref = { called: false };

chart.options({
type: 'wordCloud',
data: {
type: 'fetch',
value: 'data/philosophyWord.json',
},
layout: {
canvas: () => {
ref.called = true;
return document.createElement('canvas');
},
},
});

const finished = chart.render();
Expand Down
15 changes: 7 additions & 8 deletions site/docs/spec/mark/wordcloud.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,13 @@ chart.render();

## layout

| 属性 | 描述 | 类型 | 默认值 |
| --------- | ------------- | -------------------------------- | ---------------------------------------- |
| padding | 内间距 | `number` | `1` |
| rotate | 文字旋转角度 | `number \| (word => number)` | - |
| random | 随机方式 | `number \| (word => number)` | - |
| spiral | 外观图形 | `'archimedean' \| 'rectangular'` | - |
| imageMask | 图片蒙层 | `'HTMLImageElement \| string` | - |
| canvas | 自定义 canvas | `() => CanvasElement` | `() => document.createElement('canvas')` |
| 属性 | 描述 | 类型 | 默认值 |
| --------- | ------------ | -------------------------------- | ------ |
| padding | 内间距 | `number` | `1` |
| rotate | 文字旋转角度 | `number \| (word => number)` | - |
| random | 随机方式 | `number \| (word => number)` | - |
| spiral | 外观图形 | `'archimedean' \| 'rectangular'` | - |
| imageMask | 图片蒙层 | `'HTMLImageElement \| string` | - |

## style

Expand Down
5 changes: 4 additions & 1 deletion src/api/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export type RuntimeOptions = ViewComposition & {
plugins?: RendererPlugin[];
theme?: string;
lib?: G2Library;
createCanvas?: () => HTMLCanvasElement;
};

export class Runtime<Spec extends G2Spec = G2Spec> extends CompositionNode {
Expand All @@ -53,7 +54,8 @@ export class Runtime<Spec extends G2Spec = G2Spec> extends CompositionNode {
private _compositions: Record<string, new () => Node>;

constructor(options: RuntimeOptions) {
const { container, canvas, renderer, plugins, lib, ...rest } = options;
const { container, canvas, renderer, plugins, lib, createCanvas, ...rest } =
options;
super(rest, 'view');
this._renderer = renderer || new CanvasRenderer();
this._plugins = plugins || [];
Expand All @@ -63,6 +65,7 @@ export class Runtime<Spec extends G2Spec = G2Spec> extends CompositionNode {
library: { ...lib, ...library },
emitter: this._emitter,
canvas,
createCanvas,
};
this._create();
}
Expand Down
6 changes: 4 additions & 2 deletions src/data/wordCloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,11 @@ export function normalizeFontSize(fontSize: any, range?: [number, number]) {
return () => fontSize;
}

export const WordCloud: DC<Partial<WordCloudOptions>> = (options) => {
export const WordCloud: DC<Partial<WordCloudOptions>> = (options, context) => {
return async (data) => {
const cloudOptions = Object.assign({}, DEFAULT_OPTIONS, options);
const cloudOptions = Object.assign({}, DEFAULT_OPTIONS, options, {
canvas: context.createCanvas,
});
const layout = tagCloud();

await flow(layout, cloudOptions)
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/mark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ export async function initializeMark(
partialMark: G2Mark,
partialProps: MarkProps,
library: G2Library,
createCanvas: () => HTMLCanvasElement,
): Promise<[G2Mark, G2MarkState]> {
// Apply transform to mark to derive indices, data, encode, etc,.
const context = { library };
const context = { library, createCanvas };
const [I, transformedMark] = await applyMarkTransform(
partialMark,
partialProps,
Expand Down
37 changes: 28 additions & 9 deletions src/runtime/plot.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Vector2 } from '@antv/coord';
import { DisplayObject, IAnimation as GAnimation, Rect } from '@antv/g';
import { DisplayObject, IAnimation as GAnimation, HTML, Rect } from '@antv/g';
import { deepMix, upperFirst, isArray } from '@antv/util';
import { group, groups } from 'd3-array';
import { format } from 'd3-format';
Expand Down Expand Up @@ -122,6 +122,8 @@ export async function plot<T extends G2ViewTree>(
.filter(defined),
);

const { createCanvas } = context;

const typeOf = (node: G2ViewTree) => {
const { type } = node;
if (typeof type === 'function') {
Expand Down Expand Up @@ -168,7 +170,7 @@ export async function plot<T extends G2ViewTree>(
const state = nodeState.get(node);
const [view, children] = state
? initializeState(state, node, library)
: await initializeView(node, library);
: await initializeView(node, library, createCanvas);
viewNode.set(view, node);
views.push(view);

Expand All @@ -183,7 +185,9 @@ export async function plot<T extends G2ViewTree>(
// should sync position scales among facets normally.
if (transformedNodes.every(isStandardView)) {
const states = await Promise.all(
transformedNodes.map((d) => initializeMarks(d, library)),
transformedNodes.map((d) =>
initializeMarks(d, library, createCanvas),
),
);
// Note!!!
// This will mutate scales for marks.
Expand All @@ -198,7 +202,9 @@ export async function plot<T extends G2ViewTree>(
// Apply transform to get data in advance for non-mark composition
// node, which makes sure that composition node can preprocess the
// data to produce more nodes based on it.
const n = isMark(node) ? node : await applyTransform(node, library);
const n = isMark(node)
? node
: await applyTransform(node, library, createCanvas);
const N = transform(n);
if (Array.isArray(N)) discovered.push(...N);
else if (typeof N === 'function') nodeGenerators.push(N());
Expand Down Expand Up @@ -437,7 +443,11 @@ function createUpdateView(

return async (newOptions, source, callback) => {
const transitions = [];
const [newView, newChildren] = await initializeView(newOptions, library);
const [newView, newChildren] = await initializeView(
newOptions,
library,
context.createCanvas,
);
plotView(newView, selection, transitions, library, context);

// Update interaction need to reapply when update.
Expand Down Expand Up @@ -501,8 +511,9 @@ function updateInteraction(
async function initializeView(
options: G2View,
library: G2Library,
createCanvas: () => HTMLCanvasElement,
): Promise<[G2ViewDescriptor, G2ViewTree[]]> {
const flattenOptions = await transformMarks(options, library);
const flattenOptions = await transformMarks(options, library, createCanvas);

const mergedOptions = bubbleOptions(flattenOptions);

Expand Down Expand Up @@ -554,6 +565,7 @@ function bubbleOptions(options: G2View): G2View {
async function transformMarks(
options: G2View,
library: G2Library,
createCanvas: () => HTMLCanvasElement,
): Promise<G2View> {
const [useMark, createMark] = useLibrary<G2MarkOptions, MarkComponent, Mark>(
'mark',
Expand All @@ -576,7 +588,7 @@ async function transformMarks(
while (discovered.length) {
const [node] = discovered.splice(0, 1);
// Apply data transform to get data.
const mark = (await applyTransform(node, library)) as G2Mark;
const mark = (await applyTransform(node, library, createCanvas)) as G2Mark;
const { type = error('G2Mark type is required.'), key } = mark;

// For components.
Expand Down Expand Up @@ -608,6 +620,7 @@ async function transformMarks(
async function initializeMarks(
options: G2View,
library: G2Library,
createCanvas?: () => HTMLCanvasElement,
): Promise<Map<G2Mark, G2MarkState>> {
const [useTheme] = useLibrary<G2ThemeOptions, ThemeComponent, Theme>(
'theme',
Expand All @@ -630,7 +643,12 @@ async function initializeMarks(
for (const markOptions of partialMarks) {
const { type } = markOptions;
const { props = {} } = createMark(type);
const markAndState = await initializeMark(markOptions, props, library);
const markAndState = await initializeMark(
markOptions,
props,
library,
createCanvas,
);
if (markAndState) {
const [initializedMark, state] = markAndState;
markState.set(initializedMark, state);
Expand Down Expand Up @@ -1693,8 +1711,9 @@ function inferInteraction(
async function applyTransform<T extends G2ViewTree>(
node: T,
library: G2Library,
createCanvas: () => HTMLCanvasElement,
): Promise<G2ViewTree> {
const context = { library };
const context = { library, createCanvas };
const { data, ...rest } = node;
if (data == undefined) return node;
const [, { data: newData }] = await applyDataTransform([], { data }, context);
Expand Down
1 change: 1 addition & 0 deletions src/runtime/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export function render<T extends G2ViewTree = G2ViewTree>(
emitter = new EventEmitter(),
library,
} = context;

context.canvas = canvas;
context.emitter = emitter;

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export async function applyDataTransform(
const descriptor = normalizedDataSource(data);
const { transform: T = [], ...connector } = descriptor;
const transform = [connector, ...T];
const transformFunctions = transform.map(useData);
const transformFunctions = transform.map((t) => useData(t, context));
const transformedData = await composeAsync(transformFunctions)(data);

// Maintain the consistency of shape between input and output data.
Expand Down
4 changes: 3 additions & 1 deletion src/runtime/types/data.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { TransformContext } from './transform';

export type DataOptions = Record<string, any>;

export type DataProps = Record<string, never>;

export type DataComponent<O extends DataOptions = DataOptions> = {
(options?: O): Data;
(options?: O, context?: TransformContext): Data;
props: DataProps;
};

Expand Down
1 change: 1 addition & 0 deletions src/runtime/types/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { G2Library, G2Mark } from './options';

export type TransformContext = {
library: G2Library;
createCanvas: () => HTMLCanvasElement;
};

export type TransformOptions = Record<string, any>;
Expand Down

0 comments on commit a169d5c

Please sign in to comment.