Skip to content

Commit

Permalink
feat(parallel-coords): add support for text outline for axes
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed May 11, 2023
1 parent dc58697 commit 564f41e
Show file tree
Hide file tree
Showing 17 changed files with 491 additions and 143 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ build

/packages/*/dist

/tmp

*.lerna_backup

/stats
Expand Down
19 changes: 14 additions & 5 deletions packages/axes/src/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ export const renderAxisToCanvas = <Value extends ScaleValue>(

ctx.textAlign = textAlign
ctx.textBaseline = textBaseline
ctx.font = `${theme.axis.ticks.text.fontWeight ? `${theme.axis.ticks.text.fontWeight} ` : ''}${
theme.axis.ticks.text.fontSize
}px ${theme.axis.ticks.text.fontFamily}`

const textStyle = theme.axis.ticks.text
ctx.font = `${textStyle.fontWeight ? `${textStyle.fontWeight} ` : ''}${textStyle.fontSize}px ${
textStyle.fontFamily
}`

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

if (textStyle.outlineWidth > 0) {
ctx.strokeStyle = textStyle.outlineColor
ctx.lineWidth = textStyle.outlineWidth * 2
ctx.lineJoin = 'round'
ctx.strokeText(`${value}`, 0, 0)
}

if (theme.axis.ticks.text.fill) {
ctx.fillStyle = theme.axis.ticks.text.fill
ctx.fillStyle = textStyle.fill
}

ctx.fillText(String(value), 0, 0)
ctx.fillText(`${value}`, 0, 0)
ctx.restore()
})

Expand Down
81 changes: 49 additions & 32 deletions packages/axes/src/components/Axis.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, memo } from 'react'
import { useMemo, memo, useCallback } from 'react'
import * as React from 'react'
import { useSpring, useTransition, animated } from '@react-spring/web'
import { useTheme, useMotionConfig } from '@nivo/core'
Expand All @@ -7,7 +7,7 @@ import { computeCartesianTicks, getFormatter } from '../compute'
import { AxisTick } from './AxisTick'
import { AxisProps } from '../types'

const Axis = <Value extends ScaleValue>({
export const NonMemoizedAxis = <Value extends ScaleValue>({
axis,
scale,
x = 0,
Expand All @@ -34,6 +34,7 @@ const Axis = <Value extends ScaleValue>({
onClick?: (event: React.MouseEvent<SVGGElement, MouseEvent>, value: Value | string) => void
}) => {
const theme = useTheme()
const legendTextStyle = theme.axis.legend.text

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

Expand Down Expand Up @@ -80,16 +81,33 @@ const Axis = <Value extends ScaleValue>({
}

legendNode = (
<text
transform={`translate(${legendX}, ${legendY}) rotate(${legendRotation})`}
textAnchor={textAnchor}
style={{
dominantBaseline: 'central',
...theme.axis.legend.text,
}}
>
{legend}
</text>
<>
{legendTextStyle.outlineWidth > 0 && (
<text
transform={`translate(${legendX}, ${legendY}) rotate(${legendRotation})`}
textAnchor={textAnchor}
style={{
dominantBaseline: 'central',
...legendTextStyle,
}}
strokeWidth={legendTextStyle.outlineWidth * 2}
stroke={legendTextStyle.outlineColor}
strokeLinejoin="round"
>
{legend}
</text>
)}
<text
transform={`translate(${legendX}, ${legendY}) rotate(${legendRotation})`}
textAnchor={textAnchor}
style={{
dominantBaseline: 'central',
...legendTextStyle,
}}
>
{legend}
</text>
</>
)
}

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

const transition = useTransition<
(typeof ticks)[0],
{ opacity: number; transform: string; textTransform: string }
>(ticks, {
keys: tick => tick.key,
initial: tick => ({
const getAnimatedProps = useCallback(
(tick: (typeof ticks)[0]) => ({
opacity: 1,
transform: `translate(${tick.x},${tick.y})`,
textTransform: `translate(${tick.textX},${tick.textY}) rotate(${tickRotation})`,
}),
from: tick => ({
[tickRotation]
)
const getFromAnimatedProps = useCallback(
(tick: (typeof ticks)[0]) => ({
opacity: 0,
transform: `translate(${tick.x},${tick.y})`,
textTransform: `translate(${tick.textX},${tick.textY}) rotate(${tickRotation})`,
}),
enter: tick => ({
opacity: 1,
transform: `translate(${tick.x},${tick.y})`,
textTransform: `translate(${tick.textX},${tick.textY}) rotate(${tickRotation})`,
}),
update: tick => ({
opacity: 1,
transform: `translate(${tick.x},${tick.y})`,
textTransform: `translate(${tick.textX},${tick.textY}) rotate(${tickRotation})`,
}),
[tickRotation]
)

const transition = useTransition<
(typeof ticks)[0],
{ opacity: number; transform: string; textTransform: string }
>(ticks, {
keys: tick => tick.key,
initial: getAnimatedProps,
from: getFromAnimatedProps,
enter: getAnimatedProps,
update: getAnimatedProps,
leave: {
opacity: 0,
},
Expand Down Expand Up @@ -161,6 +180,4 @@ const Axis = <Value extends ScaleValue>({
)
}

const memoizedAxis = memo(Axis) as typeof Axis

export { memoizedAxis as Axis }
export const Axis = memo(NonMemoizedAxis) as typeof NonMemoizedAxis
19 changes: 17 additions & 2 deletions packages/axes/src/components/AxisTick.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ const AxisTick = <Value extends ScaleValue>({
animatedProps,
}: AxisTickProps<Value>) => {
const theme = useTheme()
const lineStyle = theme.axis.ticks.line
const textStyle = theme.axis.ticks.text

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

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

return (
<animated.g transform={animatedProps.transform} {...props}>
<line x1={0} x2={lineX} y1={0} y2={lineY} style={theme.axis.ticks.line} />
<line x1={0} x2={lineX} y1={0} y2={lineY} style={lineStyle} />
{textStyle.outlineWidth > 0 && (
<animated.text
dominantBaseline={textBaseline}
textAnchor={textAnchor}
transform={animatedProps.textTransform}
style={textStyle}
strokeWidth={textStyle.outlineWidth * 2}
stroke={textStyle.outlineColor}
strokeLinejoin="round"
>
{`${value}`}
</animated.text>
)}
<animated.text
dominantBaseline={textBaseline}
textAnchor={textAnchor}
transform={animatedProps.textTransform}
style={theme.axis.ticks.text}
style={textStyle}
>
{`${value}`}
</animated.text>
Expand Down

0 comments on commit 564f41e

Please sign in to comment.