react-spring clean scale 和 translateX transition

react-spring clean scale and translateX transition

react-swipeable-views 库正在提供 example usages。我想重用 coverflow 示例,但在 react 功能组件中。方法。我几乎设法让它工作。但是,在我的实现中,如果您缓慢滑动(不再应用比例尺),可滑动元素可能会在滑动过程中卡住。见截图:

在 react-swipeable 视图的演示中,这在某种程度上没有发生。此示例使用 react-spring 进行动画转换。我提供了一个可重现的 stackblitz demo,也许您可​​以找出问题所在。

组件

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>
  );
}
function handleSwitch(index, type) {
    if (type === "end") {
      start({
        from: { position: props.position.value },
        to: { position: Math.round(index) }
      });
      /**
       * Solution:
       * Do not stop executing this function and make final value update.
       * Just comment `return` below, and everything will work as expected.
       */
      // return;
    }
    props.position.setValue(index);
}