Replies: 11 comments
-
Data
export function Sort(options) {
return data => data.sort();
} |
Beta Was this translation helpful? Give feedback.
-
Encode
export function Encode(options) {
const { value } = options;
return data => data.map(value);
} |
Beta Was this translation helpful? Give feedback.
-
Transform
// transform/stackY
export function StackY(options) {
return (I, mark) => {};
}
|
Beta Was this translation helpful? Give feedback.
-
Scale (Change)之前 Scale 推断的逻辑是在 runtime 里面的,现在把这个逻辑分散到各个 Scale 里面本身。 export function Linear(options, context) {
const { domain } = options;
const { values, coordinate } = context;
// ....
return new Linear();
} |
Beta Was this translation helpful? Give feedback.
-
Shape (Change)value 和 theme 在 mark 那里消费掉,都变成 style,传给 shape 到参数更少,同时传入 document 去创建图形。
下面是一个使用 G 内置图形创建 shape 的例子。 // shape/point/color.ts
export function Color(context) {
const { coordinate } = context;
return {
create: 'point', // 内置图形
style: (points, value, parsedStyle, defaults) => {
return getStyles(points, coordinate, parsedStyle, defaults);
},
marker: () => {},
};
} 下面是一个使用 G 自定义机制创建图形的例子。 // shape/area/color.ts
// 自定义图形,返回的 create 会被转换成 G 的 CustomComponent
// 每次 shape.style.attr = value 的时候会调用这个方法
// 所以这个方法不进行复杂的计算操作
// 只是用于组装图形
// G 内部会把合并绘制命令
export function Color(options, context) {
const { coordinate, document } = context;
return {
create: (g) => {
const { areaStyle, connectStyle } = g.attributes;
// 如果第一次就创建并且挂载新图形
const {
area = document.createElement('path'),
connect = document.createElement('path'),
} = g;
area.attrs(areaStyle);
connect.attrs(connectStyle);
if (!g.area) (g.area = area), g.appendChild(area);
if (!g.connect) (g.connect = connect), g.appendChild(connect);
},
name: 'doubleArea',
style: (points, value, parsedStyle) => {
const defaults = getTheme(theme, value);
return getStyles(points, coordinate, parsedStyle, defaults);
},
marker: () => {},
};
} |
Beta Was this translation helpful? Give feedback.
-
Basic Mark (Change)所有的 Mark 都有如下的函数签名,除了 Composite Mark 之外。 对于 Mark 来讲有两个很关键的点:分层渲染和自定义渲染。 // 普通 Mark
export function Point(options, context) {
const { theme, key, library } = context;
return {
create: () => { // 处理数据,然后交给 runtime 去创建比例尺。
const transform = useTransform(library, options);
return appleyTransform(transform);
},
render: (root, I, value, scale, coordinate, dimension) => {
const container = root.getElementById('key');
const [P, NI] = calcPoints(I, value, scale, coordinate);
const visualData = createVisualData(NI);
const shape = useShape(library, options);
const animate = useAnimate(library, options);
const transitions = select(container)
.selectAll('.element')
.data(visualData, (d) => d.key)
.join(
(enter) =>
enter
.each(applyShape(P, shape, theme))
.each(applyAnimate(animate, theme)),
(update) =>
update
.each(applyShape(shape, theme))
.each(applyAnimate(animate, theme)),
(exit) => exit.remove(),
)
.transitions();
return transitions;
},
};
}
Point.props = {
position: 'center',
} |
Beta Was this translation helpful? Give feedback.
-
Composite Mark (Change)export function forceGraph(options, context) {
const { data } = options;
const { nodes, links } = d3.hierarchy(data);
// 一直改变 nodes 和 links 的值
const simulation = d3.forceSimulation(nodes);
// Point 的出场动画
const EnterPoint = (options, { from }) => {
const [shape] = from;
const { index, key } = shape.__data__;
return new Promise((resolve) => {
simulation.on(`tick.${key}`, () => {
const d = nodes[index];
shape.style.x = d.x;
shape.style.y = d.y;
});
simulation.on(`end.${key}`, resolve);
});
};
// Link 的出场动画
const EnterLink = (options, { from }) => {
const [shape] = from;
const { index, key } = shape.__data__;
return new Promise((resolve) => {
simulation.on(`tick.${key}`, () => {
const d = nodes[index];
shape.style.x1 = d.source.x;
shape.style.y1 = d.source.y;
shape.style.x2 = d.target.x;
shape.style.y2 = d.target.y;
});
simulation.on(`end.${key}`, resolve);
});
};
// Drag 交互
function DragNode() {
return (container) => {
const links = container.getElementByClassName('link');
const nodes = container.getElementByClassName('nodes');
// 清空之前的事件处理,并且添加新的事件处理
simulation.on('*', null);
simulation.on('tick', () => {
updateNodes(nodes);
updateLinks(links);
});
const dragstart = () => {
simulation.alphaTarget(0.3).restart();
};
const dragend = () => {
simulation.alphaTarget(0);
};
container.addEventListener('dragstart', dragstart);
container.addEventListener('dragend', dragend);
};
}
return [
{
type: 'point',
data: nodes,
animate: { enter: { type: EnterPoint } },
interaction: { drag: { type: DragNode } },
},
{ type: 'link', data: links, animate: { enter: { type: EnterLink } } },
];
} |
Beta Was this translation helpful? Give feedback.
-
Rendering Pipeline图表中的可见元素,全部都变成 Mark,包括 Component,Label 这些都是 Mark。 const spec = {
type: 'interval',
data: [
{ genre: 'Sports', sold: 275 },
{ genre: 'Strategy', sold: 115 },
{ genre: 'Action', sold: 120 },
{ genre: 'Shooter', sold: 350 },
{ genre: 'Other', sold: 150 },
],
encode: {
x: 'genre',
y: 'sold',
},
label: [{ text: 'sold' }, { text: 'sold' }],
}; const actualSpec = {
type: 'view',
key: '0',
children: [
{
type: 'interval',
key: '0-1',
encode: {
x: 'genre',
y: 'sold',
},
data: [
{ genre: 'Sports', sold: 275 },
{ genre: 'Strategy', sold: 115 },
{ genre: 'Action', sold: 120 },
{ genre: 'Shooter', sold: 350 },
{ genre: 'Other', sold: 150 },
],
},
{ type: 'axis', key: '0-2' },
{ type: 'axis', key: '0-3' },
{ type: 'legend', key: '0-4' },
{ type: 'label', key: '0-5', from: '0-1', text: 'sold' },
{ type: 'label', key: '0-6', from: '0-1', text: 'sold' },
],
}; function render() {
// ...
for (const mark of marks) {
mark.render(container, I, value, scale, coordinate, dimension);
}
// ...
} |
Beta Was this translation helpful? Give feedback.
-
Derived Mark (Change)通过 Basic Mark 派生出的 Mark,比如 Label 和 Background。这一系列 Mark 的特点是定位相对于它的父亲。 export function Label(options, context) {
const { theme, library } = context;
const { from } = options;
return {
create: () => {
// 处理数据,然后交给 runtime 去创建比例尺。
const transform = useTransform(library, options);
return appleyTransform(transform);
},
render: (container, I, value, scale, coordinate, dimension) => {
// 找到相同的 Mark 并且获得相应的位置数据
const view = container.parentNode;
const group = view.getElementById(from);
const elements = group.getElementByClassName('element');
const data = elements.map(dataOf(options));
// 和 Mark 相同的渲染流程
const shape = useShape(library, options);
const animate = useAnimate(library, options);
container
.selectAll('.element')
.data(data, (d) => d.key)
.join(
(enter) =>
enter
.attr('class', 'element label')
.each(applyShape(shape, theme))
.each(applyAnimate(animate, theme)),
(update) =>
update
.each(applyShape(shape, theme))
.each(applyAnimate(animate, theme)),
(exit) => exit.remove(),
)
.transitions();
},
};
} |
Beta Was this translation helpful? Give feedback.
-
Static Mark, Component (Change)function Axis() {
return {
create: () => {
// 根据 coordinate 初始化一些信息
},
render: (container, I, value, scale, coordinate, dimension) => {
// 渲染包括更新
},
};
} |
Beta Was this translation helpful? Give feedback.
-
组件的自定义:
相关内容的文档、demo。 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Component Interface
G2 是由一个 Runtime 和一系列可视化组件构成,为了架构的统一性,这些可视化组件的接口需要保持一致。在之前的设计中,组件接口的设计思路如下:
这样的好处是对 tranform 和 data.transform 这种需要复合的组件友好:可以减少最后合成函数的参数数量,但是对于不需要复合的组件会造成没有必要的函数嵌套,从而增加复杂性。所以这里提出一种更加简单和通用的接口。
开始使用
这里使用一个复合 Mark 的例子。
实现思路
所有组件的接口都统一成如下形式:
Beta Was this translation helpful? Give feedback.
All reactions