Skip to content

Commit

Permalink
feat(chord): migrate arc transitions to react-spring
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed Jan 1, 2022
1 parent a992666 commit ebf01a1
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 99 deletions.
8 changes: 2 additions & 6 deletions packages/chord/src/Chord.tsx
@@ -1,6 +1,5 @@
import { createElement, Fragment, ReactNode } from 'react'
import { Container, SvgWrapper, useDimensions, useTheme } from '@nivo/core'
import { useInheritedColor } from '@nivo/colors'
import { Container, SvgWrapper, useDimensions } from '@nivo/core'
import { BoxLegendSvg } from '@nivo/legends'
import { svgDefaultProps } from './defaults'
import { useChord, useChordSelection, useCustomLayerProps } from './hooks'
Expand Down Expand Up @@ -96,9 +95,6 @@ const InnerChord = ({
ribbonHoverOthersOpacity,
})

const theme = useTheme()
const getArcBorderColor = useInheritedColor(arcBorderColor, theme)

const customLayerProps = useCustomLayerProps({
center,
radius,
Expand Down Expand Up @@ -152,7 +148,7 @@ const InnerChord = ({
arcs={arcs}
arcGenerator={arcGenerator}
borderWidth={arcBorderWidth}
getBorderColor={getArcBorderColor}
borderColor={arcBorderColor}
getOpacity={getArcOpacity}
setCurrent={setCurrentArc}
isInteractive={isInteractive}
Expand Down
29 changes: 14 additions & 15 deletions packages/chord/src/ChordArc.tsx
@@ -1,15 +1,14 @@
import { createElement, memo, useMemo, MouseEvent } from 'react'
import { SpringValues, animated } from '@react-spring/web'
import { useTooltip } from '@nivo/tooltip'
import { ArcDatum, ArcGenerator, ChordCommonProps } from './types'
import { ArcAnimatedProps, ArcDatum, ArcGenerator, ChordCommonProps } from './types'
import { computeArcPath } from './compute'

interface ChordArcProps {
arc: ArcDatum
startAngle: number
endAngle: number
animatedProps: SpringValues<ArcAnimatedProps>
arcGenerator: ArcGenerator
borderWidth: number
getBorderColor: (arc: ArcDatum) => string
opacity: number
setCurrent: (arc: ArcDatum | null) => void
isInteractive: ChordCommonProps['isInteractive']
onMouseEnter?: ChordCommonProps['onArcMouseEnter']
Expand All @@ -22,11 +21,8 @@ interface ChordArcProps {
export const ChordArc = memo(
({
arc,
startAngle,
endAngle,
animatedProps,
borderWidth,
getBorderColor,
opacity,
arcGenerator,
setCurrent,
isInteractive,
Expand Down Expand Up @@ -74,13 +70,16 @@ export const ChordArc = memo(
}, [isInteractive, arc, onClick])

return (
<path
d={arcGenerator({ startAngle, endAngle }) || ''}
fill={arc.color}
fillOpacity={opacity}
<animated.path
d={computeArcPath({
startAngle: animatedProps.startAngle,
endAngle: animatedProps.endAngle,
arcGenerator,
})}
fill={animatedProps.color}
opacity={animatedProps.opacity}
strokeWidth={borderWidth}
stroke={getBorderColor(arc)}
strokeOpacity={opacity}
stroke={animatedProps.borderColor}
onMouseEnter={handleMouseEnter}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
Expand Down
139 changes: 61 additions & 78 deletions packages/chord/src/ChordArcs.tsx
@@ -1,15 +1,15 @@
import { memo } from 'react'
import { TransitionMotion, spring } from 'react-motion'
import { interpolateColor } from '@nivo/colors'
import { useMotionConfig } from '@nivo/core'
import { useTransition } from '@react-spring/web'
import { useMotionConfig, useTheme } from '@nivo/core'
import { useInheritedColor } from '@nivo/colors'
import { ChordArc } from './ChordArc'
import { ArcDatum, ArcGenerator, ChordCommonProps } from './types'
import { ArcDatum, ArcGenerator, ChordCommonProps, ArcAnimatedProps } from './types'

interface ChordArcsProps {
arcs: ArcDatum[]
arcGenerator: ArcGenerator
borderWidth: ChordCommonProps['arcBorderWidth']
getBorderColor: (arc: ArcDatum) => string
borderColor: ChordCommonProps['arcBorderColor']
getOpacity: (arc: ArcDatum) => number
setCurrent: (arc: ArcDatum | null) => void
isInteractive: ChordCommonProps['isInteractive']
Expand All @@ -24,7 +24,7 @@ export const ChordArcs = memo(
({
arcs,
borderWidth,
getBorderColor,
borderColor,
getOpacity,
arcGenerator,
setCurrent,
Expand All @@ -35,82 +35,65 @@ export const ChordArcs = memo(
onClick,
tooltip,
}: ChordArcsProps) => {
const { animate, springConfig: _springConfig } = useMotionConfig()
const { animate, config: springConfig } = useMotionConfig()

if (!animate) {
return (
<>
{arcs.map(arc => {
return (
<ChordArc
key={arc.id}
arc={arc}
arcGenerator={arcGenerator}
startAngle={arc.startAngle}
endAngle={arc.endAngle}
opacity={getOpacity(arc)}
borderWidth={borderWidth}
getBorderColor={getBorderColor}
isInteractive={isInteractive}
setCurrent={setCurrent}
onMouseEnter={onMouseEnter}
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}
onClick={onClick}
tooltip={tooltip}
/>
)
})}
</>
)
}
const theme = useTheme()
const getBorderColor = useInheritedColor(borderColor, theme)

const springConfig = {
..._springConfig,
precision: 0.001,
}
const transition = useTransition<ArcDatum, ArcAnimatedProps>(arcs, {
keys: arc => arc.id,
initial: arc => ({
startAngle: arc.startAngle,
endAngle: arc.endAngle,
color: arc.color,
opacity: getOpacity(arc),
borderColor: getBorderColor(arc),
}),
from: arc => ({
startAngle: arc.startAngle,
endAngle: arc.endAngle,
color: arc.color,
opacity: 0,
borderColor: getBorderColor(arc),
}),
update: arc => ({
startAngle: arc.startAngle,
endAngle: arc.endAngle,
color: arc.color,
opacity: getOpacity(arc),
borderColor: getBorderColor(arc),
}),
leave: arc => ({
startAngle: arc.startAngle,
endAngle: arc.endAngle,
color: arc.color,
opacity: 0,
borderColor: getBorderColor(arc),
}),
expires: true,
config: springConfig,
immediate: !animate,
})

return (
<TransitionMotion
styles={arcs.map(arc => {
return {
key: arc.id,
data: arc,
style: {
startAngle: spring(arc.startAngle, springConfig),
endAngle: spring(arc.endAngle, springConfig),
opacity: spring(getOpacity(arc), springConfig),
...interpolateColor(arc.color, springConfig),
},
}
})}
>
{interpolatedStyles => (
<>
{interpolatedStyles.map(({ key, style, data: arc }) => {
return (
<ChordArc
key={key}
arc={arc}
arcGenerator={arcGenerator}
startAngle={style.startAngle}
endAngle={style.endAngle}
opacity={style.opacity}
borderWidth={borderWidth}
getBorderColor={getBorderColor}
isInteractive={isInteractive}
setCurrent={setCurrent}
onMouseEnter={onMouseEnter}
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}
onClick={onClick}
tooltip={tooltip}
/>
)
})}
</>
)}
</TransitionMotion>
<>
{transition((animatedProps, arc) => (
<ChordArc
key={arc.id}
arc={arc}
arcGenerator={arcGenerator}
animatedProps={animatedProps}
borderWidth={borderWidth}
setCurrent={setCurrent}
isInteractive={isInteractive}
tooltip={tooltip}
onMouseEnter={onMouseEnter}
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}
onClick={onClick}
/>
))}
</>
)
}
)
9 changes: 9 additions & 0 deletions packages/chord/src/compute.ts
Expand Up @@ -9,6 +9,7 @@ import {
RibbonDatum,
RibbonGenerator,
ArcGenerator,
ArcAnimatedProps,
} from './types'
import { OrdinalColorScale } from '@nivo/colors'

Expand Down Expand Up @@ -109,6 +110,14 @@ export const computeChordArcsAndRibbons = ({
return { arcs, ribbons }
}

export const computeArcPath = ({
startAngle,
endAngle,
arcGenerator,
}: SpringValues<Pick<ArcAnimatedProps, 'startAngle' | 'endAngle'>> & {
arcGenerator: ArcGenerator
}) => to([startAngle, endAngle], (startAngle, endAngle) => arcGenerator({ startAngle, endAngle }))

export const computeRibbonPath = ({
sourceStartAngle,
sourceEndAngle,
Expand Down
8 changes: 8 additions & 0 deletions packages/chord/src/types.ts
Expand Up @@ -41,6 +41,14 @@ export interface ArcDatum {
color: string
}

export interface ArcAnimatedProps {
startAngle: number
endAngle: number
color: string
opacity: number
borderColor: string
}

export interface RibbonDatum {
id: string
source: ArcDatum
Expand Down

0 comments on commit ebf01a1

Please sign in to comment.