Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import type { IGraphic, Stage } from '@visactor/vrender-core';
import { DiscreteLegend } from '../../src';
import { createCanvas } from '../util/dom';
import { createTestStage } from '../util/vrender';

describe('DiscreteLegend pager.hugContent', () => {
let stage: Stage;
beforeAll(() => {
createCanvas(document.body, 'main');
stage = createTestStage('main');
});

afterAll(() => {
stage.release();
});

it('horizontal pager should hug content and shrink clip viewport when pager.hugContent is true', () => {
const items = new Array(6).fill(0).map((_, i) => ({
label: `long-legend-item-label-${i}`,
shape: { fill: 'red', symbolType: 'circle' }
}));
const createLegend = (pager?: any) =>
new DiscreteLegend({
layout: 'horizontal',
maxWidth: 400,
maxRow: 1,
items,
pager
});

const defaultLegend = createLegend();
stage.defaultLayer.add(defaultLegend as unknown as IGraphic);
stage.render();
const hugLegend = createLegend({ hugContent: true, space: 10 });
stage.defaultLayer.add(hugLegend as unknown as IGraphic);
stage.render();

// 默认行为不变:翻页器钉在图例可用空间末端
const defaultPager = (defaultLegend as any)._pagerComponent;
expect(defaultPager.attribute.x).toBeCloseTo(400 - defaultPager.AABBBounds.width(), 4);

// hugContent:翻页器紧跟内容右缘(间距为 pager.space),分页视口收缩到实际内容宽
const hugPager = (hugLegend as any)._pagerComponent;
const contentWidth = (hugLegend as any)._itemsContainer.AABBBounds.width();
expect(contentWidth).toBeLessThan(400 - hugPager.AABBBounds.width() - 10);
expect(hugPager.attribute.x).toBeCloseTo(contentWidth + 10, 4);
expect((hugLegend as any)._itemContext.clipContainer.attribute.width).toBeCloseTo(contentWidth, 4);

// 整体包围盒随内容收缩
expect(hugLegend.AABBBounds.width()).toBeLessThan(defaultLegend.AABBBounds.width());
});

it('vertical pager should hug content and shrink clip viewport when pager.hugContent is true', () => {
const items = new Array(8).fill(0).map((_, i) => ({
label: `item-${i}`,
shape: { fill: 'red', symbolType: 'circle' }
}));
const createLegend = (pager?: any) =>
new DiscreteLegend({
layout: 'vertical',
maxHeight: 100,
maxCol: 1,
items,
pager
});

const defaultLegend = createLegend();
stage.defaultLayer.add(defaultLegend as unknown as IGraphic);
stage.render();
const hugLegend = createLegend({ hugContent: true, space: 10 });
stage.defaultLayer.add(hugLegend as unknown as IGraphic);
stage.render();

// 默认行为不变:翻页器钉在图例可用空间末端
const defaultPager = (defaultLegend as any)._pagerComponent;
const defaultMaxHeight = (defaultLegend as any)._contentMaxHeight;
expect(defaultPager.attribute.y).toBeCloseTo(defaultMaxHeight - defaultPager.AABBBounds.height(), 4);

// hugContent:翻页器紧贴内容下缘,分页视口收缩到实际内容高
const hugPager = (hugLegend as any)._pagerComponent;
const contentHeight = (hugLegend as any)._itemsContainer.AABBBounds.height();
expect(hugPager.attribute.y).toBeCloseTo(contentHeight + 10, 4);
expect((hugLegend as any)._itemContext.clipContainer.attribute.height).toBeCloseTo(contentHeight, 4);

// 整体包围盒随内容收缩
expect(hugLegend.AABBBounds.height()).toBeLessThan(defaultLegend.AABBBounds.height());
});
});
32 changes: 28 additions & 4 deletions packages/vrender-components/src/legend/discrete/discrete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,8 @@ export class DiscreteLegend extends LegendBase<DiscreteLegendAttrs> {
const { pager } = this.attribute;
const { totalPage, isHorizontal } = this._itemContext;
const position = (pager && (pager as LegendPagerAttributes).position) || 'middle';
const hugContent = !!(pager && (pager as LegendPagerAttributes).hugContent);
const pagerSpace = (pager && (pager as LegendPagerAttributes).space) ?? DEFAULT_PAGER_SPACE;
(this._pagerComponent as Pager).setTotal(totalPage);

if (isHorizontal) {
Expand All @@ -820,8 +822,13 @@ export class DiscreteLegend extends LegendBase<DiscreteLegendAttrs> {
} else {
y = renderStartY + compHeight / 2 - this._pagerComponent.AABBBounds.height() / 2;
}
let x = compWidth - this._pagerComponent.AABBBounds.width();
if (hugContent) {
// 分页器紧贴内容右缘,内容占满可用空间时退化为原有的贴右缘布局
x = Math.max(0, Math.min(this._itemsContainer.AABBBounds.width() + pagerSpace, x));
}
this._pagerComponent.setAttributes({
x: compWidth - this._pagerComponent.AABBBounds.width(),
x,
y
});
} else {
Expand All @@ -833,9 +840,14 @@ export class DiscreteLegend extends LegendBase<DiscreteLegendAttrs> {
} else {
x = (compWidth - this._pagerComponent.AABBBounds.width()) / 2;
}
let y = compHeight - this._pagerComponent.AABBBounds.height();
if (hugContent) {
// 分页器紧贴内容下缘,内容占满可用空间时退化为原有的贴下缘布局
y = Math.max(0, Math.min(renderStartY + this._itemsContainer.AABBBounds.height() + pagerSpace, y));
}
this._pagerComponent.setAttributes({
x,
y: compHeight - this._pagerComponent.AABBBounds.height()
y
});
}
}
Expand Down Expand Up @@ -1095,11 +1107,23 @@ export class DiscreteLegend extends LegendBase<DiscreteLegendAttrs> {
}
}

// hugContent 时分页裁剪视口收缩到实际内容尺寸:定宽(高)的 clip 分组会把图例包围盒
// 固定在整个可用空间上,导致「图例项 + 分页器」无法作为整块被外部布局居中
const hugContent = !!(pager as LegendPagerAttributes).hugContent;
let clipWidth = isHorizontal ? contentWidth : compWidth;
let clipHeight = isHorizontal ? compHeight : contentHeight;
if (hugContent) {
if (isHorizontal) {
clipWidth = Math.max(0, Math.min(clipWidth, itemsContainer.AABBBounds.width()));
} else {
clipHeight = Math.max(0, Math.min(clipHeight, itemsContainer.AABBBounds.height()));
}
}
const clipGroup = graphicCreator.group({
x: 0,
y: renderStartY,
width: isHorizontal ? contentWidth : compWidth,
height: isHorizontal ? compHeight : contentHeight,
width: clipWidth,
height: clipHeight,
clip: true,
pickable: false
});
Expand Down
8 changes: 8 additions & 0 deletions packages/vrender-components/src/legend/discrete/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ export type LegendPagerAttributes = Omit<PagerAttributes, 'total'> &
* @default 'middle'
*/
position?: 'start' | 'middle' | 'end';
/**
* 是否让分页器紧贴图例内容排布。默认 false,分页器固定在图例可用空间的末端,
* 图例项与分页器呈两端对齐;开启后分页器紧跟最后一个图例项(间距为 `space`),
* 同时分页裁剪视口收缩到实际内容尺寸,使「图例项 + 分页器」的包围盒随内容
* 自适应,便于外部布局把整块内容居中放置。
* @default false
*/
hugContent?: boolean;
};

/**
Expand Down