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

François Beaufort
François Beaufort

Browser Support

  • Chrome: 116.
  • Edge: 116.
  • Firefox: not supported.
  • Safari: not supported.

Source

문서 PIP 모드 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. 실행 완료 (데스크톱)

사용 사례

맞춤 동영상 플레이어, 화상 회의, 생산성 앱 등 다양한 방식으로 이 API를 사용할 수 있습니다.

맞춤 동영상 플레이어

웹사이트는 기존 <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
true인 경우 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 창이 닫힐 때 처리

PIP 창이 닫히는 시점을 알려면 (웹사이트에서 시작했거나 사용자가 수동으로 닫았기 때문에) 창 "pagehide" 이벤트를 수신 대기하세요. 이벤트 핸들러는 여기에 표시된 것처럼 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() 창 메서드를 사용하여 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);

오프너 창에 포커스

focus() 창 메서드를 사용하여 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.
}

데모

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

의견 공유

GitHub에 문제 제출을 통해 제안사항과 질문을 보내주세요.