react-native-chart-kit
Version:
Beautiful React Native charts for dashboards, reports, and data-rich mobile apps.
85 lines (84 loc) • 3.31 kB
JavaScript
import { useEffect, useRef, useState } from "react";
import { interpolateLineChartTooltipPosition } from "./tooltip";
const tooltipPositionThreshold = 0.5;
export const useAnimatedTooltipModel = (tooltip) => {
const latestPositionRef = useRef(undefined);
const previousTooltipRef = useRef(undefined);
const [animatedPosition, setAnimatedPosition] = useState(undefined);
useEffect(() => {
let animationFrame = 0;
if (!tooltip) {
latestPositionRef.current = undefined;
previousTooltipRef.current = undefined;
animationFrame = requestAnimationFrame(() => {
setAnimatedPosition(undefined);
});
return () => {
cancelAnimationFrame(animationFrame);
};
}
const targetPosition = { x: tooltip.x, y: tooltip.y };
const currentPosition = latestPositionRef.current ?? targetPosition;
const hasPreviousTooltip = previousTooltipRef.current !== undefined;
const positionAnimationDuration = tooltip.config.positionAnimationDuration;
previousTooltipRef.current = tooltip;
if (!hasPreviousTooltip) {
latestPositionRef.current = targetPosition;
animationFrame = requestAnimationFrame(() => {
setAnimatedPosition(targetPosition);
});
return () => {
cancelAnimationFrame(animationFrame);
};
}
if (positionAnimationDuration <= 0) {
latestPositionRef.current = targetPosition;
animationFrame = requestAnimationFrame(() => {
setAnimatedPosition(targetPosition);
});
return () => {
cancelAnimationFrame(animationFrame);
};
}
const deltaX = Math.abs(currentPosition.x - targetPosition.x);
const deltaY = Math.abs(currentPosition.y - targetPosition.y);
if (deltaX < tooltipPositionThreshold &&
deltaY < tooltipPositionThreshold) {
latestPositionRef.current = targetPosition;
animationFrame = requestAnimationFrame(() => {
setAnimatedPosition(targetPosition);
});
return () => {
cancelAnimationFrame(animationFrame);
};
}
let startTime;
const tick = (timestamp) => {
startTime ?? (startTime = timestamp);
const progress = Math.min((timestamp - startTime) / positionAnimationDuration, 1);
const nextPosition = interpolateLineChartTooltipPosition({
from: currentPosition,
progress,
to: targetPosition
});
latestPositionRef.current = nextPosition;
setAnimatedPosition(nextPosition);
if (progress < 1) {
animationFrame = requestAnimationFrame(tick);
}
};
animationFrame = requestAnimationFrame(tick);
return () => {
cancelAnimationFrame(animationFrame);
};
}, [tooltip]);
if (!tooltip) {
return undefined;
}
const position = animatedPosition ?? { x: tooltip.x, y: tooltip.y };
return {
...tooltip,
x: position.x,
y: position.y
};
};