문서 PIP 모드 API를 사용하면 임의의 HTML 콘텐츠로 채울 수 있는 항상 최상위 창을 열 수 있습니다. 이는 HTML <video> 요소만 PIP 모드 창에 배치할 수 있는 기존 <video>용 PIP 모드 API를 확장합니다.
Document Picture-in-Picture API의 PIP 모드 창은 window.open()를 사용하여 열린 빈 동일 출처 창과 비슷하지만 몇 가지 차이점이 있습니다.
- PIP 모드 창이 다른 창 위에 떠 있습니다.
- PIP 모드 창은 여는 창보다 오래 지속되지 않습니다.
- PIP 모드 창을 탐색할 수 없습니다.
- PIP 모드 창 위치는 웹사이트에서 설정할 수 없습니다.
상태
| 단계 | 상태 |
|---|---|
| 1. 설명 만들기 | 완전함 |
| 2. 사양의 초기 초안 만들기 | In progress |
| 3. 의견 수집 및 디자인 반복 | In progress |
| 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()의 width 및 height 옵션을 이상적인 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() 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 플레이어 데모를 사용해 보세요.
- Tomodoro는 포모도로 웹 앱으로, 사용 가능한 경우 문서 PIP API를 활용합니다. GitHub pull 요청을 참고하세요.
의견 공유
제안사항과 질문이 있으면 GitHub에 문제를 제출하세요.