@darrench3140/react-native-sprite-sheet
Version:
An up to date react native sprite sheet for use in animations.
121 lines (119 loc) • 4.47 kB
JavaScript
;
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from 'react';
import { View, StyleSheet, Platform, Image } from 'react-native';
import { Image as ExpoImage } from 'expo-image';
import Animated, { useSharedValue, useAnimatedStyle, withTiming, withRepeat, withSequence, Easing, cancelAnimation } from 'react-native-reanimated';
import { jsx as _jsx } from "react/jsx-runtime";
const AnimatedImage = Platform.OS === 'android' ? Animated.createAnimatedComponent(ExpoImage) : Animated.createAnimatedComponent(Image);
const AnimatedSprite = /*#__PURE__*/forwardRef((props, ref) => {
const {
source,
spriteSheetSize,
fps = 60,
size,
offset = {
x: 0,
y: 1
},
columnRowMapping,
frameSize,
frames,
inLoop = false,
autoPlay = true,
animations,
defaultAnimationName,
styles
} = props;
const [allFrames, setAllFrames] = useState(frames ?? []);
const [currentAnimationName, setCurrentAnimationName] = useState(defaultAnimationName);
const frameIndex = useSharedValue(0);
const toggleAnimation = useCallback((animationName, loop, customFps) => {
if (!animations[animationName]) {
console.warn(`Invalid animation name: ${animationName}`);
return;
}
frameIndex.value = 0; // quick frame reset
setCurrentAnimationName(animationName);
const selectedFramesIndices = animations[animationName];
const animationsSequence = selectedFramesIndices.map((_, index) => withTiming(index, {
duration: 1000 / customFps,
easing: Easing.linear
}));
const numberOfReps = loop ? -1 : 1;
frameIndex.value = withRepeat(withSequence(...animationsSequence), numberOfReps, false);
}, [animations, frameIndex]);
useImperativeHandle(ref, () => ({
startAnimation: (animationName = currentAnimationName, loop = inLoop, customFps = fps) => {
toggleAnimation(animationName, loop, customFps);
},
stopAnimation: () => {
cancelAnimation(frameIndex);
},
getCurrentAnimationName: () => currentAnimationName
}), [toggleAnimation, inLoop, fps, frameIndex, currentAnimationName]);
const animatedStyle = useAnimatedStyle(() => {
const selectedFrames = animations[currentAnimationName]?.map(index => allFrames[index]) ?? [];
const index = Math.floor(frameIndex.value ?? 0);
const frame = selectedFrames[index]?.frame;
if (!frame) return {};
// Calculate the scale factors
const scaleX = size.width / frame.w;
const scaleY = size.height / frame.h;
return {
width: spriteSheetSize.width * scaleX,
// The scaled sprite sheet width
height: spriteSheetSize.height * scaleY,
// The scaled sprite sheet height
transform: [
// Translate to the position of the frame
{
translateX: -(frame.x + (offset?.x ?? 0)) * scaleX
}, {
translateY: -(frame.y + (offset?.y ?? 0)) * scaleY
}]
};
});
useEffect(() => {
if (autoPlay) {
toggleAnimation(currentAnimationName, inLoop, fps);
}
}, [toggleAnimation, autoPlay, currentAnimationName, inLoop, fps, ref]);
useEffect(() => {
// if prop 'frames' is undefined, calculate frames manually
if (!frames) {
if (!columnRowMapping || columnRowMapping.length === 0) throw new Error('columnRowMapping is not set.');
const w = frameSize?.width ?? spriteSheetSize.width / Math.max(...columnRowMapping);
const h = frameSize?.height ?? spriteSheetSize.height / columnRowMapping.length;
const calcFrames = columnRowMapping.flatMap((noOfColumn, rowIndex) => {
return Array.from({
length: noOfColumn
}, (_, colIndex) => ({
frame: {
h: h,
w: w,
x: colIndex * w,
y: rowIndex * h
}
}));
});
setAllFrames(calcFrames);
}
}, []); // eslint-disable-line react-hooks/exhaustive-deps
const containerStyle = StyleSheet.compose(StyleSheet.compose(defaultStyles.container, size), styles);
return /*#__PURE__*/_jsx(View, {
style: containerStyle,
children: /*#__PURE__*/_jsx(AnimatedImage, {
source: source,
style: animatedStyle,
contentFit: 'contain',
resizeMode: 'contain'
})
});
});
const defaultStyles = StyleSheet.create({
container: {
overflow: 'hidden'
}
});
export default /*#__PURE__*/React.memo(AnimatedSprite);
//# sourceMappingURL=AnimatedSprite.js.map