Skip to content

Instantly share code, notes, and snippets.

@terrysahaidak
Created April 18, 2022 20:13
Show Gist options
  • Save terrysahaidak/0028878c4422b3d85eb8f55b4c83123b to your computer and use it in GitHub Desktop.
Save terrysahaidak/0028878c4422b3d85eb8f55b4c83123b to your computer and use it in GitHub Desktop.
import {
Canvas,
LinearGradient,
Path,
useDerivedValue,
useValue,
vec,
Skia,
SkPath,
runTiming,
SkiaAnimation,
} from '@shopify/react-native-skia';
import React, {useEffect, useRef, useState} from 'react';
import {
StyleSheet,
Text,
View,
Dimensions,
TouchableOpacity,
} from 'react-native';
const window = Dimensions.get('window');
export type GraphProps = {
height?: number;
width?: number;
currentPath: SkPath;
previousPath: SkPath;
};
export const createGraphPath = (
width: number,
height: number,
steps: number,
round = true,
) => {
const retVal = Skia.Path.Make();
let y = height / 2;
retVal.moveTo(0, y);
const prevPt = {x: 0, y};
for (let i = 0; i < width; i += width / steps) {
// increase y by a random amount between -10 and 10
y += Math.random() * 30 - 15;
y = Math.max(height * 0.2, Math.min(y, height * 0.7));
if (round && i > 0) {
const xMid = (prevPt.x + i) / 2;
const yMid = (prevPt!.y + y) / 2;
retVal.quadTo(prevPt.x, prevPt.y, xMid, yMid);
prevPt.x = i;
prevPt.y = y;
} else {
retVal.lineTo(i, y);
}
}
return retVal;
};
const data = [
{
title: 'Hour',
path: createGraphPath(window.width, 400, 60),
},
{
title: 'Day',
path: createGraphPath(window.width, 400, 60),
},
{
title: 'Month',
path: createGraphPath(window.width, 400, 60),
},
{
title: 'Year',
path: createGraphPath(window.width, 400, 60),
},
];
export const Graph: React.FC<GraphProps> = ({
height = window.height,
width = window.width,
currentPath,
previousPath,
}) => {
const progress = useValue(1);
const [path, setPath] = useState(previousPath);
const [nextPath, setNextPath] = useState(currentPath);
const animationRef = useRef<SkiaAnimation>();
useEffect(() => {
progress.animation = undefined;
// Create new random path
setNextPath(currentPath);
// Start animation
animationRef.current = runTiming(progress, 1, {duration: 350}, () => {
// Update main path to nextpath
setPath(currentPath);
// Reset progress
progress.current = 0;
});
}, [progress, currentPath, previousPath]);
const interpolatedPath = useDerivedValue(
() => nextPath.interpolate(path, progress.current),
[progress, path, nextPath],
);
return (
<View style={styles.chartContainer}>
<Canvas style={styles.graph}>
<Path
path={interpolatedPath}
strokeWidth={4}
style="stroke"
strokeJoin="round"
color="black"
strokeCap="round">
<LinearGradient
start={vec(0, height * 0.5)}
end={vec(width * 0.5, height * 0.5)}
colors={['black', '#cccc66']}
/>
</Path>
</Canvas>
</View>
);
};
function usePrevious(value) {
const prevValue = useRef();
useEffect(() => {
prevValue.current = value;
}, [value]);
return prevValue.current;
}
export default function Interpolation() {
const [currentIndex, setIndex] = useState(0);
const currentPath = data[currentIndex];
const previousPath = data[usePrevious(currentIndex) ?? currentIndex];
return (
<View style={styles.container}>
<Graph currentPath={currentPath.path} previousPath={previousPath.path} />
<Timeline
currentIndex={currentIndex}
timelines={data}
onChange={setIndex}
/>
</View>
);
}
function Timeline({timelines, onChange, currentIndex}) {
return (
<View style={styles.timelineContainer}>
{timelines.map((item, index) => (
<TouchableOpacity key={index} onPress={() => onChange(index)}>
<View style={styles.textContainer}>
<Text
style={[styles.text, currentIndex === index && styles.active]}>
{item.title}
</Text>
</View>
</TouchableOpacity>
))}
</View>
);
}
const styles = StyleSheet.create({
container: {flex: 1, backgroundColor: 'white', justifyContent: 'center'},
chartContainer: {height: 300, width: window.width, backgroundColor: 'white'},
graph: {
flex: 1,
},
timelineContainer: {flexDirection: 'row', padding: 20, alignSelf: 'center'},
textContainer: {padding: 20},
text: {color: 'gray', fontSize: 18, fontWeight: '500'},
active: {color: 'black'},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment