-
transition에 대한 물리엔진 패키지 - react-springSPA/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> ); };
위 코드의 특이점은 타이밍으로 타이머가 있어서 마우스 효과로 애니메이션이 일어난 후에 몇 초 있다가 본래의 상태로 자연스럽게 돌아간다는 것입니다.
짧은 시간 후 저절로 꺼지는 "쓸모없는 기계"와 비슷합니다.
꽤 괜찮아보이지만, 더 나아질 수 있습니다.
위의 효과는 꽤 인공적으로 느껴집니다.
물리엔진으로 사용할 수 있는 React Spring 패키지도 있습니다.
https://www.npmjs.com/package/react-spring
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;
'SPA > REACT' 카테고리의 다른 글
React memo를 사용하기 전에 (0) 2022.11.13 다크모드를 위해서 Context API보다 CSS 변수를 활용하세요 (0) 2022.10.30 리플로우가 일어나는 환경에서 React Suspense 사용해보기 (0) 2022.01.18 [React] useState의 초깃값 지연 (0) 2021.01.01 [React] React를 위한 렌더링 캐시 (0) 2019.01.27