Ripple Button

12705 단어 React후크
오늘은, 눌려진 위치에 RippleEffect 를 발생시키는 버튼의 구현을 해설합니다.
code: github/$ yarn 1203






컴포넌트의 사용감은 다음과 같습니다.
<RippleButton
  width={'160px'}
  heihgt={'64px'}
  borderRadius={'5px'}
  backgroundColor={'#7089b9'}
  effectSize={160}
>
  <span className="children">
    <SVGInline svg={UP1} />
    UPLOAD
  </span>
</RippleButton>

보시다시피, 스타일 설정을 props로 받아들입니다. 또한 버튼 내부는 자유롭게 콘텐츠를 배포할 수 있도록 props.children을 render합니다.

Custom Hooks 잘라내기



View와 로직을 구분하기 위해 useRippleEffect라는 Custom Hooks를 정의합니다. 반환값에는, 컴퍼넌트에 필요한 계산 프로퍼티과 갱신 핸들러를 포함합니다 (여기에서는, 적용하는 style, 마우스 다운 핸들러, 마우스 업 핸들러). 각 Hooks API에 바인딩된 처리 세부사항은 설명적으로 생략합니다.
function useRippleEffect(props: Props) {
  const [state, update] = useState<State>(...)
  const tx = useMemo(...)
  const ty = useMemo(...)
  const ts = useMemo(...)
  const effectSytle = useMemo(...)
  const handleMouseDown = useCallback(...)
  const handleMouseUp = useCallback(...)
  return {
    effectSytle,
    handleMouseDown,
    handleMouseUp
  }
}

Ref Injection



이전에 ref는 Statefull Component에서만 사용할 수 있었지만 useRef를 사용하여 FC에서 ref를 사용할 수 있습니다. ref 는 Custom Hooks 에 포함하지 않고 「useRippleEffect」를 이용하는 Component 상에서 정의해, 주입합니다. 이는 ref가 여러 Custom Hooks의 관심 대상이 될 수 있기 때문입니다.
const View = (props: Props) => {
  const ref = useRef({} as HTMLButtonElement)
  const {
    handleMouseDown,
    handleMouseUp,
    effectSytle
  } = useRippleEffect({
    ref,
    effectDuration: props.effectDuration
  })
  return (
    <button
      ref={ref}
      className={props.className}
      onMouseDown={handleMouseDown}
      onMouseUp={handleMouseUp}
      onClick={props.onClick}
    >
      <span className="effect" style={effectSytle} />
      {props.children}
    </button>
  )
}

위치 계산



리플은 DOM을 사용하여 표현합니다. 버튼을 누르면 요소의 상대 위치와 버튼 좌표에서 파문 DOM의 중앙 위치를 식별합니다. 이 때, 애니메이션 동력이 되는 「transitionDuration」을 0 으로 지정합니다.
const handleMouseDown = useCallback(
    (event: MouseEvent<HTMLButtonElement>) => {
      event.persist()
      update(_state => {
        if (props.ref.current === null) return _state
        const clickX = event.pageX
        const clickY = event.pageY
        const clientRect = props.ref.current.getBoundingClientRect()
        const positionX = clientRect.left + window.pageXOffset
        const positionY = clientRect.top + window.pageYOffset
        const transformX = clickX - positionX
        const transformY = clickY - positionY
        return {
          ..._state,
          opacity: 0.5,
          transformX,
          transformY,
          transformScale: 0,
          transitionDuration: 0
        }
      })
    },
    []
  )

이번 샘플집에서는, useState 로 취급하는 상태는 적극적으로 object 에 정리하고 있습니다. 요 전날의 게시에 의한 의도 도 물론 있습니다만, 이와 같이 정리해 상태를 갱신할 필요가 있는 경우, state 마다 useState 하는 것보다 편리합니다.

CSS 애니메이션 발화



마우스 업시, 움직이고 싶은 프로퍼티와 함께, transitionDuration 를 부여하면 애니메이션이 발화합니다.
const handleMouseUp = useCallback(
  (event: MouseEvent<HTMLButtonElement>) => {
    event.persist()
    update(_state => ({
      ..._state,
      opacity: 0,
      transformScale: 1,
      transitionDuration: _state.effectDuration
    }))
  },
  []
)

좋은 웹페이지 즐겨찾기