Presentation API
Presentation API 允许 用户代理(例如 Web 浏览器)有效地通过大型显示设备(如投影仪和联网电视)显示 Web 内容。支持的多媒体设备类型包括通过 HDMI、DVI 或类似接口有线连接的显示器,以及通过 DLNA、Chromecast、AirPlay 或 Miracast 无线连接的显示器。

通常,网页使用 Presentation Controller API 来指定要在演示设备上呈现的 Web 内容并启动演示会话。通过 Presentation Receiver API,正在演示的 Web 内容可以获取会话状态。通过为控制器页面和接收器页面提供基于消息的通道,Web 开发人员可以实现这两个页面之间的交互。
根据演示设备提供的连接机制,任何控制器页面和接收器页面都可以由同一个用户代理呈现,也可以由不同的用户代理呈现。
- 对于 1-UA 模式设备,两个页面都由同一个用户代理加载。但是,接收器页面的渲染结果将通过支持的远程渲染协议发送到演示设备。
- 对于 2-UAs 模式设备,接收器页面直接在演示设备上加载。控制用户代理通过支持的演示控制协议与演示设备通信,以控制演示会话并在两个页面之间传输消息。
接口
Presentation-
在控制浏览上下文时,
Presentation接口提供了一种机制来覆盖浏览器将演示内容启动到外部屏幕的默认行为。在接收浏览上下文时,Presentation接口提供对可用演示连接的访问。 PresentationRequest-
启动或重新连接到由控制浏览上下文发起的演示。
-
一个
PresentationAvailability对象与可用的演示显示相关联,并代表演示请求的演示显示可用性。 -
当与
PresentationRequest对象关联的连接创建时,会在该对象上触发PresentationConnectionAvailableEvent。 PresentationConnection-
每个演示连接都由一个
PresentationConnection对象表示。 PresentationConnectionCloseEvent-
当演示连接进入
closed状态时,会触发PresentationConnectionCloseEvent。 PresentationReceiver-
PresentationReceiver允许接收浏览上下文访问控制浏览上下文并与它们进行通信。 PresentationConnectionList-
PresentationConnectionList表示未终止的演示连接的集合。它也是新可用演示连接事件的监视器。
示例
下面的示例代码重点介绍了 Presentation API 的主要功能:controller.html 实现控制器,presentation.html 实现演示。两个页面都从域名 https://example.org 提供服务(https://example.org/controller.html 和 https://example.org/presentation.html)。这些示例假定控制页面一次只管理一个演示。有关更多详细信息,请参阅代码示例中的注释。
监视演示显示的可用性
在 controller.html 中
<button id="presentBtn" class="hidden">Present</button>
.hidden {
display: none;
}
// The Present button is visible if at least one presentation display is available
const presentBtn = document.getElementById("presentBtn");
// It is also possible to use relative presentation URL e.g. "presentation.html"
const presUrls = [
"https://example.com/presentation.html",
"https://example.net/alternate.html",
];
// Show or hide present button depending on display availability
const handleAvailabilityChange = (available) => {
if (available) {
presentBtn.classList.remove("hidden");
} else {
presentBtn.classList.add("hidden");
}
};
// Promise is resolved as soon as the presentation display availability is known.
const request = new PresentationRequest(presUrls);
request
.getAvailability()
.then((availability) => {
// availability.value may be kept up-to-date by the controlling UA as long
// as the availability object is alive. It is advised for the web developers
// to discard the object as soon as it's not needed.
handleAvailabilityChange(availability.value);
availability.onchange = () => {
handleAvailabilityChange(availability.value);
};
})
.catch(() => {
// Availability monitoring is not supported by the platform, so discovery of
// presentation displays will happen only after request.start() is called.
// Pretend the devices are available for simplicity; or, one could implement
// a third state for the button.
handleAvailabilityChange(true);
});
开始新的演示
在 controller.html 中
presentBtn.onclick = () => {
// Start new presentation.
request
.start()
// The connection to the presentation will be passed to setConnection on success.
.then(setConnection);
// Otherwise, the user canceled the selection dialog or no screens were found.
};
重新连接到演示
在 controller.html 文件中
<button id="reconnectBtn" class="hidden">Reconnect</button>
const reconnect = () => {
const presId = localStorage.getItem("presId");
// presId is mandatory when reconnecting to a presentation.
if (presId) {
request
.reconnect(presId)
// The new connection to the presentation will be passed to
// setConnection on success.
.then(setConnection);
// No connection found for presUrl and presId, or an error occurred.
}
};
// On navigation of the controller, reconnect automatically.
reconnect();
// Or allow manual reconnection.
reconnectBtn.onclick = reconnect;
由控制用户代理发起演示
在 controller.html 文件中
navigator.presentation.defaultRequest = new PresentationRequest(presUrls);
navigator.presentation.defaultRequest.onconnectionavailable = (evt) => {
setConnection(evt.connection);
};
设置 presentation.defaultRequest 允许页面在控制用户代理启动演示时指定要使用的 PresentationRequest。
监视连接状态并交换数据
在 controller.html 中
<button id="disconnectBtn" class="hidden">Disconnect</button>
<button id="stopBtn" class="hidden">Stop</button>
<button id="reconnectBtn" class="hidden">Reconnect</button>
let connection;
// The Disconnect and Stop buttons are visible if there is a connected presentation
const stopBtn = document.querySelector("#stopBtn");
const reconnectBtn = document.querySelector("#reconnectBtn");
const disconnectBtn = document.querySelector("#disconnectBtn");
stopBtn.onclick = () => {
connection?.terminate();
};
disconnectBtn.onclick = () => {
connection?.close();
};
function setConnection(newConnection) {
// Disconnect from existing presentation, if not attempting to reconnect
if (
connection &&
connection !== newConnection &&
connection.state !== "closed"
) {
connection.onclose = undefined;
connection.close();
}
// Set the new connection and save the presentation ID
connection = newConnection;
localStorage.setItem("presId", connection.id);
function showConnectedUI() {
// Allow the user to disconnect from or terminate the presentation
stopBtn.classList.remove("hidden");
disconnectBtn.classList.remove("hidden");
reconnectBtn.classList.add("hidden");
}
function showDisconnectedUI() {
disconnectBtn.classList.add("hidden");
stopBtn.classList.add("hidden");
if (localStorage.getItem("presId")) {
// If there is a presId in localStorage, allow the user to reconnect
reconnectBtn.classList.remove("hidden");
} else {
reconnectBtn.classList.add("hidden");
}
}
// Monitor the connection state
connection.onconnect = () => {
showConnectedUI();
// Register message handler
connection.onmessage = (message) => {
console.log(`Received message: ${message.data}`);
};
// Send initial message to presentation page
connection.send("Say hello");
};
connection.onclose = () => {
connection = null;
showDisconnectedUI();
};
connection.onterminate = () => {
localStorage.removeItem("presId");
connection = null;
showDisconnectedUI();
};
}
监视可用的连接并发送问候
在 presentation.html 中
const addConnection = (connection) => {
connection.onmessage = (message) => {
if (message.data === "Say hello") connection.send("hello");
};
};
navigator.presentation.receiver.connectionList.then((list) => {
list.connections.forEach((connection) => {
addConnection(connection);
});
list.onconnectionavailable = (evt) => {
addConnection(evt.connection);
};
});
通过消息传递区域信息
在 controller.html 文件中
connection.send('{"string": "你好,世界!", "lang": "zh-CN"}');
connection.send('{"string": "こんにちは、世界!", "lang": "ja"}');
connection.send('{"string": "안녕하세요, 세계!", "lang": "ko"}');
connection.send('{"string": "Hello, world!", "lang": "en-US"}');
在 presentation.html 文件中
connection.onmessage = (message) => {
const messageObj = JSON.parse(message.data);
const spanElt = document.createElement("SPAN");
spanElt.lang = messageObj.lang;
spanElt.textContent = messageObj.string;
document.body.appendChild(spanElt);
};
规范
| 规范 |
|---|
| Presentation API # interface-presentation |
浏览器兼容性
加载中…
另见
Presentation API polyfill 包含一个 JavaScript polyfill,实现了 W3C Second Screen Working Group 正在标准化的 Presentation API 规范。该 polyfill 主要用于探索 Presentation API 如何基于不同的演示机制进行实现。