rn-sprite-sheet
rn-sprite-sheet copied to clipboard
Different sources work incorrect
Hi, great component! Thank you for it very much! But if I have few different sources pasted in spritesheet component it works incorrect because it load and calculate data only in constructor. So I need to update and calc new params when source is changed. frameWidth and frameHeight pasted into getFrameCoords because it is don't updated in state before it is called. Maybe my changes will help someone or you can implement it into library.
export default class SpriteSheet extends React.Component {
static propTypes = {
source: PropTypes.number.isRequired, // source must be required; { uri } will not work
columns: PropTypes.number.isRequired,
rows: PropTypes.number.isRequired,
animations: PropTypes.object.isRequired, // see example
viewStyle: stylePropType, // styles for the sprite sheet container
imageStyle: stylePropType, // styles for the sprite sheet
height: PropTypes.number, // set either height, width, or neither
width: PropTypes.number, // do not set both height and width
onLoad: PropTypes.func
};
static defaultPropTypes = {
columns: 1,
rows: 1,
animations: {}
};
constructor(props) {
super(props);
this.state = {
imageHeight: 0,
imageWidth: 0,
source: null,
defaultFrameHeight: 0,
defaultFrameWidth: 0,
topInputRange: [0, 1],
topOutputRange: [0, 1],
leftInputRange: [0, 1],
leftOutputRange: [0, 1]
};
this.time = new Animated.Value(0);
this.interpolationRanges = {};
let { source, height, width, rows, columns } = this.props;
let image = resolveAssetSource(source);
let ratio = 1;
let imageHeight = image.height;
let imageWidth = image.width;
let frameHeight = image.height / rows;
let frameWidth = image.width / columns;
if (width) {
ratio = (width * columns) / image.width;
imageHeight = image.height * ratio;
imageWidth = width * columns;
frameHeight = (image.height / rows) * ratio;
frameWidth = width;
} else if (height) {
ratio = (height * rows) / image.height;
imageHeight = height * rows;
imageWidth = image.width * ratio;
frameHeight = height;
frameWidth = (image.width / columns) * ratio;
}
Object.assign(this.state, {
imageHeight,
imageWidth,
frameHeight,
frameWidth,
source
});
this.generateInterpolationRanges(frameWidth, frameHeight);
}
///update calculating with new source
updateData(){
const { source, height, width, rows, columns } = this.props;
const image = resolveAssetSource(source);
let ratio = 1;
let imageHeight = image.height;
let imageWidth = image.width;
let frameHeight = image.height / rows;
let frameWidth = image.width / columns;
if (width) {
ratio = (width * columns) / image.width;
frameHeight = Math.floor((image.height / rows) * ratio);
frameWidth = width;
imageHeight = frameHeight*rows//Math.floor(image.height * ratio);
imageWidth = frameWidth*columns//Math.floor(width * columns);
} else if (height) {
ratio = (height * rows) / image.height;
imageHeight = height * rows;
imageWidth = image.width * ratio;
frameHeight = height;
frameWidth = (image.width / columns) * ratio;
}
this.setState({
imageHeight,
imageWidth,
frameHeight,
frameWidth,
source
});
this.generateInterpolationRanges(frameWidth, frameHeight);
}
componentDidUpdate(){
if (this.state.source !== this.props.source) {
this.updateData()
}
}
render() {
const { imageHeight, imageWidth, frameHeight, frameWidth, animationType } = this.state;
const { viewStyle, imageStyle, rows, columns, height, width, source, onLoad} = this.props;
console.log(this.state)
console.log(this.props)
const { top = { in: [0, 0], out: [0, 0] }, left = { in: [0, 0], out: [0, 0] } } =
this.interpolationRanges[animationType] || {};
return (
<View
style={[
viewStyle,
{
height: frameHeight,
width: frameWidth,
overflow: 'hidden'
}
]}
>
<Animated.Image
source={source}
onLoad={onLoad}
style={[
imageStyle,
{
height: imageHeight,
width: imageWidth,
top: this.time.interpolate({
inputRange: top.in,
outputRange: top.out
}),
left: this.time.interpolate({
inputRange: left.in,
outputRange: left.out
})
}
]}
/>
</View>
);
}
generateInterpolationRanges = (frameWidth, frameHeight) => {
let { animations } = this.props;
for (let key in animations) {
const { length } = animations[key];
const input = [].concat(...Array.from({ length }, (_, i) => [i, i + 0.99999999999]));
this.interpolationRanges[key] = {
top: {
in: input,
out: [].concat(
...animations[key].map(i => {
let { y } = this.getFrameCoords(i, frameWidth, frameHeight);
return [y, y];
})
)
},
left: {
in: input,
out: [].concat(
...animations[key].map(i => {
let { x } = this.getFrameCoords(i, frameWidth, frameHeight);
return [x, x];
})
)
}
};
}
};
stop = cb => {
this.time.stopAnimation(cb);
};
play = ({ type, fps, loop, resetAfterFinish, onFinish = () => {} }) => {
let { animations } = this.props;
let { length } = animations[type];
this.setState({ animationType: type }, () => {
let animation = Animated.timing(this.time, {
toValue: length,
duration: (length / fps) * 1000,
easing: Easing.linear
});
this.time.setValue(0);
if (loop) {
Animated.loop(animation).start();
} else {
animation.start(() => {
if (resetAfterFinish) {
this.time.setValue(0);
}
onFinish();
});
}
});
};
getFrameCoords = (i, frameWidth, frameHeight) => {
const { rows, columns } = this.props;
const successionWidth = i * frameWidth;
return {
x: -successionWidth % (columns * frameWidth),
y: -Math.floor(successionWidth / (columns * frameWidth)) * frameHeight
};
};
}
Thank you! Can you make a pr for this so I can see the diffs?
@Lvbnhbq1987 what value does successionWidth at getFrameCoords map to?
Would it not be simpler to edit the source image to work around needing to have multiple images?