Во многих компонентах Nivo точки выступают ключевыми элементами
визуализации. Особенно это заметно в Line,
ScatterPlot, Bump, Radar и ряде
производных графиков. Размер точки влияет не только на внешний вид
диаграммы, но и на читаемость данных, плотность визуализации и
производительность.
Базовое управление точками осуществляется через свойства:
pointSize
pointColor
pointBorderWidth
pointBorderColor
Пример:
<ResponsiveLine
data={data}
pointSize={10}
pointColor="#4f46e5"
pointBorderWidth={2}
pointBorderColor="#ffffff"
/>
В этом случае:
pointSize задаёт диаметр точки;pointColor определяет цвет заполнения;pointBorderWidth отвечает за толщину границы;pointBorderColor задаёт цвет рамки.Наиболее распространённый вариант — единый размер для всех точек.
pointSize={8}
Такой подход подходит:
Пример:
<ResponsiveLine
data={data}
pointSize={8}
/>
При больших объёмах данных крупные точки начинают перекрывать линии и друг друга.
pointSize={3}
Подобный размер полезен:
Иногда необходимо сделать акцент именно на узлах графика.
pointSize={16}
Крупные точки применяются:
В ScatterPlot размер точки часто зависит от значения
данных.
Пример структуры данных:
const data = [
{
id: "dataset",
data: [
{ x: 10, y: 20, size: 0.5 },
{ x: 15, y: 10, size: 1.2 },
{ x: 30, y: 40, size: 2.1 }
]
}
]
Настройка:
<ResponsiveScatterPlot
data={data}
nodeSize={d => d.data.size * 10}
/>
Здесь размер вычисляется функцией.
Nivo позволяет передавать callback-функции.
pointSize={(point) => {
return point.data.y > 50 ? 14 : 6
}}
Логика может зависеть от:
Пример:
<ResponsiveLine
data={data}
pointSize={(point) => {
if (point.data.y > 100) {
return 18
}
return 6
}}
/>
Размер можно масштабировать относительно диапазона данных.
const getPointSize = (value) => {
const min = 0
const max = 100
return 4 + (value / max) * 12
}
Использование:
pointSize={(point) => getPointSize(point.data.y)}
Подобная техника создаёт эффект bubble-chart даже внутри обычного line-графика.
Иногда точки вовсе не нужны.
pointSize={0}
Это полезно:
Пример:
<ResponsiveLine
data={data}
pointSize={0}
/>
Помимо размеров Nivo поддерживает изменение формы.
Используется свойство:
pointSymbol
Оно принимает React-компонент.
Простейший пример:
const CustomPoint = ({ size, color, borderWidth, borderColor }) => (
<circle
r={size / 2}
fill={color}
stroke={borderColor}
strokeWidth={borderWidth}
/>
)
Подключение:
<ResponsiveLine
data={data}
pointSymbol={CustomPoint}
/>
Вместо круга можно использовать прямоугольник.
const SquarePoint = ({
size,
color,
borderWidth,
borderColor
}) => (
<rect
x={-size / 2}
y={-size / 2}
width={size}
height={size}
fill={color}
stroke={borderColor}
strokeWidth={borderWidth}
/>
)
Использование:
pointSymbol={SquarePoint}
const DiamondPoint = ({
size,
color,
borderWidth,
borderColor
}) => (
<rect
x={-size / 2}
y={-size / 2}
width={size}
height={size}
transform={`rotate(45)`}
fill={color}
stroke={borderColor}
strokeWidth={borderWidth}
/>
)
const TrianglePoint = ({
size,
color,
borderWidth,
borderColor
}) => (
<polygon
points={`
0,-${size / 2}
${size / 2},${size / 2}
-${size / 2},${size / 2}
`}
fill={color}
stroke={borderColor}
strokeWidth={borderWidth}
/>
)
Поскольку pointSymbol представляет собой
React-компонент, допускается использование практически любых
SVG-элементов.
Пример звезды:
const StarPoint = ({ color }) => (
<path
d="M10 1 L12 7 H18 L13 11 L15 17 L10 13 L5 17 L7 11 L2 7 H8 Z"
fill={color}
transform="scale(0.8)"
/>
)
Компонент получает координаты автоматически.
Стандартные параметры:
const CustomPoint = ({
size,
color,
borderWidth,
borderColor
}) => {
return (
<circle
r={size / 2}
fill={color}
stroke={borderColor}
strokeWidth={borderWidth}
/>
)
}
Позиционирование вручную обычно не требуется, поскольку Nivo уже размещает точку в нужной координате.
SVG позволяет создавать более выразительные точки.
const ShadowPoint = ({
size,
color
}) => (
<g>
<circle
r={size / 2 + 2}
fill="rgba(0,0,0,0.15)"
/>
<circle
r={size / 2}
fill={color}
/>
</g>
)
pointColor="rgba(79, 70, 229, 0.5)"
Подобный стиль полезен:
Тонкая белая граница часто улучшает читаемость.
pointBorderWidth={2}
pointBorderColor="#fff"
Особенно эффективно:
Точка может автоматически получать цвет серии.
pointColor={{ from: "serieColor" }}
Пример:
<ResponsiveLine
data={data}
pointColor={{ from: "serieColor" }}
/>
pointBorderColor={{
from: "serieColor",
modifiers: [
["darker", 0.6]
]
}}
Такой подход создаёт более объёмный вид точек.
Форма точки может зависеть от данных.
pointSymbol={(props) => {
if (props.datum.data.y > 50) {
return <TrianglePoint {...props} />
}
return <SquarePoint {...props} />
}}
Nivo поддерживает встроенную анимацию через
motionConfig.
motionConfig="gentle"
Пример:
<ResponsiveLine
data={data}
pointSize={12}
motionConfig="wobbly"
/>
При обновлении данных точки плавно изменяют положение и размеры.
Большое число SVG-элементов может заметно нагружать браузер.
Основные рекомендации:
pointSize={2}
pointSize={0}
Сложные SVG-компоненты:
могут значительно снижать FPS.
Для экстремально больших объёмов данных используется Canvas-версия.
ResponsiveLineCanvas
или:
ResponsiveScatterPlotCanvas
Canvas работает значительно быстрее при тысячах точек.
Пример кастомного символа:
const HoverPoint = ({
size,
color,
isInteractive
}) => (
<circle
r={isInteractive ? size : size / 2}
fill={color}
/>
)
Один из наиболее информативных подходов.
nodeSize={(d) => d.data.importance * 12}
colors={{ scheme: "category10" }}
opacity={0.6}
Такой стиль помогает отображать:
Даже линейный график может визуально напоминать bubble chart.
<ResponsiveLine
data={data}
pointSize={(point) => point.data.volume / 10}
pointColor={{ from: "serieColor" }}
pointBorderWidth={1}
pointBorderColor="#fff"
/>
Точки могут использовать SVG-градиенты.
defs={[
{
id: "gradient",
type: "linearGradient",
colors: [
{ offset: 0, color: "#60a5fa" },
{ offset: 100, color: "#2563eb" }
]
}
]}
pointColor="url(#gradient)"
<ResponsiveLine
data={data}
margin={{
top: 40,
right: 40,
bottom: 60,
left: 80
}}
pointSize={(point) => {
if (point.data.y > 80) return 16
if (point.data.y > 40) return 10
return 6
}}
pointColor={{ from: "serieColor" }}
pointBorderWidth={2}
pointBorderColor={{
from: "serieColor",
modifiers: [["darker", 0.5]]
}}
pointSymbol={TrianglePoint}
enablePoints={true}
useMesh={true}
motionConfig="gentle"
/>
В данном примере реализованы:
useMesh.В ScatterPlot размеры особенно важны, поскольку точки
являются главным визуальным элементом.
Пример:
<ResponsiveScatterPlot
data={data}
nodeSize={(node) => node.data.z * 20}
/>
Поле z часто используется как третье измерение
данных.
Для сложных проектов удобно использовать d3-scale.
import { scaleLinear } from "d3-scale"
const sizeScale = scaleLinear()
.domain([0, 1000])
.range([4, 30])
Использование:
nodeSize={(node) => sizeScale(node.data.value)}
Размеры точек позволяют создавать акценты:
| Размер | Назначение |
|---|---|
| 2–4 | Плотные данные |
| 5–8 | Стандартные графики |
| 9–14 | Акцентированные точки |
| 15+ | Bubble-представление |
| Форма | Когда использовать |
|---|---|
| Круг | Универсальный вариант |
| Квадрат | Технические графики |
| Ромб | Выделение категории |
| Треугольник | Сигналы и события |
| Звезда | Особые точки |
pointSize={30}
Приводит к:
<filter>
<mask>
<clipPath>
Сильно ухудшает производительность.
На тёмных фонах точки могут сливаться с линиями.
pointBorderWidth={0}
Если размер зависит от данных напрямую:
pointSize={(p) => p.data.y}
график может стать нечитабельным. Необходима нормализация диапазона.