演示 API
演示 API 允许用户代理(例如 Web 浏览器)通过大型演示设备(例如投影仪和网络连接的电视)有效地显示 Web 内容。支持的媒体设备类型包括有线连接(使用 HDMI、DVI 等)或无线连接的显示器,例如DLNA、Chromecast、AirPlay或Miracast。
通常,网页使用演示控制器 API 指定要在演示设备上呈现的 Web 内容并启动演示会话。使用演示接收器 API,演示 Web 内容可以获取会话状态。通过提供控制器页面和接收器页面,并使用消息传递通道,Web 开发人员可以实现这两个页面之间的交互。
根据演示设备提供的连接机制,任何控制器页面和接收器页面都可以由同一个用户代理呈现,也可以由不同的用户代理呈现。
- 对于 1-UA 模式设备,这两个页面都由同一个用户代理加载。但是,接收器页面的呈现结果将通过支持的远程呈现协议发送到演示设备。
- 对于 2-UA 模式设备,接收器页面直接在演示设备上加载。控制用户代理通过支持的演示控制协议与演示设备通信,以控制演示会话并将两个页面之间的消息传输到演示设备。
接口
演示
-
在控制浏览上下文中,
Presentation
接口提供了一种机制来覆盖浏览器启动演示到外部屏幕的默认行为。在接收浏览上下文中,Presentation
接口提供对可用演示连接的访问。 PresentationRequest
-
启动或重新连接由控制浏览上下文创建的演示。
-
一个PresentationAvailability 对象与可用的演示显示器相关联,并表示演示请求的演示显示器可用性。
-
当与对象关联的连接创建时,
PresentationConnectionAvailableEvent
在PresentationRequest
上触发。 PresentationConnection
-
每个演示连接都由一个PresentationConnection 对象表示。
PresentationConnectionCloseEvent
-
当演示连接进入
closed
状态时,会触发PresentationConnectionCloseEvent
。 PresentationReceiver
-
PresentationReceiver
允许接收浏览上下文访问控制浏览上下文并与它们通信。 PresentationConnectionList
-
PresentationConnectionList
表示非终止演示连接的集合。它也是新可用演示连接事件的监视器。
示例
以下示例代码重点介绍了演示 API 主要功能的使用方式:controller.html
实现控制器,presentation.html
实现演示。这两个页面都从域名https://example.org
(https://example.org/controller.html
和 https://example.org/presentation.html
)提供服务。这些示例假设控制页面一次管理一个演示。有关更多详细信息,请参阅代码示例中的注释。
监视演示显示器的可用性
在controller.html
中
<button id="presentBtn" style="display: none;">Present</button>
<script>
// 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) => {
presentBtn.style.display = available ? "inline" : "none";
};
// 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);
});
</script>
启动新的演示
在controller.html
中
<script>
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.
};
</script>
重新连接到演示
在controller.html
文件中
<button id="reconnectBtn" style="display: none;">Reconnect</button>
<script>
const reconnect = () => {
// read presId from localStorage if exists
const presId = localStorage["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.
document.addEventListener("DOMContentLoaded", reconnect);
// Or allow manual reconnection.
reconnectBtn.onclick = reconnect;
</script>
由控制 UA 启动演示
在controller.html
文件中
<script>
navigator.presentation.defaultRequest = new PresentationRequest(presUrls);
navigator.presentation.defaultRequest.onconnectionavailable = (evt) => {
setConnection(evt.connection);
};
</script>
设置presentation.defaultRequest
允许页面指定控制 UA 启动演示时要使用的PresentationRequest
。
监视连接状态并交换数据
在controller.html
中
<button id="disconnectBtn" style="display: none;">Disconnect</button>
<button id="stopBtn" style="display: none;">Stop</button>
<button id="reconnectBtn" style="display: none;">Reconnect</button>
<script>
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["presId"] = connection.id;
function showConnectedUI() {
// Allow the user to disconnect from or terminate the presentation
stopBtn.style.display = "inline";
disconnectBtn.style.display = "inline";
reconnectBtn.style.display = "none";
}
function showDisconnectedUI() {
disconnectBtn.style.display = "none";
stopBtn.style.display = "none";
reconnectBtn.style.display = localStorage["presId"] ? "inline" : "none";
}
// 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 = () => {
// Remove presId from localStorage if exists
delete localStorage["presId"];
connection = null;
showDisconnectedUI();
};
}
</script>
监视可用连接并问好
在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
文件中
<script>
connection.send('{"string": "你好,世界!", "lang": "zh-CN"}');
connection.send('{"string": "こんにちは、世界!", "lang": "ja"}');
connection.send('{"string": "안녕하세요, 세계!", "lang": "ko"}');
connection.send('{"string": "Hello, world!", "lang": "en-US"}');
</script>
在presentation.html
文件中
<script>
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);
};
</script>
规范
规范 |
---|
演示 API # interface-presentation |
浏览器兼容性
BCD 表仅在浏览器中加载
另请参阅
演示 API polyfill 包含演示 API 规范的 JavaScript polyfill,该规范正在 W3C 的第二屏幕工作组 中进行标准化。polyfill 主要用于探索如何在不同的演示机制之上实现演示 API。