ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • transition에 대한 물리엔진 패키지 - react-spring
    SPA/REACT 2022. 8. 21. 23:46

    마우스 오버 애니메이션은 응용 프로그램이 동적이고 반응적으로 느껴지도록 하는 좋은 방법입니다.

    작은 적용으로 전체 제품을 업그레이드 할 수 있는 디테일입니다.

    마우스 오버를 사용하지 않고 키보드로 웹을 사용하는 사람들은 Enter를 눌러서 마우스 오버 효과를 활성화 할 수 있습니다.

    const Boop = ({ rotation = 0, timing = 150, children }) => {
      const [isBooped, setIsBooped] = React.useState(false);
      const style = {
        display: 'inline-block',
        backfaceVisibility: 'hidden',
        transform: isBooped
          ? `rotate(${rotation}deg)`
          : `rotate(0deg)`,
        transition: `transform ${timing}ms`,
      };
      React.useEffect(() => {
        if (!isBooped) {
          return;
        }
        const timeoutId = window.setTimeout(() => {
          setIsBooped(false);
        }, timing);
        return () => {
          window.clearTimeout(timeoutId);
        };
      }, [isBooped, timing]);
      const trigger = () => {
        setIsBooped(true);
      };
      return (
        <span onMouseEnter={trigger} style={style}>
          {children}
        </span>
      );
    };

    위 코드의 특이점은 타이밍으로 타이머가 있어서 마우스 효과로 애니메이션이 일어난 후에 몇 초 있다가 본래의 상태로 자연스럽게 돌아간다는 것입니다.

    짧은 시간 후 저절로 꺼지는 "쓸모없는 기계"와 비슷합니다.

    Edit react-animation-hover

    꽤 괜찮아보이지만, 더 나아질 수 있습니다.

    위의 효과는 꽤 인공적으로 느껴집니다.

    물리엔진으로 사용할 수 있는 React Spring 패키지도 있습니다.

    https://www.npmjs.com/package/react-spring 

     

    react-spring

    <p align="center"> <img src="https://i.imgur.com/QZownhg.png" width="240" /> </p>. Latest version: 9.5.2, last published: a month ago. Start using react-spring in your project by running `npm i react-spring`. There are 1552 other projects in the npm regist

    www.npmjs.com

    import { animated, useSpring } from "react-spring";
    import { useEffect, useState } from "react";
    export const BoopSpring = ({ rotation = 0, timing = 150, children }) => {
      const [isBooped, setIsBooped] = useState(false);
      const style = useSpring({
        display: "inline-block",
        backfaceVisibility: "hidden",
        transform: isBooped ? `rotate(${rotation}deg)` : `rotate(0deg)`
      });
      useEffect(() => {
        if (!isBooped) {
          return;
        }
        const timeoutId = window.setTimeout(() => {
          setIsBooped(false);
        }, timing);
        return () => {
          window.clearTimeout(timeoutId);
        };
      }, [isBooped, timing]);
      const trigger = () => {
        setIsBooped(true);
      };
      return (
        <animated.span onMouseEnter={trigger} style={style}>
          {children}
        </animated.span>
      );
    };

    패키지를 설치한 후에, 직접 style을 useSpring hook을 통해서 animated 컴포넌트로 전달을 해주었습니다.

     

    조금은 더 자연스러워졌죠?

    느린 느낌이 있어서 조금 더 수정합시다.

    const style = useSpring({
      display: 'inline-block',
      backfaceVisibility: 'hidden',
      transform: isBooped
        ? `rotate(${rotation}deg)`
        : `rotate(0deg)`,
      config: {
        tension: 300,
        friction: 10,
      },
    });

     

    혹자는 setTimeout 대신에 React Spring API에서 onReset Callback을 사용하는 것이 어떤가에 대해서 말하곤 합니다.

    하지만, React Spring의 물리학적 장점을 위해서 Timeout을 사용하는 것이 더 낫다고 생각합니다.

    1. 마우스 오버가 연속으로 일어났을 때 완전히 멈추기 전에 기본 위치로 다시 잡아당겨야하기 때문에 timeout을 clear해서 잡아당기고 싶습니다.

    2. onReset의 일반적인 문제는 애니메이션이 끝날 때 까지 대기한다는 점입니다.

     

    아래 출처를 클릭해서 trigger함수에 대해서 useCallback을 사용해서 최적화도 해보세요 : )

    PrefersReducedMotion을 통해서 a11y도 대응해보세요.

     

    해당 작업들을 추가로 한 뒤의 UseSpring 완성 Hooks

    import React from 'react';
    import { useSpring } from 'react-spring';
    // UPDATE this path to your copy of the hook!
    // Source here: https://joshwcomeau.com/snippets/react-hooks/use-prefers-reduced-motion
    import usePrefersReducedMotion from '@hooks/use-prefers-reduced-motion.hook';
    function useBoop({
      x = 0,
      y = 0,
      rotation = 0,
      scale = 1,
      timing = 150,
      springConfig = {
        tension: 300,
        friction: 10,
      },
    }) {
      const prefersReducedMotion = usePrefersReducedMotion();
      const [isBooped, setIsBooped] = React.useState(false);
      const style = useSpring({
        transform: isBooped
          ? `translate(${x}px, ${y}px)
             rotate(${rotation}deg)
             scale(${scale})`
          : `translate(0px, 0px)
             rotate(0deg)
             scale(1)`,
        config: springConfig,
      });
      React.useEffect(() => {
        if (!isBooped) {
          return;
        }
        const timeoutId = window.setTimeout(() => {
          setIsBooped(false);
        }, timing);
        return () => {
          window.clearTimeout(timeoutId);
        };
      }, [isBooped]);
      const trigger = React.useCallback(() => {
        setIsBooped(true);
      }, []);
      let appliedStyle = prefersReducedMotion ? {} : style;
      return [appliedStyle, trigger];
    }
    export default useBoop;

     

     

     

     

     

    출처 : https://www.joshwcomeau.com/react/boop/

Designed by Tistory.