[Vue.js] Intersection Observer로 PIP 모드 구현하기

수경·2021년 3월 15일
0

Dev Log

목록 보기
1/5
post-thumbnail

pip : picture in picture의 약자

유튜브를 사용하다보면 위 사진과 같이 fix된 형태의 박스를 볼 수 있다.

이런 형태로 웹에서 스크롤을 해도
1) 동영상이 사라지지 않고
2) 텍스트 컨텐츠에 집중할 수 있게 하기 위한 레이아웃을 만들어보았다.

처음에는 window scroll 이벤트와 resize 이벤트를 vue에 mounted 된 순간부터 계속 감지하여 content 엘리먼트의 top이 0이 된 순간 pip 모드로 변경해주었었다. scroll 이벤트를 끊임 없이 감지할 뿐만 아니라 height와 width도 그 순간 순간 변화를 시켜주고 있던 코드라 아무리 부드러운 애니메이션을 추가해도 무언가 뻑뻑함 감이 있었다. 그러다 리액트 공부를 하며 Intersection Observer API를 알게 되었고 엘리먼트의 가시성을 감시하는 역할을 하기 때문에 pip 모드도 충분히 가능 할 것이라 생각했다. 역시 어렵지 않게 구현해낼 수 있었다!

[IntersectionObserver 생성] new IntersectionObserver(callback[, options]);

setPIPLayout() {
  const menuBox = document.getElementById('menubar')

  const observer = new IntersectionObserver(this.handleIntersect, {
    threshold: 0.9,
  })
  observer.observe(menuBox)
},

(처음에는 단순히 youtube box의 가시성을 체크해야 한다고 생각해서 observer에 youtube box를 얹어주려했는데 youtube box의 가시성은 사라지는 것이 아니라 사이즈가 변하는 것이기 때문에 아래에 이미지에 보이는 MenuBar 가 root를 살짝 벗어나면 pip 모드로 변할 수 있게 하였다.)

IntersectionObserver의 옵션에는 root, rootMargin, threshold가 있다.

root: 엘리먼트의 가시성은 root로 선언 된 뷰로 부터 교차되어 있는지 확인 한다. 기본 값은 브라우저 뷰포트 이다.
rootMargin: root의 margin 값, css와 유사하게 사용한다. 기본 값은 0
threshold: 가시성의 퍼센테이지를 나타낸다. 단일, 배열 둘 다 선언 가능하다.

(threshhold 값을 제외하고는 기본 값을 사용하였기 때문에 따로 선언하지 않았다.)

처리할 callback 메소드

handleIntersect(entries) {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
    //menuBox가 화면에 보이면
      this.isPIP_Mode = false
    } else {
    //menuBox가 화면에 보이지 않을 때 PIP 모드
      this.isPIP_Mode = true
    }
  })
},

class binding을 위해 isPIP_Mode 변수에 값을 담아줬다.

<div
  id="youtube-box"
  :class="[isPIP_Mode ? 'pip-mode-box' : 'default-mode-box']"
>

...

</div>

scroll 이벤트를 감지하여 레이아웃 사이즈를 js내에서 하나하나 변경해줄때보다 훨씬 가벼워진 느낌이다.
물론 코드 길이도 아주 획기적으로 줄었다.

뿌듯뿌듯 😆

profile
developer

0개의 댓글