1

尝试实现键盘触发事件的功能(基于此卡片刷卡示例),似乎无法这样做,因为(我假设)没有重新渲染,因此无法识别状态挂钩/触发down/trigger内部更改useGesture, 任何想法?

我试过了

  • useState & useEffect 设置 keydown 状态并区分它们(未识别)
  • 将类添加到div基于状态(不使用 Spring 插值)
// These two are just helpers, they curate spring data, values that are later being interpolated into css
const to = i => ({
  x: 0,
  y: i * -10,
  scale: 1,
  angle: 25,
  rot: -10 + Math.random() * 20,
  delay: i * 100,
});
const from = i => ({ rot: 0, scale: 1.5, y: -1000 });
// This is being used down there in the view, it interpolates rotation and scale into a css transform
const trans = (r, s, a) =>
  `perspective(1500px) rotateX(${a}deg) rotateY(${r /
    10}deg) rotateZ(${r}deg) scale(${s})`;

const Deck = ({ cards, handleAllSwiped, handleListCard }) => {
  const [listed, setListed] = useState(false);
  const [collected, setCollected] = useState(false);
  const [keypressed, setKeypressed] = useState(() => null);
  // The set flags all the cards that are flicked out
  const [gone] = useState(() => new Set());

  useEffect(
    () => {
      console.log(`_gone_ has changed`);
      console.log({gone})
      setKeypressed(null);
    },
    [gone.size],
  );
  useEffect(
    () => {
      console.log(`_keypressed_ has changed`, { keypressed });
      keypressed !== null && gone.add[cards.length - gone.size - 1];
    },
    [keypressed],
  );

  const handleKeydown = useCallback(e => {
    if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
      return setKeypressed(e.key === 'ArrowLeft' ? 'left' : 'right');
    } else {
      return null;
    }
  });

  useEffect(() => {
    window.addEventListener('keydown', handleKeydown);
    return () => window.removeEventListener('keydown', handleKeydown);
  }, []);

  // Create a bunch of springs that contain x/y-position, rotation and scale - using the helpers above
  const [props, set] = useSprings(cards.length, i => ({
    ...to(i),
    from: from(i),
  }));
  // Create a gesture, we're interested in down-state, delta (current-pos - click-pos), direction and velocity
  const bind = useGesture(
    ({
      args: [index],
      down,
      delta: [xDelta],
      distance,
      direction: [xDir],
      velocity,
    }) => {
      // If you flick hard enough it should trigger the card to fly out
      const trigger = velocity > 0.3 || distance > 200;
      // Direction should either point left or right
      const dir = xDelta < 0 ? -1 : 1;
      const direction = dir > 0 ? `right` : `left`;
      // If button/finger's up and trigger velocity is reached, we flag the card ready to fly out
      if (!down && trigger) {
        gone.add(index);
        direction === 'left' ? handleListCard(index) : null;
      }

      // When all cards are removed, make a reset
      if (!down && gone.size === cards.length) {
        handleAllSwiped();
      }

      if (!down || down == false) {
        setCollected(false);
        setListed(false);
      }

      // useSprings.set reconfigures the springs
      set(i => {
        // We're only interested in changing spring-data for the current spring
        if (index !== i) return;
        const isGone = gone.has(index);
        // When a card is gone it flys out left or right, otherwise it's either dragged to delta, or goes back to zero
        const x = isGone ? (200 + window.innerWidth) * dir : down ? xDelta : 0;
        // How much the card tilts, flicking it harder makes it rotate faster
        const rot = xDelta / 100 + (isGone ? dir * 10 * velocity : 0);
        // Active cards lift up a bit
        const scale = down ? 1.3 : 1;

        const angle = down ? 0 : 25;

        return {
          x,
          rot,
          scale,
          angle,
          delay: undefined,
          config: { friction: 50, tension: down ? 800 : isGone ? 200 : 500 },
        };
      });
    },
  );

  // Now we're just mapping the animated values to our view, that's it. Btw, this component only renders once. :-)

  return (
    <div className={styles.deckRoot}>
      <span
        className={cn(
          styles.dropMarket,
          listed && styles['dropMarket--active'],
        )}>
        <ShoppingCartIcon />
      </span>
      <span
        className={cn(
          styles.dropCollection,
          collected && styles['dropCollection--active'],
        )}>
        <LibraryIcon />
      </span>
      {props.map(({ x, y, rot, scale, angle }, i) => (
        <animated.div
          key={i}
          style={{
            transform: interpolate(
              [x, y],
              (x, y) => `translate3d(${x}px,${y}px,0)`,
            ),
          }}
          >
          {/* This is the card itself, we're binding our gesture to it (and inject its index so we know which is which) */}
          <animated.div
            {...bind(i)}
            style={{
              transform: interpolate([rot, scale, angle], trans),
              // backgroundImage: `url(${cards[i]})`,
            }}>
            {cards[i]}
          </animated.div>
        </animated.div>
      ))}
    </div>
  );
};

4

0 回答 0