Skip to content

Commit 705d80d

Browse files
Fix interaction elementhoverscale (#7236)
* fix: 优化 elementHoverScale 交互效果 * docs: 更新文档 * fix: 修复优化后的交互方式在图例筛选后表现异常的问题
1 parent 27de29b commit 705d80d

File tree

4 files changed

+40
-70
lines changed

4 files changed

+40
-70
lines changed

site/docs/manual/core/interaction/elementHoverScale.en.md

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ Or at the View level. Interactions declared on the view are passed to child mark
6969
| Property | Description | Type | Default Value | Required |
7070
|----------------|---------------------------------|-----------|-----------------------------|----------|
7171
| scale | Scale factor on hover | `number` | `1.04` | |
72-
| scaleOrigin | Origin point for scaling | `string` | `'center center'` | |
7372
| shadow | Whether to show shadow | `boolean` | `true` | |
7473
| shadowColor | Shadow color | `string` | `'rgba(0, 0, 0, 0.4)'` | |
7574
| shadowBlur | Shadow blur radius | `number` | `10` | |
@@ -83,15 +82,7 @@ Or at the View level. Interactions declared on the view are passed to child mark
8382

8483
#### scale
8584

86-
Controls the scale factor when hovering. A value of `1.04` means the element scales to 104% of its original size.
87-
88-
#### scaleOrigin
89-
90-
Sets the origin point for scaling. Supported formats:
91-
92-
- `'center center'` - scale from the center (default)
93-
- `'top left'` - scale from the top-left corner
94-
- `'50% 50%'` - specify using percentages
85+
Controls the scale factor when hovering. A value of `1.04` means the element scales to 104% of its original size. Scaling defaults to center origin.
9586

9687
#### shadow
9788

@@ -311,7 +302,6 @@ chart.options({
311302
interaction: {
312303
elementHoverScale: {
313304
scale: 1.15, // larger scale factor
314-
scaleOrigin: 'center center',
315305
shadow: true,
316306
shadowColor: 'rgba(139, 0, 139, 0.6)', // custom shadow color
317307
shadowBlur: 20, // larger blur radius

site/docs/manual/core/interaction/elementHoverScale.zh.md

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ order: 11
6969
| 属性 | 描述 | 类型 | 默认值 | 必选 |
7070
| ------------- | ------------------------ | ---------- | ----------------------- | ---- |
7171
| scale | 缩放比例 | `number` | `1.04` | |
72-
| scaleOrigin | 缩放中心点 | `string` | `'center center'` | |
7372
| shadow | 是否显示阴影 | `boolean` | `true` | |
7473
| shadowColor | 阴影颜色 | `string` | `'rgba(0, 0, 0, 0.4)'` | |
7574
| shadowBlur | 阴影模糊度 | `number` | `10` | |
@@ -83,15 +82,7 @@ order: 11
8382

8483
#### scale
8584

86-
控制元素悬浮时的放大倍数。值为 `1.04` 表示放大到原始大小的 104%。
87-
88-
#### scaleOrigin
89-
90-
设置缩放的中心点,支持以下格式:
91-
92-
- `'center center'` - 从中心点缩放(默认)
93-
- `'top left'` - 从左上角缩放
94-
- `'50% 50%'` - 使用百分比指定
85+
控制元素悬浮时的放大倍数。值为 `1.04` 表示放大到原始大小的 104%。缩放默认以元素中心为基准点。
9586

9687
#### shadow
9788

@@ -312,7 +303,6 @@ chart.options({
312303
interaction: {
313304
elementHoverScale: {
314305
scale: 1.15, // 更大的缩放比例
315-
scaleOrigin: 'center center',
316306
shadow: true,
317307
shadowColor: 'rgba(139, 0, 139, 0.6)', // 自定义阴影颜色
318308
shadowBlur: 20, // 更大的模糊半径

src/interaction/elementHoverScale.ts

Lines changed: 38 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { DisplayObject } from '@antv/g';
22
import { deepMix } from '@antv/util';
33
import { group } from '@antv/vendor/d3-array';
4+
import { isPolar } from '../utils/coordinate';
45
import {
56
createDatumof,
67
createUseState,
@@ -21,7 +22,6 @@ export function elementHoverScale(
2122
datum,
2223
groupKey = (element) => element,
2324
scaleFactor = 1.04,
24-
scaleOrigin = 'center center',
2525
shadow = true,
2626
shadowColor = 'rgba(0, 0, 0, 0.4)',
2727
shadowBlur = 10,
@@ -31,6 +31,7 @@ export function elementHoverScale(
3131
delay = 60,
3232
emitter,
3333
state = {},
34+
coordinate,
3435
}: Record<string, any>,
3536
) {
3637
// Helper function to get current valid elements
@@ -49,17 +50,19 @@ export function elementHoverScale(
4950
const { updateState, removeState, hasState } = useState(valueof);
5051

5152
const originalStyles = new Map<DisplayObject, Record<string, any>>();
52-
const hoveredElements = new Set<DisplayObject>();
5353

5454
let out;
5555

5656
const applyHoverEffect = (element: DisplayObject) => {
57-
if (hoveredElements.has(element)) return;
57+
if (originalStyles.has(element)) return;
5858

59-
// Capture current state before applying effect
6059
const currentTransform = element.style.transform || '';
6160
const currentTransformOrigin = element.style.transformOrigin || '';
61+
// Normalize 'none' to empty string as 'none' is not a valid transform value for concatenation
62+
const normalizedTransform =
63+
currentTransform === 'none' ? '' : currentTransform;
6264

65+
// Save original styles for restoration
6366
originalStyles.set(element, {
6467
transform: currentTransform,
6568
transformOrigin: currentTransformOrigin,
@@ -70,26 +73,21 @@ export function elementHoverScale(
7073
shadowOffsetY: element.style.shadowOffsetY || 0,
7174
});
7275

73-
// Treat 'none' as empty string since it means no transform
74-
const prefix =
75-
currentTransform && currentTransform !== 'none' ? currentTransform : '';
76-
const scaleTransform = `scale(${scaleFactor})`;
77-
78-
// Build new transform: append or replace scale in existing transform
79-
let newTransform: string;
80-
if (prefix && !prefix.includes('scale')) {
81-
newTransform = `${prefix} ${scaleTransform}`.trimStart();
82-
} else if (prefix && prefix.includes('scale')) {
83-
newTransform = prefix
84-
.replace(/scale\([^)]+\)/g, scaleTransform)
85-
.trimStart();
86-
} else {
87-
newTransform = scaleTransform;
76+
// For polar coordinates without transform, set transformOrigin to coordinate center
77+
// When legend filtering occurs, element positioning changes from translate to absolute path coords
78+
// Setting transformOrigin to center ensures consistent radial growth in both cases
79+
if (coordinate && isPolar(coordinate) && !normalizedTransform) {
80+
const center = coordinate.getCenter() as [number, number];
81+
element.style.transformOrigin = `${center[0]}px ${center[1]}px`;
8882
}
8983

90-
// Apply styles
91-
element.style.transformOrigin = scaleOrigin;
92-
element.style.transform = newTransform;
84+
// Apply scale transform
85+
const scaleTransform = `scale(${scaleFactor})`;
86+
element.style.transform = normalizedTransform
87+
? `${normalizedTransform} ${scaleTransform}`
88+
: scaleTransform;
89+
90+
// Apply visual effects
9391
element.style.zIndex = zIndex;
9492

9593
if (shadow) {
@@ -98,8 +96,6 @@ export function elementHoverScale(
9896
element.style.shadowOffsetX = shadowOffsetX;
9997
element.style.shadowOffsetY = shadowOffsetY;
10098
}
101-
102-
hoveredElements.add(element);
10399
};
104100

105101
const removeHoverEffect = (element: DisplayObject) => {
@@ -115,7 +111,6 @@ export function elementHoverScale(
115111
element.style.shadowOffsetX = original.shadowOffsetX;
116112
element.style.shadowOffsetY = original.shadowOffsetY;
117113

118-
hoveredElements.delete(element);
119114
originalStyles.delete(element);
120115
};
121116

@@ -131,25 +126,25 @@ export function elementHoverScale(
131126
if (out) clearTimeout(out);
132127

133128
const currentKeyGroup = group(validElements, groupKey);
134-
const k = groupKey(element);
135-
const currentGroup = currentKeyGroup.get(k);
129+
const currentKey = groupKey(element);
130+
const currentGroup = currentKeyGroup.get(currentKey);
136131

137132
if (!currentGroup) return;
138133

139134
const groupSet = new Set(currentGroup);
140135

141136
// Remove hover effects from elements not in current group
142-
for (const e of validElements) {
143-
if (!groupSet.has(e)) {
144-
removeState(e, 'active');
145-
removeHoverEffect(e);
137+
for (const element of validElements) {
138+
if (!groupSet.has(element)) {
139+
removeState(element, 'active');
140+
removeHoverEffect(element);
146141
}
147142
}
148143

149144
// Apply hover effects to current group
150-
for (const e of currentGroup) {
151-
if (!hasState(e, 'active')) updateState(e, 'active');
152-
applyHoverEffect(e as DisplayObject);
145+
for (const element of currentGroup) {
146+
if (!hasState(element, 'active')) updateState(element, 'active');
147+
applyHoverEffect(element as DisplayObject);
153148
}
154149

155150
// Emit events
@@ -175,21 +170,18 @@ export function elementHoverScale(
175170
const validElements = getCurrentElements();
176171

177172
// Remove hover effects and states from all valid elements
178-
for (const e of validElements) {
179-
removeState(e, 'active');
180-
removeHoverEffect(e);
173+
for (const element of validElements) {
174+
removeState(element, 'active');
175+
removeHoverEffect(element);
181176
}
182177

183-
hoveredElements.clear();
184-
185178
if (nativeEvent) {
186179
emitter.emit('element:unhoverscale', { nativeEvent });
187180
}
188181
};
189182

190-
const pointerout = (event) => {
191-
if (delay > 0) delayReset();
192-
else reset();
183+
const pointerout = () => {
184+
delay > 0 ? delayReset() : reset();
193185
};
194186

195187
const pointerleave = () => {
@@ -232,19 +224,17 @@ export function elementHoverScale(
232224

233225
// Clean up all hover effects from current elements
234226
const validElements = getCurrentElements();
235-
for (const e of validElements) {
236-
removeHoverEffect(e);
227+
for (const element of validElements) {
228+
removeHoverEffect(element);
237229
}
238230
originalStyles.clear();
239-
hoveredElements.clear();
240231
};
241232
}
242233

243234
export function ElementHoverScale({
244235
delay,
245236
createGroup,
246237
scale: scaleFactorParam,
247-
scaleOrigin,
248238
shadow,
249239
shadowColor,
250240
shadowBlur,
@@ -257,6 +247,7 @@ export function ElementHoverScale({
257247
const { container, view, options } = context;
258248
const plotArea = selectPlotArea(container);
259249
const datumof = createDatumof(view);
250+
const { coordinate } = view;
260251

261252
return elementHoverScale(plotArea, {
262253
elements: selectG2Elements,
@@ -266,7 +257,6 @@ export function ElementHoverScale({
266257
: undefined,
267258
state: mergeState(options, ['active']),
268259
scaleFactor: scaleFactorParam,
269-
scaleOrigin,
270260
shadow,
271261
shadowColor,
272262
shadowBlur,
@@ -275,6 +265,7 @@ export function ElementHoverScale({
275265
zIndex,
276266
delay,
277267
emitter,
268+
coordinate,
278269
...rest,
279270
});
280271
};

src/spec/interaction.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ export type ElementHighlightByColorInteraction = {
152152
export type ElementHoverScaleInteraction = {
153153
type?: 'elementHoverScale';
154154
scale?: number;
155-
scaleOrigin?: string;
156155
shadow?: boolean;
157156
shadowColor?: string;
158157
shadowBlur?: number;

0 commit comments

Comments
 (0)