Tính năng Hình trong hình (PiP) giúp người dùng xem video trong một cửa sổ nổi (luôn đặt trên các cửa sổ khác) để họ có thể theo dõi chúng xem trong khi tương tác với các trang web hoặc ứng dụng khác.
Với API web Hình trong hình, bạn có thể bắt đầu và kiểm soát Chế độ Hình trong hình cho các thành phần video trên trang web của bạn. Hãy dùng thử trên Mẫu Hình trong hình chính thức.
Thông tin khái quát
Vào tháng 9 năm 2016, Safari đã thêm tính năng hỗ trợ Hình trong hình thông qua WebKit API trong macOS Sierra. Sáu tháng sau, Chrome đã tự động phát Video có tính năng Hình trong hình trên thiết bị di động khi phát hành Android O sử dụng API Android gốc. Sáu tháng sau, chúng tôi thông báo ý định xây dựng và chuẩn hoá một API Web, tính năng tương thích với Safari, cho phép các trang web để các nhà phát triển tạo và kiểm soát toàn bộ trải nghiệm về tính năng Hình trong hình. Và chúng tôi ở đây!
Tìm hiểu về mã
Vào chế độ Hình trong hình
Hãy bắt đầu đơn giản với một thành phần video và cách để người dùng tương tác chẳng hạn như một phần tử nút.
<video id="videoElement" src="https://example.com/file.mp4"></video>
<button id="pipButtonElement"></button>
Chỉ yêu cầu chế độ Hình trong hình theo cử chỉ của người dùng và không bao giờ được bật
promise (lời hứa) do videoElement.play()
trả về. Điều này là do lời hứa không
chưa phổ biến các cử chỉ của người dùng. Thay vào đó, hãy gọi requestPictureInPicture()
trong một
trình xử lý lượt nhấp trên pipButtonElement
như minh hoạ dưới đây. Bạn có trách nhiệm
để xử lý những gì xảy ra nếu người dùng nhấp hai lần.
pipButtonElement.addEventListener('click', async function () {
pipButtonElement.disabled = true;
await videoElement.requestPictureInPicture();
pipButtonElement.disabled = false;
});
Khi lời hứa được thực hiện, Chrome sẽ thu nhỏ video thành một cửa sổ nhỏ người dùng có thể di chuyển xung quanh và đặt lên trên các cửa sổ khác.
Bạn đã hoàn tất. Tuyệt vời! Bạn có thể ngừng đọc và làm việc xứng đáng đi nghỉ mát. Tiếc là không phải lúc nào cũng như vậy. Lời hứa có thể từ chối cho bất kỳ trong số các lý do sau:
- Hệ thống này không hỗ trợ chế độ Hình trong hình.
- Tài liệu không được phép sử dụng tính năng Hình trong hình do hạn chế chính sách về quyền.
- Siêu dữ liệu video chưa được tải (
videoElement.readyState === 0
). - Tệp video chỉ có âm thanh.
- Thuộc tính
disablePictureInPicture
mới hiện đã có trên phần tử video. - Lệnh gọi không được thực hiện trong trình xử lý sự kiện cử chỉ của người dùng (ví dụ: nhấp vào nút). Kể từ Chrome 74, lựa chọn này chỉ áp dụng nếu không có phần tử trong Đã có chế độ Hình trong hình.
Mục Hỗ trợ tính năng dưới đây cho biết cách bật/tắt một nút dựa trên các hạn chế này.
Hãy thêm một khối try...catch
để phát hiện các lỗi có thể xảy ra này và cho phép
người dùng biết điều gì đang xảy ra.
pipButtonElement.addEventListener('click', async function () {
pipButtonElement.disabled = true;
try {
await videoElement.requestPictureInPicture();
} catch (error) {
// TODO: Show error message to user.
} finally {
pipButtonElement.disabled = false;
}
});
Phần tử video sẽ hoạt động như nhau cho dù ở chế độ Hình trong hình hay not: sự kiện được kích hoạt còn phương thức gọi hoạt động. Chỉ số này phản ánh các thay đổi của trạng thái trong cửa sổ Hình trong hình (chẳng hạn như phát, tạm dừng, tua, v.v.) và cửa sổ này cũng có thể thay đổi trạng thái theo cách có lập trình trong JavaScript.
Thoát chế độ Hình trong hình
Bây giờ, hãy tạo nút chuyển đổi giữa chế độ nhập và thoát chế độ Hình trong hình. T4
trước tiên, bạn phải kiểm tra xem đối tượng chỉ đọc document.pictureInPictureElement
có phải là
là phần tử video của chúng tôi. Nếu không, chúng tôi sẽ gửi yêu cầu nhập
Hình trong hình như ở trên. Nếu không, chúng tôi sẽ yêu cầu rời đi bằng cách gọi điện
document.exitPictureInPicture()
, tức là video sẽ xuất hiện lại sau
thẻ ban đầu. Lưu ý rằng phương thức này cũng trả về một lời hứa (promise).
...
try {
if (videoElement !== document.pictureInPictureElement) {
await videoElement.requestPictureInPicture();
} else {
await document.exitPictureInPicture();
}
}
...
Nghe sự kiện Hình trong hình
Các hệ điều hành thường hạn chế chế độ Hình trong hình ở một cửa sổ, vì vậy Quá trình triển khai của Chrome tuân theo mẫu này. Tức là người dùng chỉ có thể chơi mỗi lần một video Hình trong hình. Bạn sẽ thấy người dùng thoát Chế độ Hình trong hình ngay cả khi bạn không yêu cầu.
Trình xử lý sự kiện enterpictureinpicture
và leavepictureinpicture
mới cho phép
chúng tôi điều chỉnh trải nghiệm cho người dùng. Có thể là bất cứ điều gì như việc duyệt qua
danh mục video để hiển thị một cuộc trò chuyện phát trực tiếp.
videoElement.addEventListener('enterpictureinpicture', function (event) {
// Video entered Picture-in-Picture.
});
videoElement.addEventListener('leavepictureinpicture', function (event) {
// Video left Picture-in-Picture.
// User may have played a Picture-in-Picture video from a different page.
});
Tuỳ chỉnh cửa sổ Hình trong hình
Chrome 74 hỗ trợ các nút phát/tạm dừng, bài hát trước và bài hát tiếp theo trong Cửa sổ Hình trong hình mà bạn có thể kiểm soát bằng API Phiên nội dung nghe nhìn.
Theo mặc định, nút phát/tạm dừng luôn hiển thị ở chế độ Hình trong hình
trừ phi video đang phát các đối tượng MediaStream (ví dụ: getUserMedia()
,
getDisplayMedia()
, canvas.captureStream()
) hoặc video có MediaSource
thời lượng được đặt thành +Infinity
(ví dụ: nguồn cấp dữ liệu trực tiếp). Để đảm bảo nút phát/tạm dừng
luôn hiển thị, hãy thiết lập some
xem trình xử lý tác vụ Phiên nội dung đa phương tiện cho cả nút "Play" (Phát) và
"Pause" (Tạm dừng) sự kiện truyền thông như sau.
// Show a play/pause button in the Picture-in-Picture window
navigator.mediaSession.setActionHandler('play', function () {
// User clicked "Play" button.
});
navigator.mediaSession.setActionHandler('pause', function () {
// User clicked "Pause" button.
});
Đang hiện "Bản nhạc trước" và "Bản nhạc tiếp theo" các thành phần điều khiển cửa sổ cũng tương tự như vậy. Chế độ cài đặt Trình xử lý thao tác của Phiên phát nội dung đa phương tiện cho những người dùng đó sẽ hiển thị ở chế độ Hình trong hình để có thể xử lý các thao tác này.
navigator.mediaSession.setActionHandler('previoustrack', function () {
// User clicked "Previous Track" button.
});
navigator.mediaSession.setActionHandler('nexttrack', function () {
// User clicked "Next Track" button.
});
Để xem ví dụ thực tế, hãy thử mẫu Phiên nội dung nghe nhìn chính thức.
Xem kích thước cửa sổ Hình trong hình
Nếu bạn muốn điều chỉnh chất lượng video khi video truy cập vào và rời đi Chế độ Hình trong hình, bạn cần biết kích thước cửa sổ Hình trong hình và nếu người dùng đổi kích thước cửa sổ theo cách thủ công.
Ví dụ bên dưới cho thấy cách lấy chiều rộng và chiều cao của Cửa sổ Hình trong hình khi hình ảnh được tạo hoặc đổi kích thước.
let pipWindow;
videoElement.addEventListener('enterpictureinpicture', function (event) {
pipWindow = event.pictureInPictureWindow;
console.log(`> Window size is ${pipWindow.width}x${pipWindow.height}`);
pipWindow.addEventListener('resize', onPipWindowResize);
});
videoElement.addEventListener('leavepictureinpicture', function (event) {
pipWindow.removeEventListener('resize', onPipWindowResize);
});
function onPipWindowResize(event) {
console.log(
`> Window size changed to ${pipWindow.width}x${pipWindow.height}`
);
// TODO: Change video quality based on Picture-in-Picture window size.
}
Bạn không nên kết nối trực tiếp với sự kiện đổi kích thước khi mỗi thay đổi nhỏ được thực hiện kích thước cửa sổ Hình trong hình sẽ kích hoạt một sự kiện riêng có thể khiến hiệu suất nếu bạn đang thực hiện một thao tác tốn kém ở mỗi lần đổi kích thước. Trong nói cách khác, thao tác đổi kích thước sẽ kích hoạt các sự kiện lặp đi lặp lại để một cách nhanh chóng. Tôi khuyên bạn nên sử dụng các kỹ thuật phổ biến như điều tiết và gỡ bỏ để giải quyết vấn đề này.
Hỗ trợ tính năng
API web hình trong hình có thể không được hỗ trợ nên bạn phải phát hiện điều này
để cung cấp cải tiến tăng dần. Ngay cả khi được hỗ trợ, tính năng này có thể
bị người dùng tắt hoặc bị tắt theo chính sách về quyền. Thật may là bạn có thể sử dụng
giá trị boolean mới document.pictureInPictureEnabled
để xác định điều này.
if (!('pictureInPictureEnabled' in document)) {
console.log('The Picture-in-Picture Web API is not available.');
} else if (!document.pictureInPictureEnabled) {
console.log('The Picture-in-Picture Web API is disabled.');
}
Đây là cách mà bạn nên áp dụng cho một thành phần nút cụ thể trong video xử lý chế độ hiển thị của nút Hình trong hình.
if ('pictureInPictureEnabled' in document) {
// Set button ability depending on whether Picture-in-Picture can be used.
setPipButton();
videoElement.addEventListener('loadedmetadata', setPipButton);
videoElement.addEventListener('emptied', setPipButton);
} else {
// Hide button if Picture-in-Picture is not supported.
pipButtonElement.hidden = true;
}
function setPipButton() {
pipButtonElement.disabled =
videoElement.readyState === 0 ||
!document.pictureInPictureEnabled ||
videoElement.disablePictureInPicture;
}
Hỗ trợ video MediaStream
Phát video các đối tượng MediaStream (ví dụ: getUserMedia()
, getDisplayMedia()
,
canvas.captureStream()
) cũng hỗ trợ tính năng Hình trong hình trong Chrome 71. Chiến dịch này
nghĩa là bạn có thể hiển thị cửa sổ Hình trong hình có chứa webcam của người dùng
luồng video, hiển thị luồng video hoặc thậm chí là phần tử canvas. Lưu ý rằng
phần tử video không cần phải được đính kèm vào DOM để nhập
Hình trong hình như minh hoạ dưới đây.
Hiển thị webcam của người dùng trong cửa sổ Hình trong hình
const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getUserMedia({video: true});
video.play();
// Later on, video.requestPictureInPicture();
Hiện màn hình trong cửa sổ Hình trong hình
const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getDisplayMedia({video: true});
video.play();
// Later on, video.requestPictureInPicture();
Hiển thị thành phần canvas trong cửa sổ Hình trong hình
const canvas = document.createElement('canvas');
// Draw something to canvas.
canvas.getContext('2d').fillRect(0, 0, canvas.width, canvas.height);
const video = document.createElement('video');
video.muted = true;
video.srcObject = canvas.captureStream();
video.play();
// Later on, video.requestPictureInPicture();
Khi kết hợp canvas.captureStream()
với Media Session API, bạn có thể
Bạn có thể tạo cửa sổ danh sách phát âm thanh trong Chrome 74. Xem kênh chính thức
Mẫu danh sách phát âm thanh.
Mẫu, bản minh hoạ và lớp học lập trình
Hãy xem mẫu Hình trong hình chính thức của chúng tôi để dùng thử tính năng Hình trong hình Web API.
Sau đó là các bản minh hoạ và lớp học lập trình.
Bước tiếp theo
Trước tiên, hãy xem trang trạng thái triển khai để biết phần nào của API hiện được triển khai trong Chrome và các trình duyệt khác.
Dưới đây là những nội dung bạn có thể thấy trong tương lai gần:
- Các nhà phát triển web có thể thêm các chế độ điều khiển tuỳ chỉnh Hình trong hình.
- Chúng tôi sẽ cung cấp API Web mới để hiển thị các đối tượng
HTMLElement
tuỳ ý trong cửa sổ nổi.
Hỗ trợ trình duyệt
API web hình trong hình được hỗ trợ trong Chrome, Edge, Opera và Safari. Xem MDN để biết chi tiết.
Tài nguyên
- Trạng thái tính năng của Chrome: https://www.chromestatus.com/feature/5729206566649856
- Lỗi khi triển khai Chrome: https://crbug.com/?q=component:Blink>Media>PictureInPicture (Hình ảnh trong hình)
- Quy cách của API Web hình trong hình: https://wicg.github.io/picture-in-picture
- Vấn đề về quy cách: https://github.com/WICG/picture-in-picture/issues
- Mẫu: https://googlechrome.github.io/samples/picture-in-picture/
- Polyfill hình trong hình không chính thức: https://github.com/gbentaieb/pip-polyfill/
Rất cảm ơn Mounir Lamouri và Jennifer Apacible vì những đóng góp của họ Chế độ Hình trong hình và trợ giúp trong bài viết này. Xin chân thành cảm ơn mọi người liên quan đến nỗ lực chuẩn hoá.