React Hooks로 CSS 그리드에 애니메이션 요소 만들기

일반적으로 grid-rowgrid-columns는 트랜지션이 불가능하여 움직이는 애니메이션 요소를 생성하기 어렵습니다.

계산을 위해 요소 생성을 사용하여 활성화하는 것을 발견했습니다.

예를 들어 UpLabs SmoothBottomBar을 재현하려고 합니다.

그리고 그것은 성공할 수 있습니다

demo & Full Source Code



메이킹 스텝



1. 기본 그리드 생성



먼저 맨 띠를 만듭니다.

시사





전체 코드



https://stackblitz.com/edit/react-ts-animation-grid-1

설명



메인 그리드는 여기입니다. 나는 react-icons 을 사용합니다. 매우 유용합니다.

icons 배열은 까다로울 수 있습니다.

import { FiUser, FiHome, FiInbox } from "react-icons/fi"

export const Menu1 = () => {
  const icons = [FiHome, FiInbox, FiUser]

  return (
    <Container>
      <MenuGrid>
        {icons.map((Icon) => (
          <IconWrap>
            <Icon />
          </IconWrap>
        ))}
      </MenuGrid>
    </Container>
  )
}

2. 계산할 요소를 만듭니다.



다음으로 계산을 위해 셀을 추가합니다.

시사





전체 코드



https://stackblitz.com/edit/react-ts-animation-grid-2

설명



그리드 열에 아이콘을 추가하고 셀 위치 상태 가져오기를 추가해야 합니다.


// Item got props that passed to `grid-column`
const Item = styled.div`
  ${({ x }) => css`
    grid-column: ${x};
  `}
  grid-row: 1;
`

export const Menu2 = () => {
  const [gridPosition, setGridPosition] = useState<number>(1)

  // ...
        {icons.map((Icon, i) => (
          <Item x={i + 1} onMouseOver={(e) => setGridPosition(i + 1)}>
            <IconWrap>
              <Icon />
            </IconWrap>
          </Item>
        ))}
  // ...
}

이름이 PositionCalcurator 인 셀을 추가합니다.


// PositionCalcurator extends Item. That can set row and cell.
const PositionCalcurator = styled(Item)`
  border: 1px solid red; // for debug
`

export const Menu2 = () => {
  const [gridPosition, setGridPosition] = useState<number>(1)
  const calcuratorRef = useRef<HTMLElement>(null) // calucrator has `ref`

  const icons = [FiHome, FiInbox, FiUser]

  return (
    <Container>
      {/* ... */}
        <PositionCalcurator ref={calcuratorRef} x={gridPosition} />
      {/* ... */}
    </Container>
  )
}

3. 애니메이션 커서를 추가합니다.



다음으로 호버 커서를 추가합니다.

시사





전체 코드



https://stackblitz.com/edit/react-ts-animation-grid-3

설명


refPositionCalcurator로 설정했습니다. 이제 그리드 위치와 동일한 계산된 refs rect 위치를 얻을 수 있습니다.

나는 cursorRect 상태를 생성하고 useEffect 와 연결합니다. 이 효과는 변경 시 호출됩니다gridPosition.(그러나 사용 시 경고될 수 있습니다eslint-plugin-react-hooks )

export const Menu3 = () => {
  const [gridPosition, setGridPosition] = useState<number>(1)
  const [cursorRect, setCursor] = useState<null | Rect>(null) // append
  const calcuratorRef = useRef<HTMLElement>(null)
  useEffect(() => {
    if (!calcuratorRef.current) return
    const top = calcuratorRef.current.offsetTop
    const left = calcuratorRef.current.offsetLeft
    const width = calcuratorRef.current.clientWidth
    const height = calcuratorRef.current.clientHeight
    const cursor = { top, left, width, height }
    setCursor(cursor)
  }, [gridPosition])

Cursor를 통과한 cursorRect 구성 요소를 추가합니다. 이 요소는 그리드 및 세트position:absolute 및 애니메이션 속성과 격리되어 있기 때문에 애니메이션을 활성화합니다.


const Cursor = styled.div`
  position: absolute;
  transition: 0.4s;
  transition-timing-function: ease-in-out;
  background: green;
  opacity: 0.5;
  ${({ top, left, width, height }) => css`
    top: ${top}px;
    left: ${left}px;
    width: ${width}px;
    height: ${height}px;
  `};
`
export const Menu3 = () => {
  const [cursorRect, setCursor] = useState<null | Rect>(null) // append

  // ...
  return (
    <Container>
      {/* Append first for z-index! */}
      {cursorRect && <Cursor {...cursorRect} />}

<Cursor>는 z-인덱스 때문에 Container에 첫 번째 요소가 필요합니다.

4. 텍스트 애니메이션.



이것은 관련 그리드 애니메이션이 아니지만 원본을 따릅니다.

시사





전체 대구



https://stackblitz.com/edit/react-ts-animation-grid-4

설명


:hover 의사만 사용하고 싶지만 커서 상태와 동기화할 수 없기 때문에 active 소품을 추가합니다.

export const Menu4 = () => {
  const [gridPosition, setGridPosition] = useState<number>(1)
  const isActive = useCallback((x) => gridPosition === x, [gridPosition])
  const icons = [["Home", FiHome], ["Inbox", FiInbox], ["Profile", FiUser]]
  return (
    {/* ... */}
    {icons.map(([text, Icon], i) => (
      <Item x={i + 1} onMouseOver={(e) => setGridPosition(i + 1)}>
        <AnimateIcon
          x={i + 1}
          onMouseOver={(e) => setGridPosition(i + 1)}
          text={text}
          active={isActive(i + 1)}
        >
          <Icon />
        </AnimateIcon>
      </Item>
    ))}
  )

그리고 변화AnimateIcon
const AnimateIconInner = styled(IconWrap)`
  transition: 0.5s;
  ::after {
    font-size: 0.6em;
    transition: 0.5s;

    overflow: hidden;
    content: attr(data-text);
    ${({ active }) => css`
      width: ${active ? "100%" : "0px"};
    `}
  }
`

const AnimationContainer = styled.div`
  width: auto;
`

const AnimateIcon = ({ x, onMouseOver, active, children, text }) => {
  return (
    <Item x={x} onMouseOver={onMouseOver}>
      <AnimateIconInner active={active} data-text={text}>
        <AnimationContainer>{children}</AnimationContainer>
      </AnimateIconInner>
    </Item>
  )
}

5. 터닝 및 마감.



버전이 바뀌었습니다(첫 번째 데모와 동일).



전체 코드: https://stackblitz.com/edit/react-ts-animation-grid-final

결론



원시 CSS로 움직이는 CSS 그리드를 찾을 수 없습니다.
한편, 그리드를 좌표 계산으로 사용할 수 있다는 것은 유용할 것 같습니다.

록맨 스테이지 셀렉트와 같은 또 다른 패턴을 만듭니다.
https://amination-grid-menu.netlify.com/

CSS 그리드로 행복한 애니메이션!

좋은 웹페이지 즐겨찾기