Skip to content

Commit 5a56c74

Browse files
moayuisuda林舸
andauthored
feat: add legend focus icon (#356)
* feat: add legend focus icon * feat: focus icon autofit outter size * chore: update snapshots * docs: add focus icon markdown * feat: remove redundant opacity --------- Co-authored-by: 林舸 <anhaohui.ahh@antgroup.com>
1 parent 933d596 commit 5a56c74

7 files changed

Lines changed: 133 additions & 22 deletions

File tree

.vscode/settings.json

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
{
2-
"cSpell.words": ["bbox", "Sparkline", "timebar"],
3-
"svg.preview.background": "white"
4-
}
2+
"cSpell.words": [
3+
"bbox",
4+
"Sparkline",
5+
"timebar"
6+
],
7+
"svg.preview.background": "white",
8+
"projectConfig": {
9+
"dima": {
10+
"installedvscode": true
11+
}
12+
}
13+
}

__tests__/integration/snapshots/Marker1.svg

Lines changed: 8 additions & 3 deletions
Loading

docs/components/legend.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import { Category, Continuous } from '@antv/component';
2929
| gridCol | `number` | 每列显示的图例项个数 | `-` |
3030
| rowPadding | `number` | 图例项之间的行间距 | `0` |
3131
| colPadding | `number` | 图例项之间的列间距 | `0` |
32+
| focus | `boolean` | 是否开启聚焦 | `-` |
33+
| focusMarkerSize | `number` | 聚焦图标尺寸 | `12` |
3234
| click | `ClickEvent` | 点击事件 | `-` |
3335
| mouseenter | `MouseEnterEvent` | 鼠标移入事件 | `-` |
3436
| mouseleave | `MouseLeaveEvent` | 鼠标移出事件 | `-` |

src/ui/legend/category/item.ts

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
RectStyleProps,
99
TextStyleProps,
1010
} from '@antv/g';
11+
import { isUndefined } from '@antv/util';
1112
import { Marker } from '../../marker';
1213
import type { ComponentOptions } from '../../../core';
1314
import { Component } from '../../../core';
@@ -50,6 +51,8 @@ export type CategoryItemStyleProps = GroupStyleProps &
5051
x?: number;
5152
y?: number;
5253
poptip?: PoptipStyleProps & PoptipRender;
54+
focus?: boolean;
55+
focusMarkerSize?: number;
5356
};
5457

5558
export type CategoryItemOptions = ComponentOptions<CategoryItemStyleProps>;
@@ -62,6 +65,8 @@ const CLASS_NAMES = classNames(
6265
labelGroup: 'label-group',
6366
label: 'label',
6467
valueGroup: 'value-group',
68+
focusGroup: 'focus-group',
69+
focus: 'focus',
6570
value: 'value',
6671
backgroundGroup: 'background-group',
6772
background: 'background',
@@ -111,6 +116,8 @@ export class CategoryItem extends Component<CategoryItemStyleProps> {
111116

112117
private poptipGroup!: Poptip;
113118

119+
private focusGroup!: Selection<Group>;
120+
114121
private markerGroup!: Selection<Group>;
115122

116123
private labelGroup!: Selection<Group>;
@@ -130,13 +137,17 @@ export class CategoryItem extends Component<CategoryItemStyleProps> {
130137
private get actualSpace() {
131138
const label = this.labelGroup;
132139
const value = this.valueGroup;
133-
const { markerSize } = this.attributes;
140+
const { markerSize, focus, focusMarkerSize } = this.attributes;
134141
const { width: labelWidth, height: labelHeight } = label.node().getBBox();
135142
const { width: valueWidth, height: valueHeight } = value.node().getBBox();
143+
144+
const focusWidth = focus ? focusMarkerSize ?? 12 : 0;
145+
136146
return {
137147
markerWidth: markerSize,
138148
labelWidth,
139149
valueWidth,
150+
focusWidth,
140151
height: Math.max(markerSize, labelHeight, valueHeight),
141152
};
142153
}
@@ -150,41 +161,50 @@ export class CategoryItem extends Component<CategoryItemStyleProps> {
150161
return [span1 / basis, span2 / basis];
151162
}
152163

164+
setAttribute(n: any, v: any) {
165+
super.setAttribute(n, v);
166+
}
167+
153168
private get shape() {
154169
const { markerSize, width: fullWidth } = this.attributes;
155170
const actualSpace = this.actualSpace;
156-
const { markerWidth, height } = actualSpace;
171+
const { markerWidth, focusWidth, height } = actualSpace;
157172
let { labelWidth, valueWidth } = this.actualSpace;
158-
const [spacing1, spacing2] = this.spacing;
173+
const [spacing1, spacing2, spacing3] = this.spacing;
159174

160175
if (fullWidth) {
161-
const width = fullWidth - markerSize - spacing1 - spacing2;
176+
const width = fullWidth - markerSize - spacing1 - spacing2 - focusWidth - spacing3;
162177
const [span1, span2] = this.span;
163178
[labelWidth, valueWidth] = [span1 * width, span2 * width];
164179
}
165180

166-
const width = markerWidth + labelWidth + valueWidth + spacing1 + spacing2;
167-
return { width, height, markerWidth, labelWidth, valueWidth };
181+
const width = markerWidth + labelWidth + valueWidth + spacing1 + spacing2 + focusWidth + spacing3;
182+
return { width, height, markerWidth, labelWidth, valueWidth, focusWidth };
168183
}
169184

170185
private get spacing() {
171-
const { spacing } = this.attributes;
172-
if (!spacing) return [0, 0];
173-
const [spacing1, spacing2] = parseSeriesAttr(spacing);
174-
if (this.showValue) return [spacing1, spacing2];
175-
return [spacing1, 0];
186+
const { spacing, focus } = this.attributes;
187+
if (!spacing) return [0, 0, 0];
188+
const [spacing1, spacing2, spacing3] = parseSeriesAttr(spacing);
189+
return [spacing1, this.showValue ? spacing2 : 0, focus ? spacing3 : 0];
176190
}
177191

178192
private get layout() {
179-
const { markerWidth, labelWidth, valueWidth, width, height } = this.shape;
180-
const [spacing1, spacing2] = this.spacing;
193+
const { markerWidth, labelWidth, valueWidth, focusWidth, width, height } = this.shape;
194+
const [spacing1, spacing2, spacing3] = this.spacing;
181195
return {
182196
height,
183197
width,
184198
markerWidth,
185199
labelWidth,
186200
valueWidth,
187-
position: [markerWidth / 2, markerWidth + spacing1, markerWidth + labelWidth + spacing1 + spacing2],
201+
focusWidth,
202+
position: [
203+
markerWidth / 2,
204+
markerWidth + spacing1,
205+
markerWidth + labelWidth + spacing1 + spacing2,
206+
markerWidth + labelWidth + valueWidth + spacing1 + spacing2 + spacing3 + focusWidth / 2,
207+
],
188208
};
189209
}
190210

@@ -280,6 +300,49 @@ export class CategoryItem extends Component<CategoryItemStyleProps> {
280300
});
281301
}
282302

303+
private renderFocus(ctn: Selection) {
304+
const { focus, focusMarkerSize } = this.attributes;
305+
const defaultOptions = {
306+
x: 0,
307+
y: 0,
308+
size: focusMarkerSize,
309+
opacity: 0.6,
310+
symbol: 'focus',
311+
stroke: '#aaaaaa',
312+
lineWidth: 1,
313+
};
314+
315+
if (isUndefined(focus)) return;
316+
317+
this.focusGroup = ctn.maybeAppendByClassName<Group>(CLASS_NAMES.focusGroup, 'g').style('zIndex', 0);
318+
ifShow(focus, this.focusGroup, () => {
319+
const marker = new Marker({
320+
style: {
321+
...defaultOptions,
322+
symbol: 'focus',
323+
},
324+
});
325+
const interactiveCircle = new Circle({
326+
style: {
327+
r: defaultOptions.size / 2,
328+
fill: 'transparent',
329+
},
330+
});
331+
332+
const container = this.focusGroup.node();
333+
container.appendChild(interactiveCircle);
334+
container.appendChild(marker);
335+
marker.update({ opacity: 0 });
336+
337+
ctn.node().addEventListener('pointerenter', () => {
338+
marker.update({ opacity: 1 });
339+
});
340+
ctn.node().addEventListener('pointerleave', () => {
341+
marker.update({ opacity: 0 });
342+
});
343+
});
344+
}
345+
283346
private renderPoptip(ctn: Selection) {
284347
const { poptip } = this.attributes;
285348
if (!poptip) return;
@@ -305,7 +368,7 @@ export class CategoryItem extends Component<CategoryItemStyleProps> {
305368
labelWidth,
306369
valueWidth,
307370
height,
308-
position: [markerX, labelX, valueX],
371+
position: [markerX, labelX, valueX, focusX],
309372
},
310373
} = this;
311374
const halfHeight = height / 2;
@@ -316,6 +379,7 @@ export class CategoryItem extends Component<CategoryItemStyleProps> {
316379
transform: `translate(${markerX}, ${halfHeight})${this.markerGroup.node().style._transform}`,
317380
});
318381
this.labelGroup.styles({ transform: `translate(${labelX}, ${halfHeight})` });
382+
if (this.focusGroup) this.focusGroup.styles({ transform: `translate(${focusX}, ${halfHeight})` });
319383

320384
ellipsisIt(this.labelGroup.select(CLASS_NAMES.label.class).node(), Math.ceil(labelWidth));
321385
if (this.showValue) {
@@ -333,6 +397,7 @@ export class CategoryItem extends Component<CategoryItemStyleProps> {
333397
this.renderValue(ctn);
334398
this.renderBackground(ctn);
335399
this.renderPoptip(ctn);
400+
this.renderFocus(ctn);
336401
this.adjustLayout();
337402
}
338403
}

src/ui/legend/category/items.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ export type CategoryItemsStyleProps = GroupStyleProps &
5555
mouseenter?: (el: Selection) => void;
5656
mouseleave?: (el: Selection) => void;
5757
poptip?: PoptipStyleProps & PoptipRender;
58+
focus?: boolean;
59+
focusMarkerSize?: number;
5860
};
5961

6062
export type CategoryItemsOptions = ComponentOptions<CategoryItemsStyleProps>;
@@ -129,7 +131,7 @@ export class CategoryItems extends Component<CategoryItemsStyleProps> {
129131
}
130132

131133
private get renderData() {
132-
const { data, layout, poptip } = this.attributes;
134+
const { data, layout, poptip, focus, focusMarkerSize } = this.attributes;
133135
const style = subStyleProps<CategoryItemStyleProps>(this.attributes, 'item');
134136

135137
const d = data.map((datum, index) => {
@@ -142,6 +144,8 @@ export class CategoryItems extends Component<CategoryItemsStyleProps> {
142144
labelText,
143145
valueText,
144146
poptip,
147+
focus,
148+
focusMarkerSize,
145149
...Object.fromEntries(
146150
Object.entries(style).map(([key, val]) => [key, getCallbackValue(val, [datum, index, data])])
147151
),

src/ui/marker/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
hexagon,
1313
hv,
1414
hvh,
15+
focus,
1516
hyphen,
1617
line,
1718
plus,
@@ -122,3 +123,4 @@ Marker.registerSymbol('hv', hv);
122123
Marker.registerSymbol('vh', vh);
123124
Marker.registerSymbol('hvh', hvh);
124125
Marker.registerSymbol('vhv', vhv);
126+
Marker.registerSymbol('focus', focus);

src/ui/marker/symbol.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,27 @@ export function vhv(x: number, y: number) {
195195
export const button: SymbolFactor = (x, y, r) => {
196196
return [['M', x - r, y - r], ['L', x + r, y], ['L', x - r, y + r], ['Z']];
197197
};
198+
199+
export const focus: SymbolFactor = (x, y, r) => {
200+
const outerRadius = r;
201+
const innerRadius = r * 0.2;
202+
const crossLength = r * 0.7;
203+
204+
return [
205+
// 外圆
206+
['M', x - outerRadius, y],
207+
['A', outerRadius, outerRadius, 0, 1, 0, x + outerRadius, y],
208+
['A', outerRadius, outerRadius, 0, 1, 0, x - outerRadius, y],
209+
['Z'],
210+
// 水平十字线 (简单线条)
211+
['M', x - crossLength, y],
212+
['L', x - innerRadius, y],
213+
['M', x + innerRadius, y],
214+
['L', x + crossLength, y],
215+
// 垂直十字线 (简单线条)
216+
['M', x, y - crossLength],
217+
['L', x, y - innerRadius],
218+
['M', x, y + innerRadius],
219+
['L', x, y + crossLength],
220+
];
221+
};

0 commit comments

Comments
 (0)