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.
-
Component Interface
G2 是由一个 Runtime 和一系列可视化组件构成,为了架构的统一性,这些可视化组件的接口需要保持一致。在之前的设计中,组件接口的设计思路如下:
这样的好处是对 tranform 和 data.transform 这种需要复合的组件友好:可以减少最后合成函数的参数数量,但是对于不需要复合的组件会造成没有必要的函数嵌套,从而增加复杂性。所以这里提出一种更加简单和通用的接口。
开始使用
这里使用一个复合 Mark 的例子。
实现思路
所有组件的接口都统一成如下形式:
Beta Was this translation helpful? Give feedback.
All reactions