Skip to content

Commit

Permalink
fix(line): animate paths properly
Browse files Browse the repository at this point in the history
  • Loading branch information
wyze committed Oct 22, 2020
1 parent 0affed6 commit 6d2cd27
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 43 deletions.
2 changes: 2 additions & 0 deletions packages/core/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react'
import { OpaqueInterpolation } from 'react-spring'

declare module '@nivo/core' {
export type DatumValue = string | number | Date
Expand Down Expand Up @@ -152,6 +153,7 @@ declare module '@nivo/core' {

export type DataFormatter = (value: DatumValue) => string | number

export function useAnimatedPath(path: string): OpaqueInterpolation<string>
export function useValueFormatter(formatter?: DataFormatter | string): DataFormatter

export type LinearGradientDef = {
Expand Down
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"d3-color": "^1.2.3",
"d3-format": "^1.4.4",
"d3-hierarchy": "^1.1.8",
"d3-interpolate": "^2.0.1",
"d3-scale": "^3.0.0",
"d3-scale-chromatic": "^1.3.3",
"d3-shape": "^1.3.5",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export * from './useAnimatedPath'
export * from './useCurveInterpolation'
export * from './useDimensions'
export * from './useMeasure'
Expand Down
31 changes: 31 additions & 0 deletions packages/core/src/hooks/useAnimatedPath.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { interpolateString } from 'd3-interpolate'
import { useEffect, useMemo, useRef } from 'react'
import { useMotionConfig } from '../motion'
import { useSpring } from 'react-spring'

const usePrevious = value => {
const ref = useRef()

useEffect(() => {
ref.current = value
}, [value])

return ref.current
}

export const useAnimatedPath = path => {
const { animate, config: springConfig } = useMotionConfig()

const previousPath = usePrevious(path)
const interpolator = useMemo(() => interpolateString(previousPath, path), [previousPath, path])

const { value } = useSpring({
from: { value: 0 },
to: { value: 1 },
reset: true,
config: springConfig,
immediate: !animate,
})

return value.interpolate(interpolator)
}
68 changes: 38 additions & 30 deletions packages/line/src/Areas.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,44 +8,52 @@
*/
import React, { memo } from 'react'
import PropTypes from 'prop-types'
import { useSprings, animated } from 'react-spring'
import { useMotionConfig, blendModePropType } from '@nivo/core'
import { useSpring, animated } from 'react-spring'
import { useAnimatedPath, useMotionConfig, blendModePropType } from '@nivo/core'

const Areas = ({ areaGenerator, areaOpacity, areaBlendMode, lines }) => {
const AreaPath = ({ areaBlendMode, areaOpacity, color, fill, path }) => {
const { animate, config: springConfig } = useMotionConfig()

const computedLines = lines.slice(0).reverse()
const animatedPath = useAnimatedPath(path)
const animatedProps = useSpring({
color,
config: springConfig,
immediate: !animate,
})

const springs = useSprings(
computedLines.length,
computedLines.map(line => {
return {
path: areaGenerator(line.data.map(d => d.position)),
color: line.color,
config: springConfig,
immediate: !animate,
}
})
return (
<animated.path
d={animatedPath}
fill={fill ? fill : animatedProps.color}
fillOpacity={areaOpacity}
strokeWidth={0}
style={{
mixBlendMode: areaBlendMode,
}}
/>
)
}

AreaPath.propTypes = {
areaBlendMode: blendModePropType.isRequired,
areaOpacity: PropTypes.number.isRequired,
color: PropTypes.string,
fill: PropTypes.string,
path: PropTypes.string.isRequired,
}

const Areas = ({ areaGenerator, areaOpacity, areaBlendMode, lines }) => {
const computedLines = lines.slice(0).reverse()

return (
<g>
{springs.map((animatedProps, index) => {
const line = computedLines[index]

return (
<animated.path
key={line.id}
d={animatedProps.path}
fill={line.fill ? line.fill : animatedProps.color}
fillOpacity={areaOpacity}
strokeWidth={0}
style={{
mixBlendMode: areaBlendMode,
}}
/>
)
})}
{computedLines.map(line => (
<AreaPath
key={line.id}
path={areaGenerator(line.data.map(d => d.position))}
{...{ areaOpacity, areaBlendMode, ...line }}
/>
))}
</g>
)
}
Expand Down
19 changes: 6 additions & 13 deletions packages/line/src/LinesItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,16 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import React, { memo } from 'react'
import React, { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
import { useSpring, animated } from 'react-spring'
import { useMotionConfig } from '@nivo/core'
import { animated } from 'react-spring'
import { useAnimatedPath } from '@nivo/core'

const LinesItem = ({ lineGenerator, points, color, thickness }) => {
const { animate, config: springConfig } = useMotionConfig()
const path = useMemo(() => lineGenerator(points), [lineGenerator, points])
const animatedPath = useAnimatedPath(path)

const animatedProps = useSpring({
path: lineGenerator(points),
config: springConfig,
immediate: !animate,
})

return (
<animated.path d={animatedProps.path} fill="none" strokeWidth={thickness} stroke={color} />
)
return <animated.path d={animatedPath} fill="none" strokeWidth={thickness} stroke={color} />
}

LinesItem.propTypes = {
Expand Down
24 changes: 24 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5017,6 +5017,18 @@
resolved "https://registry.yarnpkg.com/@types/configstore/-/configstore-2.1.1.tgz#cd1e8553633ad3185c3f2f239ecff5d2643e92b6"
integrity sha1-zR6FU2M60xhcPy8jns/10mQ+krY=

"@types/d3-path@^1":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.9.tgz#73526b150d14cd96e701597cbf346cfd1fd4a58c"
integrity sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ==

"@types/d3-shape@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-2.0.0.tgz#61aa065726f3c2641aedc59c3603475ab11aeb2f"
integrity sha512-NLzD02m5PiD1KLEDjLN+MtqEcFYn4ZL9+Rqc9ZwARK1cpKZXd91zBETbe6wpBB6Ia0D0VZbpmbW3+BsGPGnCpA==
dependencies:
"@types/d3-path" "^1"

"@types/debug@^0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-0.0.29.tgz#a1e514adfbd92f03a224ba54d693111dbf1f3754"
Expand Down Expand Up @@ -9096,6 +9108,11 @@ d3-color@1, d3-color@^1.2.3:
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.2.3.tgz#6c67bb2af6df3cc8d79efcc4d3a3e83e28c8048f"
integrity sha512-x37qq3ChOTLd26hnps36lexMRhNXEtVxZ4B25rL0DVdDsGQIJGB18S7y9XDwlDD6MD/ZBzITCf4JjGMM10TZkw==

"d3-color@1 - 2":
version "2.0.0"
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e"
integrity sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==

d3-delaunay@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-5.1.1.tgz#fa2313f5fff1c6007f7d814276fc6914f370d63c"
Expand Down Expand Up @@ -9146,6 +9163,13 @@ d3-interpolate@1:
dependencies:
d3-color "1"

d3-interpolate@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-2.0.1.tgz#98be499cfb8a3b94d4ff616900501a64abc91163"
integrity sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==
dependencies:
d3-color "1 - 2"

d3-path@1:
version "1.0.7"
resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.7.tgz#8de7cd693a75ac0b5480d3abaccd94793e58aae8"
Expand Down

0 comments on commit 6d2cd27

Please sign in to comment.