Skip to content

feat: Add crosshairs follow mouse feature for Tooltip#7285

Open
yaohuitc wants to merge 2 commits intoantvis:v5from
yaohuitc:feat_crosshairs_follow
Open

feat: Add crosshairs follow mouse feature for Tooltip#7285
yaohuitc wants to merge 2 commits intoantvis:v5from
yaohuitc:feat_crosshairs_follow

Conversation

@yaohuitc
Copy link
Copy Markdown

feat: Add crosshairs follow mouse feature for Tooltip

Summary

This PR adds support for mouse-following crosshairs in tooltip interactions, enabling crosshairs to track the mouse position in real-time instead of snapping to data points. This is particularly useful for financial charts like K-line (candlestick) charts where users need precise coordinate tracking.

Changes

1. Type Definition (src/spec/interaction.ts)

Added 9 new parameters to TooltipInteraction:

Parameter | Type | Description -- | -- | -- crosshairsFollow | boolean | Whether crosshairs follow mouse position crosshairsXFollow | boolean | Whether horizontal crosshair follows mouse crosshairsYFollow | boolean | Whether vertical crosshair follows mouse crosshairsTag | boolean | Whether to show crosshair tags crosshairsXTag | boolean | Whether to show horizontal crosshair tag crosshairsYTag | boolean | Whether to show vertical crosshair tag crosshairsYTagPosition | 'left' \\| 'right' | Position of vertical crosshair tag crosshairsXTagFormatter | (value: any) => string | Formatter for horizontal crosshair tag crosshairsYTagFormatter | (value: any) => string | Formatter for vertical crosshair tag

2. Documentation (site/docs/manual/component/tooltip.en.md & tooltip.zh.md)

  • Updated interaction.tooltip configuration table with new parameters
  • Updated crosshairs detailed property table with new parameters

Usage Example

js
chart.interaction('tooltip', {
  crosshairs: true,
  crosshairsFollow: true,      // Enable crosshairs follow mouse
  crosshairsTag: true,         // Show coordinate tags
  crosshairsXTagFormatter: (v) => v.toFixed(2),
  crosshairsYTagFormatter: (v) => v.toFixed(2),
});

Use Cases

  • K-line (Candlestick) Charts: Real-time crosshair tracking for precise price/time coordinates
  • Financial Analysis: Track mouse position across multiple data series
  • Scientific Charts: Display exact coordinates while exploring data
image
Checklist
  • npm test passes
  • benchmarks are included
  • commit message follows commit guidelines
  • documents are updated
Description of change

@yaohuitc yaohuitc changed the title Feat crosshairs follow feat: Add crosshairs follow mouse feature for Tooltip Mar 29, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces crosshair follow and tag features for tooltips, including documentation and a new K-line example. The implementation involves updating the seriesTooltip logic and adding tag rendering capabilities. Review feedback recommends enhancing type safety in the global polyfill and extracting hardcoded constants for improved maintainability.

Comment thread site/.dumi/global.ts
Comment on lines +7 to +11
'10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (c: any) =>
(
+c ^
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (+c / 4)))
).toString(16),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better type safety and code clarity, it's recommended to avoid using any. The parameter c in the replace callback function can be explicitly typed as string.

Suggested change
'10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (c: any) =>
(
+c ^
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (+c / 4)))
).toString(16),
'10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (c: string) =>
(
+c ^
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (+c / 4)))
).toString(16),

Comment on lines +595 to +671
function updateTag(
root,
key: 'crosshairsTagX' | 'crosshairsTagY',
value: any,
x: number,
y: number,
axis: 'x' | 'y',
tagStyle: Record<string, any> = {},
boundary: { left: number; right: number; top: number; bottom: number },
) {
if (!defined(value)) {
if (key === 'crosshairsTagX') hideTagX(root);
if (key === 'crosshairsTagY') hideTagY(root);
return;
}
const {
formatter,
position,
offsetX,
offsetY,
paddingX,
paddingY,
textStyle,
backgroundStyle,
} = normalizeTagStyle(tagStyle);
const group = showTag(root, key);
const [background, text] = group.childNodes as [Rect, Text];
const textValue = formatter ? formatter(value) : `${value}`;
text.attr({
text: textValue,
x: 0,
y: 0,
fill: '#fff',
fontSize: 10,
fontFamily: 'sans-serif',
...textStyle,
});
const bounds = text.getLocalBounds();
const {
min: [minX, minY],
max: [maxX, maxY],
} = bounds;
const width = maxX - minX;
const height = maxY - minY;
const tagWidth = width + paddingX * 2;
const tagHeight = height + paddingY * 2;
background.attr({
x: 0,
y: 0,
width: tagWidth,
height: tagHeight,
radius: 2,
fill: '#1b1e23',
...backgroundStyle,
});
text.attr({
x: paddingX - minX,
y: paddingY - minY,
});
const px =
axis === 'x'
? x - tagWidth / 2 + offsetX
: position === 'left'
? x - tagWidth - 6 + offsetX
: x + 6 + offsetX;
const py =
axis === 'x' ? y - tagHeight - 6 + offsetY : y - tagHeight / 2 + offsetY;
const clampedX = Math.max(
boundary.left,
Math.min(boundary.right - tagWidth, px),
);
const clampedY = Math.max(
boundary.top,
Math.min(boundary.bottom - tagHeight, py),
);
group.style.transform = `translate(${clampedX}, ${clampedY})`;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The updateTag function contains several hardcoded values that could be extracted into constants for better maintainability and readability:

  • Default Styles: The default styles for the tag's text and background (e.g., fill: '#fff', fontSize: 10, radius: 2) should be defined as constant objects at a higher scope.
  • Magic Numbers: The number 6 is used as an offset for positioning the tag. This should be a named constant (e.g., TAG_OFFSET).

Extracting these values will make the code cleaner and easier to modify in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant