diff --git a/src/geometry/base.ts b/src/geometry/base.ts index 4f821e053d..59f75ded1d 100644 --- a/src/geometry/base.ts +++ b/src/geometry/base.ts @@ -160,20 +160,6 @@ export interface GeometryCfg { multiplePieWidthRatio?: number; } -// 根据 elementId 查找对应的 label,因为有可能一个 element 对应多个 labels,所以在给 labels 打标识时做了处理 -// 打标规则详见 ./label/base.ts#L263 -function filterLabelsById(id: string, labelsMap: Record) { - const labels = []; - each(labelsMap, (label: IGroup, labelId: string) => { - const elementId = labelId.split(' ')[0]; - if (elementId === id) { - labels.push(label); - } - }); - - return labels; -} - /** * Geometry 几何标记基类,主要负责数据到图形属性的映射以及绘制逻辑。 */ @@ -2098,21 +2084,25 @@ export default class Geometry extends Base { // 将 label 同 element 进行关联 const labelsMap = geometryLabel.labelsRenderer.shapesMap; - each(this.elementsMap, (element: Element, id) => { - const labels = filterLabelsById(id, labelsMap); // element 实例同 label 进行绑定 - if (labels.length) { - element.labelShape = labels; - for (let i = 0; i < labels.length; i++) { - const label = labels[i]; - const labelChildren = label.getChildren(); - for (let j = 0; j < labelChildren.length; j++) { - const child = labelChildren[j]; - child.cfg.name = ['element', 'label']; - child.cfg.element = element; - } + // Store labels for every element. + const elementLabels = new Map>(); + each(labelsMap, (labelGroup: IGroup, labelGroupId: string) => { + const labelChildren = labelGroup.getChildren(); + for (let j = 0; j < labelChildren.length; j++) { + const labelShape = labelChildren[j]; + const element = this.elementsMap[labelShape.get('elementId') || labelGroupId.split(' ')[0]]; + if (element) { + labelShape.cfg.name = ['element', 'label']; + labelShape.cfg.element = element; + const labels = elementLabels.get(element) || new Set(); + labels.add(labelGroup); + elementLabels.set(element, labels); } } }); + for (const [element, labels] of elementLabels.entries()) { + element.labelShape = [...labels]; + } } /** * 是否需要进行群组入场动画 diff --git a/src/geometry/label/base.ts b/src/geometry/label/base.ts index 7b84e74742..0fa7e4c37e 100644 --- a/src/geometry/label/base.ts +++ b/src/geometry/label/base.ts @@ -74,8 +74,8 @@ export default class GeometryLabel { return items; } - public render(mapppingArray: MappingDatum[], isUpdate: boolean = false) { - const labelItems = this.getLabelItems(mapppingArray); + public render(mappingArray: MappingDatum[], isUpdate: boolean = false) { + const labelItems = this.getLabelItems(mappingArray); const labelsRenderer = this.getLabelsRenderer(); const shapes = this.getGeometryShapes(); // 渲染文本 diff --git a/tests/unit/geometry/base-spec.ts b/tests/unit/geometry/base-spec.ts index 2636c68f4b..f184f5ef17 100644 --- a/tests/unit/geometry/base-spec.ts +++ b/tests/unit/geometry/base-spec.ts @@ -202,11 +202,11 @@ describe('Geometry', () => { }, }); - geometry.label('temperature', (val) => {}); + geometry.label('temperature', (val) => { }); expect(geometry.labelOption.callback).toBeInstanceOf(Function); expect(geometry.labelOption.cfg).toBeUndefined(); - geometry.label('temperature', (val) => {}, { + geometry.label('temperature', (val) => { }, { type: 'base', }); expect(geometry.labelOption.callback).toBeInstanceOf(Function); @@ -843,31 +843,31 @@ describe('Geometry', () => { }); chart.data(data); - const geometry = chart.interval().position('year*valye'); + const geometry = chart.interval().position('year*value'); - const beforFn = jest.fn(); + const beforeFn = jest.fn(); const afterFn = jest.fn(); // 无动画 geometry.animate(false); - geometry.once(GEOMETRY_LIFE_CIRCLE.BEFORE_DRAW_ANIMATE, () => beforFn(1)); + geometry.once(GEOMETRY_LIFE_CIRCLE.BEFORE_DRAW_ANIMATE, () => beforeFn(1)); geometry.once(GEOMETRY_LIFE_CIRCLE.AFTER_DRAW_ANIMATE, () => afterFn(1)); chart.render(); - await delay(500); + await delay(100); - expect(beforFn).not.toBeCalled(); + expect(beforeFn).not.toBeCalled(); expect(afterFn).not.toBeCalled(); // 有动画 geometry.animate(true); - geometry.once(GEOMETRY_LIFE_CIRCLE.BEFORE_DRAW_ANIMATE, () => beforFn(2)); + geometry.once(GEOMETRY_LIFE_CIRCLE.BEFORE_DRAW_ANIMATE, () => beforeFn(2)); geometry.once(GEOMETRY_LIFE_CIRCLE.AFTER_DRAW_ANIMATE, () => afterFn(2)); chart.changeSize(300, 300); - await delay(500); + await delay(100); - expect(beforFn).toBeCalledWith(2); + expect(beforeFn).toBeCalledWith(2); // expect(afterFn).toBeCalledWith(2); const fn = jest.fn(); @@ -877,7 +877,7 @@ describe('Geometry', () => { callback: fn, }, }); - geometry.once(GEOMETRY_LIFE_CIRCLE.BEFORE_DRAW_ANIMATE, () => beforFn(3)); + geometry.once(GEOMETRY_LIFE_CIRCLE.BEFORE_DRAW_ANIMATE, () => beforeFn(3)); geometry.once(GEOMETRY_LIFE_CIRCLE.AFTER_DRAW_ANIMATE, () => afterFn(3)); chart.changeSize(400, 400); @@ -918,4 +918,73 @@ describe('Geometry', () => { expect(geometry1.zIndexReversed).toBe(false); expect(geometry1.elements[0].shape.get('zIndex')).not.toBeGreaterThan(geometry1.elements[1].shape.get('zIndex')); }); + + describe('geometry renderLabels. Bind labels to elements', () => { + const data = [ + { year: '1991', value: 15468, type: 'a' }, + { year: '1992', value: 16100, type: 'a' }, + { year: '1993', value: 15900, type: 'a' }, + { year: '1998', value: 32040, type: 'a' }, + { year: '1991', value: 5468, type: 'b' }, + { year: '1992', value: 6100, type: 'b' }, + { year: '1993', value: 5900, type: 'b' }, + { year: '1998', value: 22040, type: 'b' }, + ]; + + it('Geometry, with color mapping', async () => { + async function renderGeometry(type: string) { + const chart = new Chart({ container: createDiv(), width: 500, height: 400 }); + chart.data(data); + chart.clear(); + let geometry = chart[type]().position('year*value').color('type').label('value').animate(false); + chart.render(); + + await delay(100); + expect(geometry.elements.length).toBe(['line', 'area'].includes(type) ? 2 : data.length); + const element = geometry.elements[0]; + expect(element.labelShape.length).toBe(['line', 'area'].includes(type) ? data.length / 2 : 1); + expect(element.labelShape[0].get('visible')).toBe(true); + element.changeVisible(false); + expect(element.labelShape[0].get('visible')).toBe(false); + const bbox = element.getBBox(); + + chart.clear(); + geometry = chart[type]().position('year*value').color('type').label(false).animate(false); + chart.render(); + // previous bbox contains labelBBox, so it not equal to current bbox. + expect(geometry.elements[0].getBBox()).not.toEqual(bbox); + } + await renderGeometry('line'); + await renderGeometry('area'); + await renderGeometry('interval'); + }); + + it('Line Geometry, without color mapping', async () => { + async function renderGeometry(type: string) { + const chart = new Chart({ container: createDiv(), width: 500, height: 400 }); + chart.data(data.filter(d => d.type === 'a')); + chart.clear(); + let geometry = chart[type]().position('year*value').label('value').animate(false); + chart.render(); + + await delay(100); + const element = geometry.elements[0]; + expect(element.labelShape.length).toBe(['line', 'area'].includes(type) ? data.length / 2 : 1); + expect(element.labelShape[0].get('visible')).toBe(true); + element.changeVisible(false); + expect(element.labelShape[0].get('visible')).toBe(false); + const bbox = element.getBBox(); + + chart.clear(); + geometry = chart[type]().position('year*value').label(false).animate(false); + chart.render(); + // previous bbox contains labelBBox, so it not equal to current bbox. + expect(geometry.elements[0].getBBox()).not.toEqual(bbox); + } + + await renderGeometry('line'); + await renderGeometry('area'); + await renderGeometry('interval'); + }); + }); }); diff --git a/tsconfig.json b/tsconfig.json index 768211b9de..9e449b712a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "moduleResolution": "node", "allowSyntheticDefaultImports": true, "esModuleInterop": true, + "downlevelIteration": true, "lib": ["esnext", "dom"], "types": ["jest"], "resolveJsonModule": true,