Skip to content

Commit

Permalink
fix: support adjust-color for overflow labels (#3016)
Browse files Browse the repository at this point in the history
Co-authored-by: lingdao.lzq <lingdao.lzq@antfin.com>
  • Loading branch information
lessmost and lingdao.lzq authored Nov 17, 2020
1 parent 71d9e44 commit ac6d942
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 11 deletions.
12 changes: 8 additions & 4 deletions src/geometry/label/layout/adjust-color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ export function adjustColor(items: LabelItem[], labels: IGroup[], shapes: IShape
const textShape = label.find((el) => el.get('type') === 'text');
const shapeBBox = BBox.fromObject(shape.getBBox());
const textBBox = BBox.fromObject(textShape.getCanvasBBox());
const overflow = !shapeBBox.contains(textBBox);

// 如果文本包围图在图形内部
if (shapeBBox.contains(textBBox)) {
const bgColor = shape.attr('fill');
const fillWhite = isContrastColorWhite(bgColor);
const bgColor = shape.attr('fill');
const fillWhite = isContrastColorWhite(bgColor);

if (!overflow) {
if (fillWhite) {
if (fillColorLight) {
textShape.attr('fill', fillColorLight);
Expand All @@ -31,6 +32,9 @@ export function adjustColor(items: LabelItem[], labels: IGroup[], shapes: IShape
textShape.attr('fill', fillColorDark);
}
}
} else {
// 出现溢出直接应用 overflowLabel 样式
textShape.attr(theme.overflowLabels.style);
}
});
}
17 changes: 17 additions & 0 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1967,6 +1967,23 @@ export interface StyleSheet {
/** Geometry innerLabel 文本描边粗细 */
innerLabelBorder?: number;

/** Geometry overflowLabel 文本颜色 */
overflowLabelFillColor?: string;
/** Geometry overflowLabel 暗色文本颜色 */
overflowLabelFillColorDark?: string;
/** Geometry overflowLabel 亮色文本颜色 */
overflowLabelFillColorLight?: string;
/** Geometry overflowLabel 文本字体大小 */
overflowLabelFontSize?: number;
/** Geometry overflowLabel 文本行高 */
overflowLabelLineHeight?: number;
/** Geometry overflowLabel 文本字体粗细 */
overflowLabelFontWeight?: number | string;
/** Geometry overflowLabel 文本描边颜色 */
overflowLabelBorderColor?: string;
/** Geometry overflowLabel 文本描边粗细 */
overflowLabelBorder?: number;

/** Geometry label 文本连接线粗细 */
labelLineBorder?: number;
/** Geometry label 文本连接线颜色 */
Expand Down
15 changes: 15 additions & 0 deletions src/theme/style-sheet/dark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,21 @@ export const antvDark = {
/** Geometry innerLabel 文本描边粗细 */
innerLabelBorder: 0,

/** Geometry label 文本颜色 */
overflowLabelFillColor: BLACK_COLORS[65],
overflowLabelFillColorDark: '#2c3542',
overflowLabelFillColorLight: '#ffffff',
/** Geometry label 文本字体大小 */
overflowLabelFontSize: 12,
/** Geometry label 文本行高 */
overflowLabelLineHeight: 12,
/** Geometry label 文本字体粗细 */
overflowLabelFontWeight: 'normal',
/** Geometry label 文本描边颜色 */
overflowLabelBorderColor: WHITE_COLORS[100],
/** Geometry label 文本描边粗细 */
overflowLabelBorder: 1,

/** Geometry label 文本连接线粗细 */
labelLineBorder: 1,
/** Geometry label 文本连接线颜色 */
Expand Down
13 changes: 13 additions & 0 deletions src/theme/style-sheet/light.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,19 @@ export const antvLight = {
/** Geometry innerLabel 文本描边粗细 */
innerLabelBorder: 0,

/** Geometry overflowLabel 文本颜色 */
overflowLabelFillColor: BLACK_COLORS[65],
/** Geometry overflowLabel 文本字体大小 */
overflowLabelFontSize: 12,
/** Geometry overflowLabel 文本行高 */
overflowLabelLineHeight: 12,
/** Geometry overflowLabel 文本字体粗细 */
overflowLabelFontWeight: 'normal',
/** Geometry overflowLabel 文本描边颜色 */
overflowLabelBorderColor: WHITE_COLORS[100],
/** Geometry overflowLabel 文本描边粗细 */
overflowLabelBorder: 1,

/** Geometry label 文本连接线粗细 */
labelLineBorder: 1,
/** Geometry label 文本连接线颜色 */
Expand Down
13 changes: 12 additions & 1 deletion src/util/color.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import colorUtil from '@antv/color-util';

// 内置的一些特殊设置
const preset = {
'#5B8FF9': true,
};

// 根据YIQ亮度判断指定颜色取反色是不是白色
// http://24ways.org/2010/calculating-color-contrast
// http://www.w3.org/TR/AERT#color-contrast
export const isContrastColorWhite = (color: string): boolean => {
const [r, g, b] = colorUtil.rgb2arr(colorUtil.toRGB(color));
const rgb = colorUtil.toRGB(color).toUpperCase();
if (preset[rgb]) {
return preset[rgb];
}

const [r, g, b] = colorUtil.rgb2arr(rgb);
const isDark = (r * 299 + g * 587 + b * 114) / 1000 < 128;

return isDark;
Expand Down
9 changes: 9 additions & 0 deletions src/util/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1261,6 +1261,15 @@ export function createThemeByStylesheet(styleSheet: StyleSheet): LooseObject {
},
autoRotate: true,
},
overflowLabels: {
style: {
fill: styleSheet.overflowLabelFillColor,
fontSize: styleSheet.overflowLabelFontSize,
fontFamily: styleSheet.fontFamily,
stroke: styleSheet.overflowLabelBorderColor,
lineWidth: styleSheet.overflowLabelBorder,
},
},
pieLabels: {
labelHeight: 14,
offset: 10,
Expand Down
163 changes: 157 additions & 6 deletions tests/unit/geometry/label/layout/adjust-color-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import { createDiv } from '../../../../util/dom';

describe('adjust-color layout', () => {
const container = createDiv();
const chart = new Chart({
container,
width: 1300,
height: 800,
});

it('adjust color on interval', async () => {
const chart = new Chart({
container,
width: 1300,
height: 800,
});
chart.data(subSalesByArea);
chart.scale('sales', {
formatter: (v) => `${Math.floor(v / 10000)}万`,
Expand Down Expand Up @@ -59,8 +59,159 @@ describe('adjust-color layout', () => {
});
});

it('adjust-color overflow shape', () => {
const DATA = [
{
name: '人群定向召回',
count: 200000000,
},
{
name: '创意召回',
count: 99000000,
},
{
name: '展位疲劳度展位疲劳度展位疲劳度',
count: 70000000,
},
{
name: '计划疲劳度',
count: 65700000,
},
{
name: '召回完成',
count: 8880000,
},
];
const chart = new Chart({
container,
height: 400,
width: 600,
padding: [20, 20, 20, 60],
appendPadding: 10,
// theme: 'dark',
});
chart.data(DATA);
chart.scale('count', {
formatter: (v) => `${(v / 10000).toFixed(2)}万`,
});
chart.coordinate().transpose();
const interval = chart
.interval()
.animate(false)
.position('name*count')
.label('count', {
position: 'left',
layout: [{ type: 'adjust-color' }],
});
chart.axis('name', {
label: {
autoEllipsis: true,
},
});
chart.render();

const labelContainer = interval.labelsContainer;
expect(labelContainer.getCount()).toBe(DATA.length);
const textShapes = labelContainer.findAll((item) => item.get('type') === 'text');

textShapes.forEach((textShape) => {
const element: Element = textShape.get('element');
const shape = element.shape;
const shapeBBox = BBox.fromObject(shape.getCanvasBBox());
const textBBox = BBox.fromObject(textShape.getCanvasBBox());
if (shapeBBox.contains(textBBox)) {
const bgColor = shape.attr('fill');
const fillWhite = isContrastColorWhite(bgColor);
if (fillWhite) {
expect(textShape.attr('fill')).toBe('#ffffff');
} else {
expect(textShape.attr('fill')).toBe('#2c3542');
}
} else {
expect(textShape.attr('fill')).toBe('#595959');
expect(textShape.attr('stroke')).toBe('#FFFFFF');
expect(textShape.attr('lineWidth')).toBe(1);
}
});
});

it('adjust-color overflow shape /w dark theme', () => {
const DATA = [
{
name: '人群定向召回',
count: 200000000,
},
{
name: '创意召回',
count: 99000000,
},
{
name: '展位疲劳度展位疲劳度展位疲劳度',
count: 70000000,
},
{
name: '计划疲劳度',
count: 65700000,
},
{
name: '召回完成',
count: 8880000,
},
];
const chart = new Chart({
container,
height: 400,
width: 600,
padding: [20, 20, 20, 60],
appendPadding: 10,
theme: 'dark',
});
chart.data(DATA);
chart.scale('count', {
formatter: (v) => `${(v / 10000).toFixed(2)}万`,
});
chart.coordinate().transpose();
const interval = chart
.interval()
.animate(false)
.position('name*count')
.label('count', {
position: 'left',
layout: [{ type: 'adjust-color' }],
});
chart.axis('name', {
label: {
autoEllipsis: true,
},
});
chart.render();

const labelContainer = interval.labelsContainer;
expect(labelContainer.getCount()).toBe(DATA.length);
const textShapes = labelContainer.findAll((item) => item.get('type') === 'text');

textShapes.forEach((textShape) => {
const element: Element = textShape.get('element');
const shape = element.shape;
const shapeBBox = BBox.fromObject(shape.getCanvasBBox());
const textBBox = BBox.fromObject(textShape.getCanvasBBox());
if (shapeBBox.contains(textBBox)) {
const bgColor = shape.attr('fill');
const fillWhite = isContrastColorWhite(bgColor);
if (fillWhite) {
expect(textShape.attr('fill')).toBe('#ffffff');
} else {
expect(textShape.attr('fill')).toBe('#2c3542');
}
} else {
expect(textShape.attr('fill')).toBe('#A6A6A6');
expect(textShape.attr('stroke')).toBe('#000');
expect(textShape.attr('lineWidth')).toBe(1);
}
});
});

afterAll(() => {
chart.destroy();
removeDom(container);
});
});
5 changes: 5 additions & 0 deletions tests/unit/util/color-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import { isContrastColorWhite } from '../../../src/util/color';

describe('color', () => {
it('isContrastColorWhite', () => {
// preset
expect(isContrastColorWhite('#5B8FF9')).toBe(true);
expect(isContrastColorWhite('#5b8ff9')).toBe(true);
expect(isContrastColorWhite('rgb(91,143,249')).toBe(true);

// hex
expect(isContrastColorWhite('#000000')).toBe(true);
expect(isContrastColorWhite('#000')).toBe(true);
Expand Down

0 comments on commit ac6d942

Please sign in to comment.