Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Different sources work incorrect #7

Open
Lvbnhbq1987 opened this issue Mar 25, 2019 · 2 comments
Open

Different sources work incorrect #7

Lvbnhbq1987 opened this issue Mar 25, 2019 · 2 comments

Comments

@Lvbnhbq1987
Copy link

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
    };
  };
}
@mileung
Copy link
Owner

mileung commented Mar 25, 2019

Thank you! Can you make a pr for this so I can see the diffs?

@elisechant
Copy link
Contributor

@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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants