Thursday 31 December 2020

react-scpring clean scale and translateX transition

The react-swipeable-views library is providing example usages. I want to reuse the coverflow example, but in a react functional comp. way. I almost managed to get it working. However on my implementation, the swipeable element can get stuck during swipe, if you swipe slowly (the scale is not applied anymore). See screenshot:

enter image description here

In the demo of react-swipeable views this is somehow not happening. This example is using react-spring for the animation transitions. I provided a stackblitz demo that is reproducable and maybe you can find out the issue.

component

const useStyles = makeStyles((theme) => ({
  root: {
    background: theme.palette.background.paper,
    padding: theme.spacing(0, 6),
  },
  img: {
    width: 180,
    height: 180,
    display: "block",
    marginBottom: theme.spacing(2),
  },
  container: {
    padding: theme.spacing(2),
    borderRadius: 4,
    justifyContent: "center",
    maxWidth: 320,
    margin: "auto",
  },
  slide: {
    padding: theme.spacing(3, 2),
    color: theme.palette.text.primary,
    alignItems: "center",
    justifyContent: "center",
    flexDirection: "column",
    display: "flex",
  },
}));

const albums = [
  {
    name: "Abbey Road",
    src: "https://picsum.photos/200/300",
  },
  {
    name: "Bat Out of Hell",
    src: "https://picsum.photos/200/300",
  },
  {
    name: "Homogenic",
    src: "https://picsum.photos/200/300",
  },
  {
    name: "Number of the Beast",
    src: "https://picsum.photos/200/300",
  },
  {
    name: "It's Blitz",
    src: "https://picsum.photos/200/300",
  },
  {
    name: "The Man-Machine",
    src: "https://picsum.photos/200/300",
  },
];

export function StatisticSelector() {
  const classes = useStyles();

  const [index, setIndex] = useState(0);
  const [props, start] = useSpring(() => ({
    from: { position: 0 },
  }));

  function handleChangeIndex(indexNum) {
    setIndex(indexNum);
  }

  function handleSwitch(index, type) {
    if (type === "end") {
      start({
        from: { position: props.position.value },
        to: { position: Math.round(index) },
      });
      return;
    }
    props.position.setValue(index);
  }

  function interpolatePositionProps(range, output) {
    return props.position.interpolate({
      range,
      output,
    });
  }

  return (
    <div className={classes.container}>
      <SwipeableViews
        index={index}
        className={classes.root}
        onChangeIndex={handleChangeIndex}
        onSwitching={handleSwitch}
        enableMouseEvents
      >
        {albums.map((album, currentIndex) => {
          const inputRange = albums.map((_, i) => i);
          const scale = interpolatePositionProps(
            inputRange,
            inputRange.map((i) => (currentIndex === i ? 1 : 0.7))
          ).interpolate((x) => `scale(${x})`);

          const opacity = interpolatePositionProps(
            inputRange,
            inputRange.map((i) => (currentIndex === i ? 1 : 0.3))
          );

          const translateX = interpolatePositionProps(
            inputRange,
            inputRange.map((i) => (100 / 2) * (i - currentIndex))
          ).interpolate((x) => `translateX(${x}px)`);

          const scaleAndTranslateX = interpolate(
            [scale, translateX],
            (scale, translateX) => `${scale} ${translateX}`
          );
          return (
            <animated.div
              key={String(currentIndex)}
              className={classes.slide}
              style={Object.assign({
                opacity,
                transform: scaleAndTranslateX,
              })}
            >
              <img className={classes.img} src={album.src} alt="cover" />
              <Button variant="contained" color="primary" size="small">
                Select
              </Button>
            </animated.div>
          );
        })}
      </SwipeableViews>
    </div>
  );
}


from react-scpring clean scale and translateX transition

No comments:

Post a Comment