演示 API

可用性有限

此功能不是基线,因为它不适用于一些最常用的浏览器。

安全上下文:此功能仅在安全上下文(HTTPS)中可用,部分或全部支持的浏览器

实验性:这是一个实验性技术
在生产环境中使用此功能之前,请仔细查看浏览器兼容性表

演示 API 允许用户代理(例如 Web 浏览器)通过大型演示设备(例如投影仪和网络连接的电视)有效地显示 Web 内容。支持的媒体设备类型包括有线连接(使用 HDMI、DVI 等)或无线连接的显示器,例如DLNAChromecastAirPlayMiracast

1-UA mode loaded the Controlling and Presenting pages together before outputting to displays. 2-UA mode loaded them separately using the Presentation Control Protocol.

通常,网页使用演示控制器 API 指定要在演示设备上呈现的 Web 内容并启动演示会话。使用演示接收器 API,演示 Web 内容可以获取会话状态。通过提供控制器页面和接收器页面,并使用消息传递通道,Web 开发人员可以实现这两个页面之间的交互。

根据演示设备提供的连接机制,任何控制器页面和接收器页面都可以由同一个用户代理呈现,也可以由不同的用户代理呈现。

  • 对于 1-UA 模式设备,这两个页面都由同一个用户代理加载。但是,接收器页面的呈现结果将通过支持的远程呈现协议发送到演示设备。
  • 对于 2-UA 模式设备,接收器页面直接在演示设备上加载。控制用户代理通过支持的演示控制协议与演示设备通信,以控制演示会话并将两个页面之间的消息传输到演示设备。

接口

演示

在控制浏览上下文中,Presentation 接口提供了一种机制来覆盖浏览器启动演示到外部屏幕的默认行为。在接收浏览上下文中,Presentation 接口提供对可用演示连接的访问。

PresentationRequest

启动或重新连接由控制浏览上下文创建的演示。

PresentationAvailability

一个PresentationAvailability 对象与可用的演示显示器相关联,并表示演示请求的演示显示器可用性

PresentationConnectionAvailableEvent

当与对象关联的连接创建时,PresentationConnectionAvailableEventPresentationRequest 上触发。

PresentationConnection

每个演示连接都由一个PresentationConnection 对象表示。

PresentationConnectionCloseEvent

当演示连接进入closed 状态时,会触发PresentationConnectionCloseEvent

PresentationReceiver

PresentationReceiver 允许接收浏览上下文访问控制浏览上下文并与它们通信。

PresentationConnectionList

PresentationConnectionList 表示非终止演示连接的集合。它也是新可用演示连接事件的监视器。

示例

以下示例代码重点介绍了演示 API 主要功能的使用方式:controller.html 实现控制器,presentation.html 实现演示。这两个页面都从域名https://example.orghttps://example.org/controller.htmlhttps://example.org/presentation.html)提供服务。这些示例假设控制页面一次管理一个演示。有关更多详细信息,请参阅代码示例中的注释。

监视演示显示器的可用性

controller.html

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

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 文件中

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 文件中

html
<script>
  navigator.presentation.defaultRequest = new PresentationRequest(presUrls);
  navigator.presentation.defaultRequest.onconnectionavailable = (evt) => {
    setConnection(evt.connection);
  };
</script>

设置presentation.defaultRequest 允许页面指定控制 UA 启动演示时要使用的PresentationRequest

监视连接状态并交换数据

controller.html

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

js
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 文件中

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 文件中

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。