Skip to content

Commit

Permalink
Circle packing (#2655)
Browse files Browse the repository at this point in the history
* feat(circle-packing): 初始化 circle-packing 图表

* feat: 优化circle packing

* test: 添加测试

* fix(circle-packing): 支持autoFit为true并修改默认配置项

* docs: 添加circle packing文档

* test(circle-packing): 增加animation和tooltip的单测

* fix(circle-packing): 修复packing下钻交互问题

Co-authored-by: visiky <736929286@qq.com>
Co-authored-by: 酥云 <lisuwen.lsw@antgroup.com>
  • Loading branch information
3 people authored Jun 29, 2021
1 parent 94bfdf0 commit ab4ce2b
Show file tree
Hide file tree
Showing 26 changed files with 1,529 additions and 6 deletions.
131 changes: 131 additions & 0 deletions __tests__/data/circle-packing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
export const DATA = {
name: 'root',
children: [
{
name: 'Drama',
value: 1046790,
},
{
name: 'Comedy',
value: 1039358,
},
{
name: 'Documentary',
value: 461880,
},
{
name: 'News',
value: 308136,
},
{
name: 'Talk-Show',
value: 270578,
},
{
name: 'Action',
value: 226334,
},
{
name: 'Animation',
value: 197342,
},
{
name: 'Reality-TV',
value: 189739,
},
{
name: 'Crime',
value: 175272,
},
{
name: 'Family',
value: 150621,
},
{
name: 'Short',
value: 138255,
},
{
name: 'Adventure',
value: 121216,
},
{
name: 'Game-Show',
value: 119912,
},
{
name: 'Music',
value: 102488,
},
{
name: 'Adult',
value: 90157,
},
{
name: 'Biography',
value: 59307,
},
{
name: 'Sport',
value: 58999,
},
{
name: 'Romance',
value: 52776,
},
{
name: 'Horror',
value: 50800,
},
{
name: 'Fantasy',
value: 22614,
},
{
name: 'Sci-Fi',
value: 22026,
},
{
name: 'Thriller',
value: 19706,
},
{
name: 'Mystery',
value: 18274,
},
{
name: 'History',
value: 16108,
},
{
name: 'Western',
value: 12535,
},
{
name: 'Musical',
value: 12240,
},
{
name: 'War',
value: 1992,
},
{
name: 'Film-Noir',
value: 12240 + 1992 + 1992,
children: [
{
name: 'Musical',
value: 12240,
},
{
name: 'War',
value: 1992,
},
{
name: 'Film',
value: 1992,
},
],
},
],
};
89 changes: 89 additions & 0 deletions __tests__/unit/plots/circle-packing/animation-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { CirclePacking } from '../../../../src';
import { createDiv } from '../../../utils/dom';
import { DATA } from '../../../data/circle-packing';

describe('Circle-Packing', () => {
const div = createDiv();
const plot = new CirclePacking(div, {
autoFit: true,
padding: 0,
data: DATA,
animation: {
appear: {
animation: 'zoom-in',
duration: 500,
},
leave: {
animation: 'zoom-out',
duration: 500,
},
},
});
plot.render();

it('default', () => {
//
expect(plot.chart.geometries[0].animateOption).toEqual({
appear: {
animation: 'zoom-in',
duration: 500,
easing: 'easeQuadOut',
},
update: {
duration: 400,
easing: 'easeQuadInOut',
},
enter: {
duration: 400,
easing: 'easeQuadInOut',
animation: 'zoom-in',
},
leave: {
duration: 500,
easing: 'easeQuadIn',
animation: 'zoom-out',
},
});
});

it('update', () => {
plot.update({
animation: {
appear: {
animation: 'fade-in',
},
enter: {
animation: 'fade-in',
},
leave: {
animation: 'wave-out',
},
},
});
expect(plot.chart.geometries[0].animateOption).toEqual({
appear: {
animation: 'fade-in',
duration: 500,
easing: 'easeQuadOut',
},
update: {
duration: 400,
easing: 'easeQuadInOut',
},
enter: {
duration: 400,
easing: 'easeQuadInOut',
animation: 'fade-in',
},
leave: {
duration: 500,
easing: 'easeQuadIn',
animation: 'wave-out',
},
});
});

afterAll(() => {
plot.destroy();
});
});
134 changes: 134 additions & 0 deletions __tests__/unit/plots/circle-packing/index-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { CirclePacking } from '../../../../src';
import { createDiv } from '../../../utils/dom';
import { DATA } from '../../../data/circle-packing';
import { DEFAULT_OPTIONS } from '../../../../src/plots/circle-packing/constant';
import { getContainerSize } from '../../../../src/utils';

describe('Circle-Packing', () => {
const div = createDiv();
const plot = new CirclePacking(div, {
padding: 0,
data: DATA,
legend: false,
hierarchyConfig: {
sort: (a, b) => b.depth - a.depth,
},
});
plot.render();

it('default', () => {
expect(plot.type).toBe('circle-packing');
// @ts-ignore
expect(plot.getDefaultOptions()).toBe(CirclePacking.getDefaultOptions());

const geometry = plot.chart.geometries[0];
expect(geometry.type).toBe('point');

const positionFields = geometry.getAttribute('position').getFields();
expect(geometry.elements.length).toBe(geometry.data.length);
expect(positionFields).toHaveLength(2);
expect(positionFields).toEqual(['x', 'y']);

// 圆形布局 宽高一致,即正常
const coordinateBox = plot.chart.coordinateBBox;
const { width, height } = plot.chart.viewBBox;
const minSize = Math.min(width, height);
expect(coordinateBox.width).toBe(coordinateBox.height);
expect(minSize).toBe(coordinateBox.width);
expect(minSize).toBe(coordinateBox.height);
});

it('color', () => {
plot.update({ color: ['red', 'green', 'blue'] });

const geometry = plot.chart.geometries[0];
const elements = geometry.elements;
expect(elements.length).toBe(plot.chart.getData().length);

// 绘图数据
expect(elements[0].getModel().color).toBe('red');
expect(elements[1].getModel().color).toBe('green');
expect(elements[7].getModel().color).toBe('green');
expect(elements[14].getModel().color).toBe('blue' /** 15 % 3 === 0 */);
});

it('style', () => {
plot.update({ pointStyle: { fill: 'red', fillOpacity: 1 } });

const geometry = plot.chart.geometries[0];
let elements = geometry.elements;
expect(elements.length).toBe(plot.chart.getData().length);

// 绘图数据
expect(elements[0].shape.attr('fillOpacity')).toBe(1);
expect(elements[1].shape.attr('fillOpacity')).toBe(1);
expect(elements[1].shape.attr('fill')).toBe('red');
expect(elements[7].shape.attr('fillOpacity')).toBe(1);
expect(elements[13].shape.attr('fillOpacity')).toBe(1);
expect(elements[14].shape.attr('fillOpacity')).toBe(1);

// callback
plot.update({
rawFields: ['depth'],
pointStyle: ({ depth }) => ({
fill: 'red',
fillOpacity: depth > 1 ? 1 : 0.5,
stroke: 'green',
lineWidth: depth,
}),
});
elements = plot.chart.geometries[0].elements;
// 绘图数据
expect(elements[0].shape.attr('fillOpacity')).toBe(0.5);
expect(elements[0].shape.attr('stroke')).toBe('green');

expect(elements[0].shape.attr('fillOpacity')).toBe(0.5);
expect(elements[elements.length - 1].shape.attr('fillOpacity')).toBe(1);

expect(elements[0].shape.attr('lineWidth')).toBe(0);
expect(elements[1].shape.attr('lineWidth')).toBe(1);
expect(elements[elements.length - 1].shape.attr('lineWidth')).toBe(2);
});

it('label', () => {
let geometry = plot.chart.geometries[0];
let labelGroup = plot.chart.geometries[0].labelsContainer.getChildren()[0];
expect(typeof plot.chart.geometries[0].labelOption).toBe('object');
// @ts-ignore
expect(labelGroup.getChildByIndex(0).attr('text')).toBe(DATA.name);
// @ts-ignore
expect(plot.chart.geometries[0].labelsContainer.getChildren()[1].getChildByIndex(0).attr('text')).toBe(
DATA.children[0].name
);

plot.update({ label: { fields: ['value'] } });
labelGroup = plot.chart.geometries[0].labelsContainer.getChildren()[0];
const filterData = plot.chart.getData();
// @ts-ignore
expect(labelGroup.getChildByIndex(0).attr('text')).toBe(`${filterData[0].value}`);

// meta
plot.update({ meta: { value: { formatter: (v) => v + '%' } } });
labelGroup = plot.chart.geometries[0].labelsContainer.getChildren()[0];
// @ts-ignore
expect(labelGroup.getChildByIndex(0).attr('text')).toBe(`${filterData[0].value}%`);

// formatter
plot.update({ label: { formatter: () => 'xxx' } });
geometry = plot.chart.geometries[0];
// @ts-ignore
expect(geometry.labelsContainer.getChildren()[0].getChildByIndex(0).attr('text')).toBe('xxx');

// 关闭
plot.update({ label: false });
expect(plot.chart.geometries[0].labelOption).toBe(false);
});

it('defaultOptions 保持从 constants 中获取', () => {
expect(CirclePacking.getDefaultOptions()).toEqual(DEFAULT_OPTIONS);
});

afterAll(() => {
plot.destroy();
});
});
Loading

0 comments on commit ab4ce2b

Please sign in to comment.