Crosshair
Basic Configuration
The Crosshair component is a special tooltip designed to work in an XYContainer.
When a user is interacting with the XYContainer and a crosshair is provided, the Crosshair will appear as a vertical line and render circles on the corresponding y values in the dataset.
You can optionally enable a horizontal line with showHorizontalLine (see below).
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
import { VisXYContainer, VisLine, VisAxis, VisAxis, VisCrosshair } from '@unovis/react'
function Component(props) {
const data: DataRecord[] = props.data
const padding = {
top: 5,
bottom: 5
}
const x = (d: DataRecord) => d.x
const y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
return (
<VisXYContainer padding={padding} data={data}>
<VisLine x={x} y={y}/>
<VisAxis type="x"/>
<VisAxis type="y"/>
<VisCrosshair/>
</VisXYContainer>
)
}
@Component({
templateUrl: 'template.html'
})
export class Component {
@Input data: DataRecord[];
padding = {
top: 5,
bottom: 5
}
x = (d: DataRecord) => d.x
y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
}
<vis-xy-container [padding]="padding" [data]="data">
<vis-line [x]="x" [y]="y"></vis-line>
<vis-axis type="x"></vis-axis>
<vis-axis type="y"></vis-axis>
<vis-crosshair></vis-crosshair>
</vis-xy-container>
<script lang='ts'>
import { VisXYContainer, VisLine, VisAxis, VisAxis, VisCrosshair } from '@unovis/svelte'
export let data: DataRecord[]
const padding = {
top: 5,
bottom: 5
}
const x = (d: DataRecord) => d.x
const y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
</script>
<VisXYContainer {padding} {data}>
<VisLine {x} {y}/>
<VisAxis type="x"/>
<VisAxis type="y"/>
<VisCrosshair/>
</VisXYContainer>
<script setup lang="ts">
import { VisXYContainer, VisLine, VisAxis, VisAxis, VisCrosshair } from '@unovis/vue'
const props = defineProps<{ data: DataRecord[] }>()
const padding = {
top: 5,
bottom: 5
}
const x = (d: DataRecord) => d.x
const y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
</script>
<template>
<VisXYContainer :padding="padding" :data="data">
<VisLine :x="x" :y="y" />
<VisAxis type="x" />
<VisAxis type="y" />
<VisCrosshair />
</VisXYContainer>
</template>
import { VisXYContainer, VisLine, VisAxis, VisAxis, VisCrosshair } from '@unovis/solid'
function Component(props) {
const data: DataRecord[] = () => props.data
const padding = {
top: 5,
bottom: 5
}
const x = (d: DataRecord) => d.x
const y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
return (
<VisXYContainer padding={padding} data={data()}>
<VisLine x={x} y={y}/>
<VisAxis type="x"/>
<VisAxis type="y"/>
<VisCrosshair/>
</VisXYContainer>
)
}
import { XYContainer, Line, Axis, Crosshair } from '@unovis/ts'
import { data, DataRecord } from './data'
const x = (d: DataRecord) => d.x
const y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
const crosshair = new Crosshair<DataRecord>({ })
const container = new XYContainer<DataRecord>(node, {
padding: {
top: 5,
bottom: 5
},
components: [new Line({ x, y })],
xAxis: new Axis({ type: "x" }),
yAxis: new Axis({ type: "y" }),
crosshair
}, data)
X and Y accessors
Like other components in you can supply x and y accessors to the Crosshair component to control where it appears in your container.
There's also a dedicated yStacked property for dealing with stacked values.
By default, Crosshair automatically takes the x, y and yStacked settings from XYContainer.
But as soon as you manually specify any of them, the component will expect the rest to be provided as well. For example, if you supply
the x accessor function to your crosshair you'll also have to supply the y or yStacked settings depending on your chart configuration.
See the following example which moves the position of the crosshair line to the right of each bar.
const x: ((d: DataRecord) => number) = d.x + 0.5
const yStacked: ((d: DataRecord) => number)[] = [d => d.y, d => d.y1, d => d.y2]
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisCrosshair x={x} yStacked={yStacked}/>
<vis-crosshair [x]="x" [yStacked]="yStacked"></vis-crosshair>
<VisCrosshair {x} {yStacked}/>
<VisCrosshair :x="x" :yStacked="yStacked" />
<VisCrosshair x={x} yStacked={yStacked}/>
const crosshair = new Crosshair<DataRecord>({ x, yStacked })
Snap Mode
When snapToData is enabled (the default), the snapMode property controls how the nearest datum is selected:
CrosshairSnapMode.X(default): snap along the X axis only. Best for time series, line, and area charts where the crosshair tracks the X position.CrosshairSnapMode.XY: snap to the datum closest to the pointer in both X and Y (measured in pixel space). Best for Scatter charts where many points can share similar X values.
When snapMode is CrosshairSnapMode.XY, hideWhenFarFromPointerDistance uses the full 2D distance to the snapped point (so a point that is close in X but far in Y still counts as far away).
If forceShowAt is set, snapping falls back to CrosshairSnapMode.X because no pointer Y position is available.
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisCrosshair
snapMode="xy"
showHorizontalLine={true}
template={template}
/>
<vis-crosshair
snapMode="xy"
[showHorizontalLine]="true"
[template]="template"
></vis-crosshair>
<VisCrosshair snapMode="xy" showHorizontalLine={true} {template}/>
<VisCrosshair
snapMode="xy"
:showHorizontalLine="true"
:template="template"
/>
<VisCrosshair
snapMode="xy"
showHorizontalLine={true}
template={template}
/>
const crosshair = new Crosshair<DataRecord>({
snapMode: "xy",
showHorizontalLine: true,
template
})
Show/Hide Behavior
By default, the Crosshair component will render if the cursor is within a certain distance in pixels from a valid x value.
You can disable this feature using the hideWhenFarFromPointer attribute.
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisCrosshair hideWhenFarFromPointer={true}/>
<vis-crosshair [hideWhenFarFromPointer]="true"></vis-crosshair>
<VisCrosshair hideWhenFarFromPointer={true}/>
<VisCrosshair :hideWhenFarFromPointer="true" />
<VisCrosshair hideWhenFarFromPointer={true}/>
const crosshair = new Crosshair<DataRecord>({ hideWhenFarFromPointer: true })
hideWhenFarFromPointerDistance
Use the hideWhenFarFromPointerDistance attribute with a length (in pixels) that represents how far the cursor can be from the snapped datum before hiding.
With the default snapMode of CrosshairSnapMode.X, this is the horizontal distance to the crosshair line; with CrosshairSnapMode.XY, it is the full 2D distance to the snapped point.
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisCrosshair hideWhenFarFromPointerDistance={50}/>
<vis-crosshair [hideWhenFarFromPointerDistance]="50"></vis-crosshair>
<VisCrosshair hideWhenFarFromPointerDistance={50}/>
<VisCrosshair :hideWhenFarFromPointerDistance="50" />
<VisCrosshair hideWhenFarFromPointerDistance={50}/>
const crosshair = new Crosshair<DataRecord>({ hideWhenFarFromPointerDistance: 50 })
visibilityThreshold
The Crosshair is only displayed when at least a fraction of its container is visible in the viewport, which prevents it (and its tooltip) from appearing when the chart is mostly scrolled out of view. Charts that are wider or taller than the viewport may never reach the default ratio of 0.35, so you can lower visibilityThreshold, or set it to 0 to always show the Crosshair regardless of how much of the container is on screen.
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisCrosshair visibilityThreshold={0.35}/>
<vis-crosshair [visibilityThreshold]="0.35"></vis-crosshair>
<VisCrosshair visibilityThreshold={0.35}/>
<VisCrosshair :visibilityThreshold="0.35" />
<VisCrosshair visibilityThreshold={0.35}/>
const crosshair = new Crosshair<DataRecord>({ visibilityThreshold: 0.35 })
Horizontal Line
Set showHorizontalLine to true to render a horizontal line in addition to the vertical crosshair.
The line is placed at the Y position of the crosshair circle closest to the pointer (i.e. the snapped data point), or follows the pointer when there are no circles to snap to (for example, when snapToData is false).
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisCrosshair showHorizontalLine={undefined}/>
<vis-crosshair [showHorizontalLine]="undefined"></vis-crosshair>
<VisCrosshair showHorizontalLine={undefined}/>
<VisCrosshair :showHorizontalLine="undefined" />
<VisCrosshair showHorizontalLine={undefined}/>
const crosshair = new Crosshair<DataRecord>({ showHorizontalLine: undefined })
Custom Color
Provide a string or color accessor function to the color attribute to customize the crosshair's point color:
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
function Component(props) {
const data: DataRecord[] = props.data
const color = (d: DataRecord, i: number) => ['red', 'green', 'blue'][i]
return (
<VisCrosshair color={color}/>
)
}
@Component({
template: '<vis-crosshair [color]="color"></vis-crosshair>'
})
export class Component {
@Input data: DataRecord[];
color = (d: DataRecord, i: number) => ['red', 'green', 'blue'][i]
}
<script lang='ts'>
import { VisXYContainer, VisCrosshair } from '@unovis/svelte'
export let data: DataRecord[]
const color = (d: DataRecord, i: number) => ['red', 'green', 'blue'][i]
</script>
<VisCrosshair {color}/>
<script setup lang="ts">
import { VisXYContainer, VisCrosshair } from '@unovis/vue'
const props = defineProps<{ data: DataRecord[] }>()
const color = (d: DataRecord, i: number) => ['red', 'green', 'blue'][i]
</script>
<template>
<VisCrosshair :color="color" />
</template>
function Component(props) {
const data: DataRecord[] = () => props.data
const color = (d: DataRecord, i: number) => ['red', 'green', 'blue'][i]
return (
<VisCrosshair color={color}/>
)
}
const crosshair = new Crosshair<DataRecord>({
color: (d: DataRecord, i: number) => ['red', 'green', 'blue'][i]
})
Custom Stroke Color and Width
Provide values or accessor functions to the strokeColor and strokeWidth attributes to customize the crosshair's circle stroke color and width:
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
function Component(props) {
const data: DataRecord[] = props.data
const strokeColor = (d: DataRecord, i: number) => ['red', 'green', 'blue'][i]
const strokeWidth = (d: DataRecord, i: number) => [1, 2, 3][i]
return (
<VisCrosshair
color="none"
strokeColor={strokeColor}
strokeWidth={strokeWidth}
/>
)
}
@Component({
template: `
<vis-crosshair
color="none"
[strokeColor]="strokeColor"
[strokeWidth]="strokeWidth"
></vis-crosshair>
`
})
export class Component {
@Input data: DataRecord[];
strokeColor = (d: DataRecord, i: number) => ['red', 'green', 'blue'][i]
strokeWidth = (d: DataRecord, i: number) => [1, 2, 3][i]
}
<script lang='ts'>
import { VisXYContainer, VisCrosshair } from '@unovis/svelte'
export let data: DataRecord[]
const strokeColor = (d: DataRecord, i: number) => ['red', 'green', 'blue'][i]
const strokeWidth = (d: DataRecord, i: number) => [1, 2, 3][i]
</script>
<VisCrosshair color="none" {strokeColor} {strokeWidth}/>
<script setup lang="ts">
import { VisXYContainer, VisCrosshair } from '@unovis/vue'
const props = defineProps<{ data: DataRecord[] }>()
const strokeColor = (d: DataRecord, i: number) => ['red', 'green', 'blue'][i]
const strokeWidth = (d: DataRecord, i: number) => [1, 2, 3][i]
</script>
<template>
<VisCrosshair
color="none"
:strokeColor="strokeColor"
:strokeWidth="strokeWidth"
/>
</template>
function Component(props) {
const data: DataRecord[] = () => props.data
const strokeColor = (d: DataRecord, i: number) => ['red', 'green', 'blue'][i]
const strokeWidth = (d: DataRecord, i: number) => [1, 2, 3][i]
return (
<VisCrosshair
color="none"
strokeColor={strokeColor}
strokeWidth={strokeWidth}
/>
)
}
const crosshair = new Crosshair<DataRecord>({
color: "none",
strokeColor: (d: DataRecord, i: number) => ['red', 'green', 'blue'][i],
strokeWidth: (d: DataRecord, i: number) => [1, 2, 3][i]
})
Circle Radius
Use the circleRadius attribute to set the radius of the crosshair circles in pixels. Default value is 4.
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisCrosshair circleRadius={4}/>
<vis-crosshair [circleRadius]="4"></vis-crosshair>
<VisCrosshair circleRadius={4}/>
<VisCrosshair :circleRadius="4" />
<VisCrosshair circleRadius={4}/>
const crosshair = new Crosshair<DataRecord>({ circleRadius: 4 })
Adding a Tooltip
You can render text content for your Crosshair component by providing it with a template property and a Tooltip component within the same container.
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
import { VisXYContainer, VisScatter, VisTooltip, VisAxis, VisAxis, VisCrosshair } from '@unovis/react'
function Component(props) {
const data: DataRecord[] = props.data
const padding = {
top: 5,
bottom: 5
}
const x = (d: DataRecord) => d.x
const y = (d: DataRecord) => d.y
const template = (d: DataRecord) => [d.x, d.y].join(', ')
return (
<VisXYContainer padding={padding} data={data}>
<VisScatter x={x} y={y}/>
<VisTooltip/>
<VisAxis type="x"/>
<VisAxis type="y"/>
<VisCrosshair template={template}/>
</VisXYContainer>
)
}
@Component({
templateUrl: 'template.html'
})
export class Component {
@Input data: DataRecord[];
padding = {
top: 5,
bottom: 5
}
x = (d: DataRecord) => d.x
y = (d: DataRecord) => d.y
template = (d: DataRecord) => [d.x, d.y].join(', ')
}
<vis-xy-container [padding]="padding" [data]="data">
<vis-scatter [x]="x" [y]="y"></vis-scatter>
<vis-tooltip></vis-tooltip>
<vis-axis type="x"></vis-axis>
<vis-axis type="y"></vis-axis>
<vis-crosshair [template]="template"></vis-crosshair>
</vis-xy-container>
<script lang='ts'>
import { VisXYContainer, VisScatter, VisTooltip, VisAxis, VisAxis, VisCrosshair } from '@unovis/svelte'
export let data: DataRecord[]
const padding = {
top: 5,
bottom: 5
}
const x = (d: DataRecord) => d.x
const y = (d: DataRecord) => d.y
const template = (d: DataRecord) => [d.x, d.y].join(', ')
</script>
<VisXYContainer {padding} {data}>
<VisScatter {x} {y}/>
<VisTooltip/>
<VisAxis type="x"/>
<VisAxis type="y"/>
<VisCrosshair {template}/>
</VisXYContainer>
<script setup lang="ts">
import { VisXYContainer, VisScatter, VisTooltip, VisAxis, VisAxis, VisCrosshair } from '@unovis/vue'
const props = defineProps<{ data: DataRecord[] }>()
const padding = {
top: 5,
bottom: 5
}
const x = (d: DataRecord) => d.x
const y = (d: DataRecord) => d.y
const template = (d: DataRecord) => [d.x, d.y].join(', ')
</script>
<template>
<VisXYContainer :padding="padding" :data="data">
<VisScatter :x="x" :y="y" />
<VisTooltip />
<VisAxis type="x" />
<VisAxis type="y" />
<VisCrosshair :template="template" />
</VisXYContainer>
</template>
import { VisXYContainer, VisScatter, VisTooltip, VisAxis, VisAxis, VisCrosshair } from '@unovis/solid'
function Component(props) {
const data: DataRecord[] = () => props.data
const padding = {
top: 5,
bottom: 5
}
const x = (d: DataRecord) => d.x
const y = (d: DataRecord) => d.y
const template = (d: DataRecord) => [d.x, d.y].join(', ')
return (
<VisXYContainer padding={padding} data={data()}>
<VisScatter x={x} y={y}/>
<VisTooltip/>
<VisAxis type="x"/>
<VisAxis type="y"/>
<VisCrosshair template={template}/>
</VisXYContainer>
)
}
import { XYContainer, Scatter, Tooltip, Axis, Crosshair } from '@unovis/ts'
import { data, DataRecord } from './data'
const x = (d: DataRecord) => d.x
const y = (d: DataRecord) => d.y
const crosshair = new Crosshair<DataRecord>({
template: (d: DataRecord) => [d.x, d.y].join(', ')
})
const container = new XYContainer<DataRecord>(node, {
padding: {
top: 5,
bottom: 5
},
components: [new Scatter({ x, y })],
tooltip: new Tooltip({ }),
xAxis: new Axis({ type: "x" }),
yAxis: new Axis({ type: "y" }),
crosshair
}, data)
CSS Variables
The Crosshair component supports additional styling via CSS variables that you can define for your visualization container. For example:
.visualization-container-div {
--vis-crosshair-line-stroke-color: #f88080;
--vis-crosshair-line-stroke-dasharray: 8 4;
--vis-crosshair-circle-stroke-color: #000000;
}
All supported CSS variables and their default values
--vis-crosshair-line-stroke-color: #888;
--vis-crosshair-line-stroke-width: 1px;
--vis-crosshair-line-stroke-opacity: 1;
--vis-crosshair-line-stroke-dasharray: none;
--vis-crosshair-circle-stroke-color: #fff;
--vis-crosshair-circle-stroke-width: 1px;
--vis-crosshair-circle-stroke-opacity: 0.75;
Component Props
| Name | Type | Description |
|---|---|---|
| * required property |