비디오 전체화면 구현 디버깅🐛

·2022년 12월 29일
0

이틀 전부터 머리를 뜯어가며 고민하고 있는 이슈...
전체화면 버튼만을 클릭해서 전체화면 시작, 전체화면 끝을 하는 건 이상이 없다.
그런데, ESC를 눌러서 전체화면을 종료할 경우 전체화면 여부를 구분하는 state가 바뀌지 않아 아이콘이 여전히 전체화면일 때 그대로 남아있는 문제가 있다.
event.keyCode를 통해 keyDown 또는 keyUp을 구현해보려고 했으나, key event를 들어먹지 않는 문제가 있었다.
알고 보니 chromium 버그^^
크롬에서 전체화면을 실행하고 ESC로 종료할 경우 key event를 끌고 오지 못하는 버그가 있다

프론트는 아니지만 같은 팀 팀원분께 해당 이슈를 공유하니 조언이자 질문을 주셨다.
1. 크롬의 문제라면 edge나 safari에서는 key event를 가져와서 해결 가능?
2. 그렇다면 유튜브는 전체화면을 어떻게 구현?
그리고 내가 머릿속으로 생각한 하나의 해결방법은 3. 전체화면을 web API로 끌고 오는 것이 아닌, 수동으로 전체화면 기능을 직접 구현해보는 것이다.

이후에 프론트 동기랑 얘길하면서 계속 생각해보니, 내가 너무 어렵게 생각을 해왔던 것 같다.
전체화면 버튼을 눌렀을 때와 key event가 발생했을 때 state를 변경하면 된다고 생각했는데, 반대로 생각하면 결국에 click 이벤트든 key 이벤트든 전체화면 여부를 변경해주는 것이기 때문에 전체화면 여부를 바라보고 있으면 되는 것이다.
(어떤 삽질이든 다 하는게 도움은 되겠지만, 처음부터 더 단순히 생각하는 습관을 길러야할 것 같다.)

📌 전체화면 여부 바라보기

🐛 전체화면 여부에 따라 state 변경해주기

기존에 fullscreen on, off에 따라 state를 넣고 onClick 이벤트에 종속되게 했던 코드와 달리,
좀 더 큰 범위에서 DOM 내에 fullscreenchange를 바라보고 fullscreen일 때와 아닐 때를 구분하여 state 변경을 설정해주어 해결했다.

document.addEventListener("fullscreenchange", (e) => {
    if (document.fullscreenElement) playerContext.setIsScreenFull(true);
    else if (!document.fullscreenElement) playerContext.setIsScreenFull(false);
});

const FullScreenStart = () => {
    playerContext.playerContainer.current.requestFullscreen();
    // playerContext.setIsScreenFull(true);
};

const FullScreenEnd = () => {
    if (document.exitFullscreen) {
      document.exitFullscreen();
      // playerContext.setIsScreenFull(false);
    }
};

const onClickFullScreen = (e) => {
    e.preventDefault();
    if (playerContext.isScreenFull) FullScreenEnd();
    else if (!playerContext.isScreenFull) FullScreenStart();
};

📌 크롬 외의 브라우저에서는?

📍 safari에서 시도해보기

내 노트북에 edge가 없어 우선 safari로 시도해봤다.
결과는... 기존 코드가 chrome 위주로 짜여 있어 safari에선 아예 전체화면 기능이 먹질 않는다..^^
이렇게 새삼 또 크로스 브라우징의 중요성을 깨닫는다.

🐛 크로스 브라우징 해결해보기

  • fullscreenElement : standard
  • webkitFullscreenElement : Chrome, Safari, Opera
  • mozFullScreenElement : Firefox
  • msFullscreenElement : IE/Edge
// typescript
export default function PlayControl() {
  interface FsDocument extends HTMLDocument {
      mozFullScreenElement?: Element;
      msFullscreenElement?: Element;
      webkitFullscreenElement?: Element;
      msExitFullscreen?: () => void;
      mozCancelFullScreen?: () => void;
      webkitExitFullscreen?: () => void;
  }

  interface FsDocumentElement extends HTMLElement {
      msRequestFullscreen?: () => void;
      mozRequestFullScreen?: () => void;
      webkitRequestFullscreen?: () => void;
  }

  const fsDoc: FsDocument = document;
  const fsDocEl: FsDocumentElement = playerContext.playerContainer.current;

  // 전체화면 여부 바라보기
  // 전체화면 상태인지 아닌지 판별해주는 함수
  const isFullScreen = (): boolean => {
    if (
        fsDoc.fullscreenElement ||
        fsDoc.mozFullScreenElement ||
        fsDoc.webkitFullscreenElement ||
        fsDoc.msFullscreenElement
       ) return true;
    else return false;
  };

  // 전체화면 변화를 바라보고 있다.
  fsDoc.addEventListener(
      fsDoc.fullscreenElement
        ? "fullscreenchange"
        : fsDoc.mozFullScreenElement
        ? "mozfullscreenchange"
        : fsDoc.webkitFullscreenElement
        ? "webkitfullscreenchange"
        : fsDoc.msFullscreenElement && "msfullscreenchange",
      () => {
        if (isFullScreen()) playerContext.setIsScreenFull(true);
        else if (!isFullScreen()) playerContext.setIsScreenFull(false);
      }
  );
  
  // 전체화면 모드가 가능한지 아닌지를 보고 에러를 던져준다.
  fsDoc.addEventListener(
      fsDoc.fullscreenElement
        ? "fullscreenerror"
        : fsDoc.mozFullScreenElement
        ? "mozfullscreenerror"
        : fsDoc.webkitFullscreenElement
        ? "webkitfullscreenerror"
        : fsDoc.msFullscreenElement && "msfullscreenerror",
      () => {
        alert("전체화면 보기가 불가능합니다.");
      }
  );

  // 전체화면 버튼 클릭 시 실행되는 함수 (기존엔 start, end 함수 두 개로 나눴으나 하나로 합쳤다)
  const toggleFullScreen = (): void => {
    if (!isFullScreen()) {
       if (fsDocEl.requestFullscreen) fsDocEl.requestFullscreen();
       else if (fsDocEl.mozRequestFullScreen) fsDocEl.mozRequestFullScreen();
       else if (fsDocEl.webkitRequestFullscreen) fsDocEl.webkitRequestFullscreen();
       else if (fsDocEl.msRequestFullscreen) fsDocEl.msRequestFullscreen();
      } else if (fsDoc.exitFullscreen) fsDoc.exitFullscreen();
        else if (fsDoc.mozCancelFullScreen) fsDoc.mozCancelFullScreen();
        else if (fsDoc.webkitExitFullscreen) fsDoc.webkitExitFullscreen();
        else if (fsDoc.msExitFullscreen) fsDoc.msExitFullscreen();
  };

  // 전체화면 버튼 onClick 함수
  const onClickFullScreen = () => {
      toggleFullScreen();
  };

  return (
    <S.FullScreen onClick={onClickFullScreen}>
       {playerContext.isScreenFull ? (
            <FullscreenExitIcon
                fontSize="large"
                sx={{
                  color: "white",
                }}
            />
         ) : (
            <FullscreenIcon
                fontSize="large"
                sx={{
                  color: "white",
                }}
            />
       )}
    </S.FullScreen>
  );
}

===============================================================================

230102 업데이트

전부 수정됐다고 생각했는데 크롬에서 전체화면 모드 클릭 시, 아이콘이 안바뀌고 기본 모드로 돌아왔다가 다시 전체화면을 해줘야 아이콘이 바뀌는 문제가 있었다.
대체 왜...? 심지어 Safari에서는 정상적으로 돌아간다.
프론트 동기랑 낑낑거리며 수정은 했는데 이게 왜 정상적으로 돌아가는지 전혀 모르겠다..^^

isFullscreen 함수에 playerContext.setIsScreenFull((prev) => !prev); 이 구문을 넣어주니 정상작동이 된다. 무슨 차이일까..?
이걸로 작동이 되길래 addEventListener에서 playerContext.setIsScreenFull 구문을 지워봤더니 ESC 키보드 이벤트 발생 시 또 먹통이 되는 문제가 있다.
이유 너무 알고 싶다...

// typescript
export default function PlayControl() {
  interface FsDocument extends HTMLDocument {
      mozFullScreenElement?: Element;
      msFullscreenElement?: Element;
      webkitFullscreenElement?: Element;
      msExitFullscreen?: () => void;
      mozCancelFullScreen?: () => void;
      webkitExitFullscreen?: () => void;
  }

  interface FsDocumentElement extends HTMLElement {
      msRequestFullscreen?: () => void;
      mozRequestFullScreen?: () => void;
      webkitRequestFullscreen?: () => void;
  }

  const fsDoc: FsDocument = document;
  const fsDocEl: FsDocumentElement = playerContext.playerContainer.current;

  // 전체화면 여부 바라보기
  // 전체화면 상태인지 아닌지 판별해주는 함수
  const isFullScreen = (): boolean => {
    if (
      fsDoc.fullscreenElement ||
      fsDoc.mozFullScreenElement ||
      fsDoc.webkitFullscreenElement ||
      fsDoc.msFullscreenElement
    ) {
      playerContext.setIsScreenFull((prev) => !prev); // 이 구문을 넣어주니 정상작동이 된다. 무슨 차이일까..?
      return true;
    } else {
      playerContext.setIsScreenFull((prev) => !prev);
      return false;
    }
  };

  // 전체화면 변화를 바라보고 있다.
  fsDoc.addEventListener(
      fsDoc.fullscreenElement
        ? "fullscreenchange"
        : fsDoc.mozFullScreenElement
        ? "mozfullscreenchange"
        : fsDoc.webkitFullscreenElement
        ? "webkitfullscreenchange"
        : fsDoc.msFullscreenElement && "msfullscreenchange",
      () => {
        if (isFullScreen()) playerContext.setIsScreenFull(true);
        else if (!isFullScreen()) playerContext.setIsScreenFull(false);
      }
  );
  
  // 전체화면 모드가 가능한지 아닌지를 보고 에러를 던져준다.
  fsDoc.addEventListener(
      fsDoc.fullscreenElement
        ? "fullscreenerror"
        : fsDoc.mozFullScreenElement
        ? "mozfullscreenerror"
        : fsDoc.webkitFullscreenElement
        ? "webkitfullscreenerror"
        : fsDoc.msFullscreenElement && "msfullscreenerror",
      () => {
        alert("전체화면 보기가 불가능합니다.");
      }
  );

  // 전체화면 버튼 클릭 시 실행되는 함수 (기존엔 start, end 함수 두 개로 나눴으나 하나로 합쳤다)
  const toggleFullScreen = (): void => {
    if (!isFullScreen()) {
       if (fsDocEl.requestFullscreen) fsDocEl.requestFullscreen();
       else if (fsDocEl.mozRequestFullScreen) fsDocEl.mozRequestFullScreen();
       else if (fsDocEl.webkitRequestFullscreen) fsDocEl.webkitRequestFullscreen();
       else if (fsDocEl.msRequestFullscreen) fsDocEl.msRequestFullscreen();
      } else if (fsDoc.exitFullscreen) fsDoc.exitFullscreen();
        else if (fsDoc.mozCancelFullScreen) fsDoc.mozCancelFullScreen();
        else if (fsDoc.webkitExitFullscreen) fsDoc.webkitExitFullscreen();
        else if (fsDoc.msExitFullscreen) fsDoc.msExitFullscreen();
  };

  // 전체화면 버튼 onClick 함수
  const onClickFullScreen = () => {
      toggleFullScreen();
  };

  return (
    <S.FullScreen onClick={onClickFullScreen}>
       {playerContext.isScreenFull ? (
            <FullscreenExitIcon
                fontSize="large"
                sx={{
                  color: "white",
                }}
            />
         ) : (
            <FullscreenIcon
                fontSize="large"
                sx={{
                  color: "white",
                }}
            />
       )}
    </S.FullScreen>
  );
}

< 참고 : https://stackoverflow.com/questions/48995303/fullscreen-request-on-angular-2-4
https://www.tabnine.com/code/javascript/functions/builtins/Document/webkitFullscreenElement
https://stackoverflow.com/questions/56809298/fullscreenchange-event-isnt-compatible-with-every-browser-looking-for-vanill
https://www.w3schools.com/jsref/event_fullscreenchange.asp >

profile
개발을 개발새발 열심히➰🐶

0개의 댓글