Skip to content

Commit ce598c0

Browse files
committed
feat(parallel-coords): add support for text outline for axes
1 parent b82ad59 commit ce598c0

File tree

17 files changed

+491
-143
lines changed

17 files changed

+491
-143
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ build
1212

1313
/packages/*/dist
1414

15+
/tmp
16+
1517
*.lerna_backup
1618

1719
/stats

packages/axes/src/canvas.ts

+14-5
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,11 @@ export const renderAxisToCanvas = <Value extends ScaleValue>(
5959

6060
ctx.textAlign = textAlign
6161
ctx.textBaseline = textBaseline
62-
ctx.font = `${theme.axis.ticks.text.fontWeight ? `${theme.axis.ticks.text.fontWeight} ` : ''}${
63-
theme.axis.ticks.text.fontSize
64-
}px ${theme.axis.ticks.text.fontFamily}`
62+
63+
const textStyle = theme.axis.ticks.text
64+
ctx.font = `${textStyle.fontWeight ? `${textStyle.fontWeight} ` : ''}${textStyle.fontSize}px ${
65+
textStyle.fontFamily
66+
}`
6567

6668
if ((theme.axis.domain.line.strokeWidth ?? 0) > 0) {
6769
ctx.lineWidth = Number(theme.axis.domain.line.strokeWidth)
@@ -100,11 +102,18 @@ export const renderAxisToCanvas = <Value extends ScaleValue>(
100102
ctx.translate(tick.x + tick.textX, tick.y + tick.textY)
101103
ctx.rotate(degreesToRadians(tickRotation))
102104

105+
if (textStyle.outlineWidth > 0) {
106+
ctx.strokeStyle = textStyle.outlineColor
107+
ctx.lineWidth = textStyle.outlineWidth * 2
108+
ctx.lineJoin = 'round'
109+
ctx.strokeText(`${value}`, 0, 0)
110+
}
111+
103112
if (theme.axis.ticks.text.fill) {
104-
ctx.fillStyle = theme.axis.ticks.text.fill
113+
ctx.fillStyle = textStyle.fill
105114
}
106115

107-
ctx.fillText(String(value), 0, 0)
116+
ctx.fillText(`${value}`, 0, 0)
108117
ctx.restore()
109118
})
110119

packages/axes/src/components/Axis.tsx

+49-32
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useMemo, memo } from 'react'
1+
import { useMemo, memo, useCallback } from 'react'
22
import * as React from 'react'
33
import { useSpring, useTransition, animated } from '@react-spring/web'
44
import { useTheme, useMotionConfig } from '@nivo/core'
@@ -7,7 +7,7 @@ import { computeCartesianTicks, getFormatter } from '../compute'
77
import { AxisTick } from './AxisTick'
88
import { AxisProps } from '../types'
99

10-
const Axis = <Value extends ScaleValue>({
10+
export const NonMemoizedAxis = <Value extends ScaleValue>({
1111
axis,
1212
scale,
1313
x = 0,
@@ -34,6 +34,7 @@ const Axis = <Value extends ScaleValue>({
3434
onClick?: (event: React.MouseEvent<SVGGElement, MouseEvent>, value: Value | string) => void
3535
}) => {
3636
const theme = useTheme()
37+
const legendTextStyle = theme.axis.legend.text
3738

3839
const formatValue = useMemo(() => getFormatter(format, scale), [format, scale])
3940

@@ -80,16 +81,33 @@ const Axis = <Value extends ScaleValue>({
8081
}
8182

8283
legendNode = (
83-
<text
84-
transform={`translate(${legendX}, ${legendY}) rotate(${legendRotation})`}
85-
textAnchor={textAnchor}
86-
style={{
87-
dominantBaseline: 'central',
88-
...theme.axis.legend.text,
89-
}}
90-
>
91-
{legend}
92-
</text>
84+
<>
85+
{legendTextStyle.outlineWidth > 0 && (
86+
<text
87+
transform={`translate(${legendX}, ${legendY}) rotate(${legendRotation})`}
88+
textAnchor={textAnchor}
89+
style={{
90+
dominantBaseline: 'central',
91+
...legendTextStyle,
92+
}}
93+
strokeWidth={legendTextStyle.outlineWidth * 2}
94+
stroke={legendTextStyle.outlineColor}
95+
strokeLinejoin="round"
96+
>
97+
{legend}
98+
</text>
99+
)}
100+
<text
101+
transform={`translate(${legendX}, ${legendY}) rotate(${legendRotation})`}
102+
textAnchor={textAnchor}
103+
style={{
104+
dominantBaseline: 'central',
105+
...legendTextStyle,
106+
}}
107+
>
108+
{legend}
109+
</text>
110+
</>
93111
)
94112
}
95113

@@ -103,31 +121,32 @@ const Axis = <Value extends ScaleValue>({
103121
immediate: !animate,
104122
})
105123

106-
const transition = useTransition<
107-
(typeof ticks)[0],
108-
{ opacity: number; transform: string; textTransform: string }
109-
>(ticks, {
110-
keys: tick => tick.key,
111-
initial: tick => ({
124+
const getAnimatedProps = useCallback(
125+
(tick: (typeof ticks)[0]) => ({
112126
opacity: 1,
113127
transform: `translate(${tick.x},${tick.y})`,
114128
textTransform: `translate(${tick.textX},${tick.textY}) rotate(${tickRotation})`,
115129
}),
116-
from: tick => ({
130+
[tickRotation]
131+
)
132+
const getFromAnimatedProps = useCallback(
133+
(tick: (typeof ticks)[0]) => ({
117134
opacity: 0,
118135
transform: `translate(${tick.x},${tick.y})`,
119136
textTransform: `translate(${tick.textX},${tick.textY}) rotate(${tickRotation})`,
120137
}),
121-
enter: tick => ({
122-
opacity: 1,
123-
transform: `translate(${tick.x},${tick.y})`,
124-
textTransform: `translate(${tick.textX},${tick.textY}) rotate(${tickRotation})`,
125-
}),
126-
update: tick => ({
127-
opacity: 1,
128-
transform: `translate(${tick.x},${tick.y})`,
129-
textTransform: `translate(${tick.textX},${tick.textY}) rotate(${tickRotation})`,
130-
}),
138+
[tickRotation]
139+
)
140+
141+
const transition = useTransition<
142+
(typeof ticks)[0],
143+
{ opacity: number; transform: string; textTransform: string }
144+
>(ticks, {
145+
keys: tick => tick.key,
146+
initial: getAnimatedProps,
147+
from: getFromAnimatedProps,
148+
enter: getAnimatedProps,
149+
update: getAnimatedProps,
131150
leave: {
132151
opacity: 0,
133152
},
@@ -161,6 +180,4 @@ const Axis = <Value extends ScaleValue>({
161180
)
162181
}
163182

164-
const memoizedAxis = memo(Axis) as typeof Axis
165-
166-
export { memoizedAxis as Axis }
183+
export const Axis = memo(NonMemoizedAxis) as typeof NonMemoizedAxis

packages/axes/src/components/AxisTick.tsx

+17-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ const AxisTick = <Value extends ScaleValue>({
1616
animatedProps,
1717
}: AxisTickProps<Value>) => {
1818
const theme = useTheme()
19+
const lineStyle = theme.axis.ticks.line
20+
const textStyle = theme.axis.ticks.text
1921

2022
const value = format?.(_value) ?? _value
2123

@@ -34,12 +36,25 @@ const AxisTick = <Value extends ScaleValue>({
3436

3537
return (
3638
<animated.g transform={animatedProps.transform} {...props}>
37-
<line x1={0} x2={lineX} y1={0} y2={lineY} style={theme.axis.ticks.line} />
39+
<line x1={0} x2={lineX} y1={0} y2={lineY} style={lineStyle} />
40+
{textStyle.outlineWidth > 0 && (
41+
<animated.text
42+
dominantBaseline={textBaseline}
43+
textAnchor={textAnchor}
44+
transform={animatedProps.textTransform}
45+
style={textStyle}
46+
strokeWidth={textStyle.outlineWidth * 2}
47+
stroke={textStyle.outlineColor}
48+
strokeLinejoin="round"
49+
>
50+
{`${value}`}
51+
</animated.text>
52+
)}
3853
<animated.text
3954
dominantBaseline={textBaseline}
4055
textAnchor={textAnchor}
4156
transform={animatedProps.textTransform}
42-
style={theme.axis.ticks.text}
57+
style={textStyle}
4358
>
4459
{`${value}`}
4560
</animated.text>

0 commit comments

Comments
 (0)