<동영상> 뿐만 아니라 모든 요소에 대한 PIP 모드

François Beaufort
François Beaufort

브라우저 지원

  • Chrome: 116
  • Edge: 116.
  • Firefox: 지원되지 않음
  • Safari: 지원되지 않음

소스

Document Picture-in-Picture API를 사용하면 임의의 HTML 콘텐츠로 채울 수 있는 상시 사용 설정 창을 열 수 있습니다. HTML <video> 요소만 PIP 창에 배치할 수 있는 기존 <video>용 PIP API를 확장합니다.

Document Picture-in-Picture API의 PIP 창은 window.open()를 통해 열린 빈 동일한 출처 창과 비슷하지만 몇 가지 차이점이 있습니다.

  • PIP 모드 창이 다른 창 위에 떠 있습니다.
  • PIP 모드 창이 여는 창보다 오래 지속되지 않습니다.
  • PIP 모드 창을 탐색할 수 없습니다.
  • 웹사이트에서 PIP 창 위치를 설정할 수 없습니다.
Sintel 트레일러 동영상을 재생하는 PIP 모드 창
Document Picture-in-Picture API로 만든 PIP 창(데모).

현재 상태

단계 상태
1. 설명 동영상 만들기 완전함
2. 사양의 초안 작성 진행 중
3. 의견 수집 및 디자인 개선 진행 중
4. 오리진 트라이얼 완전함
5. 실행 완료(데스크톱)

사용 사례

맞춤 동영상 플레이어

웹사이트는 기존 <video>용 PIP API를 사용하여 PIP 동영상 환경을 제공할 수 있지만 매우 제한적입니다. 기존 PIP 창은 입력을 거의 허용하지 않으며 스타일 지정 기능이 제한적입니다. 웹사이트는 전체 문서를 PIP로 표시하여 맞춤 컨트롤 및 입력(예: 자막, 재생목록, 시간 스크러버, 동영상에 대한 좋아요 및 싫어요)을 제공하여 사용자의 PIP 동영상 환경을 개선할 수 있습니다.

화상 회의

사용자는 다양한 이유 (예: 통화에 다른 탭 표시 또는 멀티태스킹)로 화상 회의 세션 중에 브라우저 탭을 닫아도 통화 화면은 계속 보고 싶어 하는 경우가 많으므로 PIP의 주요 사용 사례입니다. 다시 한번 강조하지만, <video>용 PIP API를 통해 화상 회의 웹사이트에서 제공할 수 있는 현재 환경은 스타일과 입력에 제한이 있습니다. 전체 문서를 PIP로 표시하면 웹사이트에서 캔버스 해킹에 의존하지 않고도 여러 동영상 스트림을 단일 PIP 창으로 쉽게 결합하고 메시지 보내기, 다른 사용자 음소거, 손 들기와 같은 맞춤 컨트롤을 제공할 수 있습니다.

생산성

연구에 따르면 사용자가 웹에서 생산성을 발휘하려면 더 많은 방법이 필요합니다. PIP 모드의 문서를 사용하면 웹 앱이 더 많은 작업을 유연하게 처리할 수 있습니다. 이제 텍스트 편집, 메모 작성, 할 일 목록, 메시지 및 채팅, 디자인 및 개발 도구 등 웹 앱에서 항상 콘텐츠에 액세스할 수 있습니다.

인터페이스

속성

documentPictureInPicture.window
현재 PIP 모드 창이 있는 경우 이를 반환합니다. 그렇지 않은 경우 null을 반환합니다.

메서드

documentPictureInPicture.requestWindow(options)

PIP 모드 창이 열릴 때 확인하는 프라미스를 반환합니다. 사용자 동작 없이 호출되면 프라미스가 거부됩니다. options 사전에는 다음과 같은 선택적 구성원이 포함됩니다.

width
PIP 모드 창의 초기 너비를 설정합니다.
height
PIP 모드 창의 초기 높이를 설정합니다.
disallowReturnToOpener
참인 경우 PIP 모드 창의 '탭으로 돌아가기' 버튼을 숨깁니다. 기본값은 false입니다.
preferInitialWindowPlacement
true인 경우 기본 위치와 크기로 PIP 모드 창을 엽니다. 기본값은 false입니다.

이벤트

documentPictureInPicture.onenter
PIP 모드 창이 열릴 때 documentPictureInPicture에서 실행됩니다.

다음 HTML은 맞춤 동영상 플레이어와 버튼 요소를 설정하여 PIP 모드 창에서 동영상 플레이어를 엽니다.

<div id="playerContainer">
  <div id="player">
    <video id="video"></video>
  </div>
</div>
<button id="pipButton">Open Picture-in-Picture window</button>

PIP 모드 창 열기

다음 JavaScript는 사용자가 버튼을 클릭하여 빈 PIP 창을 열 때 documentPictureInPicture.requestWindow()를 호출합니다. 반환된 프로미스는 PIP 모드 창 JavaScript 객체로 확인됩니다. 동영상 플레이어는 append()를 사용하여 해당 창으로 이동합니다.

pipButton.addEventListener('click', async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});

PIP 모드 창 크기 설정

PIP 창 크기를 설정하려면 documentPictureInPicture.requestWindow()widthheight 옵션을 원하는 PIP 창 크기로 설정합니다. 옵션 값이 너무 크거나 작아서 사용자 친화적인 창 크기에 맞지 않는 경우 Chrome에서 옵션 값을 제한할 수 있습니다.

pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window whose size is
  // the same as the player's.
  const pipWindow = await documentPictureInPicture.requestWindow({
    width: player.clientWidth,
    height: player.clientHeight,
  });

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});

PIP 모드 창의 '탭으로 돌아가기' 버튼 숨기기

사용자가 플레이어 탭으로 돌아갈 수 있는 PIP 창의 버튼을 숨기려면 documentPictureInPicture.requestWindow()disallowReturnToOpener 옵션을 true로 설정합니다.

pipButton.addEventListener("click", async () => {
  // Open a Picture-in-Picture window which hides the "back to tab" button.
  const pipWindow = await documentPictureInPicture.requestWindow({
    disallowReturnToOpener: true,
  });
});

기본 위치와 크기로 PIP 모드 창 열기

이전 PIP 모드의 위치나 크기를 재사용하지 않으려면 documentPictureInPicture.requestWindow()preferInitialWindowPlacement 옵션을 true로 설정합니다.

pipButton.addEventListener("click", async () => {
  // Open a Picture-in-Picture window in its default position / size.
  const pipWindow = await documentPictureInPicture.requestWindow({
    preferInitialWindowPlacement: true,
  });
});

스타일 시트를 PIP 모드 창에 복사

출처 창에서 모든 CSS 스타일 시트를 복사하려면 문서에 명시적으로 연결되거나 삽입된 styleSheets를 반복하여 PIP 창에 추가합니다. 이 사본은 일회성이므로

pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();

  // Copy style sheets over from the initial document
  // so that the player looks the same.
  [...document.styleSheets].forEach((styleSheet) => {
    try {
      const cssRules = [...styleSheet.cssRules].map((rule) => rule.cssText).join('');
      const style = document.createElement('style');

      style.textContent = cssRules;
      pipWindow.document.head.appendChild(style);
    } catch (e) {
      const link = document.createElement('link');

      link.rel = 'stylesheet';
      link.type = styleSheet.type;
      link.media = styleSheet.media;
      link.href = styleSheet.href;
      pipWindow.document.head.appendChild(link);
    }
  });

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});

PIP 모드 창 닫힘 처리

"pagehide" 이벤트를 수신 대기하여 PIP 모드 창이 언제 닫히는지 알 수 있습니다 (웹사이트에서 시작했거나 사용자가 수동으로 창을 닫음). 이벤트 핸들러는 여기에 표시된 것처럼 PIP 창에서 요소를 다시 가져오는 데 적합합니다.

pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);

  // Move the player back when the Picture-in-Picture window closes.
  pipWindow.addEventListener("pagehide", (event) => {
    const playerContainer = document.querySelector("#playerContainer");
    const pipPlayer = event.target.querySelector("#player");
    playerContainer.append(pipPlayer);
  });
});

close() 메서드를 사용하여 프로그래매틱 방식으로 PIP 모드 창을 닫습니다.

// Close the Picture-in-Picture window programmatically. 
// The "pagehide" event will fire normally.
pipWindow.close();

웹사이트가 PIP 모드로 전환될 때 듣기

documentPictureInPicture에서 "enter" 이벤트를 수신하여 PIP 창이 열릴 때를 알 수 있습니다. 이 이벤트에는 PIP 모드 창에 액세스하는 window 객체가 포함됩니다.

documentPictureInPicture.addEventListener("enter", (event) => {
  const pipWindow = event.window;
});

PIP 모드 창의 요소에 액세스

아래와 같이 documentPictureInPicture.requestWindow()에서 반환된 객체 또는 documentPictureInPicture.window를 사용하여 PIP 모드 창의 요소에 액세스합니다.

const pipWindow = documentPictureInPicture.window;
if (pipWindow) {
  // Mute video playing in the Picture-in-Picture window.
  const pipVideo = pipWindow.document.querySelector("#video");
  pipVideo.muted = true;
}

PIP 모드 창의 이벤트 처리

JavaScript에서 평소와 같이 버튼과 컨트롤을 만들고 "click"와 같은 사용자 입력 이벤트에 응답합니다.

// Add a "mute" button to the Picture-in-Picture window.
const pipMuteButton = pipWindow.document.createElement("button");
pipMuteButton.textContent = "Mute";
pipMuteButton.addEventListener("click", () => { 
  const pipVideo = pipWindow.document.querySelector("#video");
  pipVideo.muted = true;
});
pipWindow.document.body.append(pipMuteButton);

PIP 모드 창 크기 조정

resizeBy()resizeTo() Window 메서드를 사용하여 PIP 창의 크기를 조절합니다. 두 방법 모두 사용자 동작이 필요합니다.

const resizeButton = pipWindow.document.createElement('button');
resizeButton.textContent = 'Resize';
resizeButton.addEventListener('click', () => {
  // Expand the Picture-in-Picture window's width by 20px and height by 30px.
  pipWindow.resizeBy(20, 30);
});
pipWindow.document.body.append(resizeButton);

opener 창에 포커스 맞추기

focus() Window 메서드를 사용하여 PIP 모드 창에서 열기 창에 포커스를 맞춥니다. 이 메서드에는 사용자 동작이 필요합니다.

const returnToTabButton = pipWindow.document.createElement("button");
returnToTabButton.textContent = "Return to opener tab";
returnToTabButton.addEventListener("click", () => {
  window.focus();
});
pipWindow.document.body.append(returnToTabButton);

CSS PIP 디스플레이 모드

CSS picture-in-picture 디스플레이 모드를 사용하여 웹 앱의 일부가 PIP 모드로 표시될 때만 적용되는 특정 CSS 규칙을 작성합니다.

@media all and (display-mode: picture-in-picture) {
  body {
    margin: 0;
  }
  h1 {
    font-size: 0.8em;
  }
}

특성 감지

Document Picture-in-Picture API가 지원되는지 확인하려면 다음을 사용하세요.

if ('documentPictureInPicture' in window) {
  // The Document Picture-in-Picture API is supported.
}

데모

VideoJS 플레이어

Document Picture-in-Picture API VideoJS 플레이어 데모를 사용해 볼 수 있습니다. 소스 코드를 확인하세요.

Pomodoro

포모도로 웹 앱인 Tomodoro도 가능한 경우 Document Picture-in-Picture API를 활용합니다. GitHub의 pull 요청을 참고하세요.

포모도로 웹 앱인 Tomodoro
Tomodoro의 PIP 모드 창

의견 공유하기

제안사항과 질문이 있으면 GitHub에서 문제 제출