diff --git a/.gitignore b/.gitignore index 1de4442..7af2ca7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ node_modules .cache dist coverage + +# Editor directories and files +.vscode/* +!.vscode/extensions.json diff --git a/example/src/App.tsx b/example/src/App.tsx index b355a9b..081e14b 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -24,6 +24,7 @@ import { import { extent, range, scaleOrdinal } from 'd3' import { makeLayout } from 'yogurt-layout' import { LegacyExamples } from './Legacy' +import { BarchartToTestDisableAnimation } from './BarchartToTestDisableAnimation' const createLinechartDataset = () => { const maxX = Math.random() * 100 @@ -390,6 +391,7 @@ const App = () => { + ) } diff --git a/example/src/BarchartToTestDisableAnimation.tsx b/example/src/BarchartToTestDisableAnimation.tsx new file mode 100644 index 0000000..24ebd01 --- /dev/null +++ b/example/src/BarchartToTestDisableAnimation.tsx @@ -0,0 +1,174 @@ +import { useState } from 'react' +import * as _ from 'lodash-es' +import { makeLayout } from 'yogurt-layout' +import { + Bars, + Cartesian, + CartesianConsumer, + Chart, + Elements, + Grid, + ScaleCategorical, + ScaleContinuous, + Svg, +} from '../../src' + +type Datum = { + label: string + icon: string + color: string + value: number +} + +const iconWidth = 30 +const iconHeight = 30 + +export function BarchartToTestDisableAnimation({}) { + const [dataset, setDataset] = useState(createDataset) + + const layout = makeLayout({ + id: 'svg', + width: 600, + height: 600, + padding: 10, + children: [ + { + id: 'wrapper', + padding: 20, + children: [{ id: 'chart' }], + }, + ], + }) + + return ( +
+
+ +
+ + + + d.label), + paddingInner: 0.1, + }} + nice={true} + > + + + + + + + + + {({ xScale: xScaleTmp, yScale: yScaleTmp }) => { + const xScale = xScaleTmp as ScaleContinuous + const yScale = yScaleTmp as ScaleCategorical + + return ( + + i} + enter={{ opacity: 0, fill: 'tomato' }} + x-data={{ base: 0, to: (d) => d.value }} + y-data={(d) => d.label} + fill={(d) => d.color} + disableAnimationByAttr={{ fill: true }} + /> + i} + tag="image" + enter={{ + // @ts-ignore + href: (d: Datum) => getIconHref(d.icon), + opacity: 0, + width: 0, + height: 0, + }} + x={(d: Datum) => xScale(d.value) - iconWidth - 10} + y={(d: Datum) => yScale(d.label) + yScale.bandwidth() / 2 - iconHeight / 2} + href={(d: Datum) => getIconHref(d.icon)} + opacity={1} + width={iconWidth} + height={iconHeight} + disableAnimation={false} + disableAnimationByAttr={{ href: true }} + /> + + ) + }} + + + + + + + + + +
+ ) +} + +function createDataset() { + const colors = ['#f6cf65', '#FF9C97', '#6b93bd', '#95CFB7', '#D4838F', '#C9B180'] + const iconsAndLabels = [ + { + label: 'Pictogram_3x3-Basketball_Filled_Black_RGB', + icon: 'https://og-icons.ocsddna.net/icons/countries/large/BRA.png', + }, + { + label: 'Pictogram_Gymnastics-Artistic_Outlined_White_RGB', + icon: 'https://og-icons.ocsddna.net/icons/countries/large/ITA.png', + }, + { + label: 'Afghanistan', + icon: 'https://og-icons.ocsddna.net/icons/disciplines/VVO_filled_black.png', + }, + { + label: 'Argentina', + icon: 'https://og-icons.ocsddna.net/icons/disciplines/TEN_filled_black.png', + }, + { + label: 'Australia', + icon: 'https://og-icons.ocsddna.net/icons/disciplines/VVO_filled_black.png', + }, + { + label: 'Italy', + icon: 'https://og-icons.ocsddna.net/icons/disciplines/VVO_filled_black.png', + }, + ] + + const casuallySortedColors = _.shuffle(colors) + const casuallySortedIconsAndLabels = _.shuffle(iconsAndLabels) + + return colors.map((_colorHex, i) => { + const iconAndLabel = casuallySortedIconsAndLabels[i] + const color = casuallySortedColors[i] + + return { + label: i.toString(), + icon: iconAndLabel.icon, + color, + value: _.random(0, 100), + } as Datum + }) +} + +function getIconHref(icon: string) { + return icon +} diff --git a/src/components/AnimatedDataset.js b/src/components/AnimatedDataset.js index 6a615aa..379db2d 100644 --- a/src/components/AnimatedDataset.js +++ b/src/components/AnimatedDataset.js @@ -55,6 +55,7 @@ export function AnimatedDataset({ delay = 0, tweens: unparsedTweens = {}, disableAnimation = false, + disableAnimationByAttr: unparseDisableAnimationByAttr = {}, easing = DEFAULT_EASE, }) { const ref = React.createRef() @@ -67,6 +68,10 @@ export function AnimatedDataset({ const init = mapKeys(unparsedInit, parseAttributeName) const events = mapKeys(unparsedEvents, parseEventName) const tweens = mapKeys(unparsedTweens, parseAttributeName) + const disableAnimationByAttr = mapKeys( + unparseDisableAnimationByAttr, + parseAttributeName + ) const attrsList = Object.keys(attrs).filter((a) => a !== 'text') const tweensList = Object.keys(tweens).filter((a) => a !== 'text') @@ -79,8 +84,8 @@ export function AnimatedDataset({ .selectAll(tag) .data(dataset, keyFn) .join( - (enter) => - enter + (enter) => { + return enter .append(tag) .text(attrs.text) .call((sel) => { @@ -94,26 +99,30 @@ export function AnimatedDataset({ }) }) .call((sel) => { - const tran = disableAnimation - ? sel - : sel - .transition() - .ease(easing) - .delay(delay) - .duration(duration) - attrsList.forEach((a) => { + const shouldDisableAnimation = + disableAnimationByAttr.hasOwnProperty(a) + ? disableAnimationByAttr[a] + : disableAnimation + + const tran = shouldDisableAnimation + ? sel + : sel + .transition(a) + .ease(easing) + .delay(delay) + .duration(duration) + .textTween(tweens.text) tran.attr(a, attrs[a]) }) tweensList.forEach((a) => { tran.attrTween(a, tweens[a]) }) - - tran.textTween(tweens.text) - }), - (update) => - update + }) + }, + (update) => { + return update .text(attrs.text) .call((sel) => { eventsList.forEach((event) => { @@ -121,42 +130,51 @@ export function AnimatedDataset({ }) }) .call((sel) => { - const tran = disableAnimation - ? sel - : sel - .transition() - .ease(easing) - .delay(delay) - .duration(duration) - attrsList.forEach((a) => { + const shouldDisableAnimation = + disableAnimationByAttr.hasOwnProperty(a) + ? disableAnimationByAttr[a] + : disableAnimation + const tran = shouldDisableAnimation + ? sel + : sel + .transition(a) + .ease(easing) + .delay(delay) + .duration(duration) + .textTween(tweens.text) tran.attr(a, attrs[a]) }) tweensList.forEach((a) => { tran.attrTween(a, tweens[a]) }) - - tran.textTween(tweens.text) - }), - (exit) => - exit.call((sel) => { - const tran = disableAnimation - ? sel - : sel.transition().ease(easing).delay(delay).duration(duration) - + }) + }, + (exit) => { + return exit.call((sel) => { oldAttrsList.forEach((a) => { - tran.attr(a, fallback(init[a], oldAttrs[a])) + const shouldDisableAnimation = + disableAnimationByAttr.hasOwnProperty(a) + ? disableAnimationByAttr[a] + : disableAnimation + const tran = shouldDisableAnimation + ? sel + : sel + .transition(a) + .ease(easing) + .delay(delay) + .duration(duration) + .attr(a, fallback(init[a], oldAttrs[a])) + .textTween(tweens.text) + tran.remove() }) tweensList.forEach((a) => { tran.attrTween(a, tweens[a]) }) - - tran.textTween(tweens.text) - - tran.remove() }) + } ) refOldAttrs.current = attrs } diff --git a/src/components/Elements.tsx b/src/components/Elements.tsx index 3179e82..6b41733 100644 --- a/src/components/Elements.tsx +++ b/src/components/Elements.tsx @@ -31,6 +31,8 @@ export interface ElementsProps NativeEventHandlers { data: T[] tag: string + disableAnimation?: boolean + disableAnimationByAttr?: Record } export function Elements({ @@ -41,6 +43,8 @@ export function Elements({ duration, enter, easing, + disableAnimation = false, + disableAnimationByAttr = {}, ...props }: ElementsProps) { const animation = useSanitizedCascadingAnimation({ delay, duration, easing }) @@ -55,6 +59,8 @@ export function Elements({ attrs={attributes} events={events} init={enter} + disableAnimation={disableAnimation} + disableAnimationByAttr={disableAnimationByAttr} {...(animation as any)} /> ) diff --git a/src/lib/types.ts b/src/lib/types.ts index 4a7c6bc..76f01f5 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -82,6 +82,7 @@ export interface AnimationIteratees { delay?: DefaultedIteratee duration?: DefaultedIteratee easing?: (time: number) => number + disableAnimationByAttr?: Record } export interface AnimationProps extends AnimationIteratees { dataKey?: KeyAccessor