O Chrome 66 permite que as páginas da Web usem uma tela secundária anexada com a API Presentation e controlem o conteúdo dela com a API Presentation Receiver.
Contexto
Até agora, os desenvolvedores Web podiam criar experiências em que um usuário veria conteúdo local no Chrome diferente do que veria em uma tela remota, mas ainda controlando essa experiência localmente. Por exemplo, gerenciar uma fila de reprodução no youtube.com enquanto os vídeos são reproduzidos na TV ou assistir a um vídeo com as anotações do apresentador em um laptop enquanto a apresentação em tela cheia é exibida em uma sessão de Hangout.
No entanto, há cenários em que os usuários podem simplesmente querer apresentar conteúdo em uma segunda tela conectada. Por exemplo, imagine um usuário em uma sala de conferências com um projetor conectado por um cabo HDMI. Em vez de espelhar a apresentação em um endpoint remoto, o usuário quer mostrar os slides em tela cheia no projetor, deixando a tela do laptop disponível para as anotações do apresentador e o controle de slides. Embora o autor do site possa oferecer suporte a isso de uma maneira muito rudimentar (por exemplo, abrindo uma nova janela, que o usuário precisa arrastar manualmente para a tela secundária e maximizar para tela cheia), isso é complicado e fornece uma experiência consistente entre apresentações locais e remotas.
Apresentar uma página
Vejamos como usar a API Presentation para apresentar uma página da Web na tela secundária anexada. O resultado final está disponível em https://googlechrome.github.io/samples/presentation-api/.
Primeiro, vamos criar um novo objeto PresentationRequest
que vai conter o
URL que queremos apresentar na tela secundária anexada.
const presentationRequest = new PresentationRequest('receiver.html');
In this article, I won’t cover use cases where the parameter passed to
`PresentationRequest` can be an array like `['cast://foo’, 'apple://foo',
'https://example.com']` as this is not relevant there.
We can now monitor presentation display availability and toggle a "Present"
button visibility based on presentation displays availability. Note that we can
also decide to always show this button.
<aside class="caution"><b>Caution:</b> The browser may use more energy while the <code>availability</code> object is alive
and actively listening for presentation display availability changes. Please
use it with caution in order to save energy on mobile.</aside>
```js
presentationRequest.getAvailability()
.then(availability => {
console.log('Available presentation displays: ' + availability.value);
availability.addEventListener('change', function() {
console.log('> Available presentation displays: ' + availability.value);
});
})
.catch(error => {
console.log('Presentation availability not supported, ' + error.name + ': ' +
error.message);
});
A exibição de um prompt de exibição de apresentação exige um gesto do usuário, como clicar
em um botão. Então, vamos chamar presentationRequest.start()
com o clique de um botão e
aguardar a resolução da promessa depois que o usuário selecionar uma tela de
apresentação (por exemplo, uma tela secundária anexada no nosso caso de uso).
function onPresentButtonClick() {
presentationRequest.start()
.then(connection => {
console.log('Connected to ' + connection.url + ', id: ' + connection.id);
})
.catch(error => {
console.log(error);
});
}
A lista apresentada ao usuário também poderá incluir endpoints remotos, como dispositivos Chromecast, se você estiver conectado a uma rede que os anuncia. As telas espelhadas não estão na lista. Consulte http://crbug.com/840466 (link em inglês).
Quando a promessa é resolvida, a página da Web no URL do objeto PresentationRequest
é
apresentada à exibição escolhida. Et voilà!
Agora podemos ir mais longe e monitorar os eventos "close" e "terminate", conforme mostrado abaixo. É possível se reconectar a um presentationConnection
"fechado" com presentationRequest.reconnect(presentationId)
,
em que presentationId
é o ID do objeto presentationRequest
anterior.
function onCloseButtonClick() {
// Disconnect presentation connection but will allow reconnection.
presentationConnection.close();
}
presentationConnection.addEventListener('close', function() {
console.log('Connection closed.');
});
function onTerminateButtonClick() {
// Stop presentation connection for good.
presentationConnection.terminate();
}
presentationConnection.addEventListener('terminate', function() {
console.log('Connection terminated.');
});
Comunicar-se com a página
Agora você está pensando: isso é bom, mas como transmitir mensagens entre minha
página do controlador (a que acabamos de criar) e a página do receptor (aquela
que transmitimos para o objeto PresentationRequest
)?
Primeiro, recupere as conexões existentes na página do receptor com
navigator.presentation.receiver.connectionList
e detecte as conexões
de entrada, conforme mostrado abaixo.
// Receiver page
navigator.presentation.receiver.connectionList
.then(list => {
list.connections.map(connection => addConnection(connection));
list.addEventListener('connectionavailable', function(event) {
addConnection(event.connection);
});
});
function addConnection(connection) {
connection.addEventListener('message', function(event) {
console.log('Message: ' + event.data);
connection.send('Hey controller! I just received a message.');
});
connection.addEventListener('close', function(event) {
console.log('Connection closed!', event.reason);
});
}
Uma conexão que recebe uma mensagem dispara um evento "message" que você pode detectar.
A mensagem pode ser uma string, um Blob, um ArrayBuffer ou um ArrayBufferView.
O envio é tão simples quanto chamar connection.send(message)
pela
página do controlador ou da página do receptor.
// Controller page
function onSendMessageButtonClick() {
presentationConnection.send('Hello!');
}
presentationConnection.addEventListener('message', function(event) {
console.log('I just received ' + event.data + ' from the receiver.');
});
Teste o exemplo em https://googlechrome.github.io/samples/presentation-api/ para ter uma noção de como ele funciona. Tenho certeza de que você vai gostar disso tanto quanto eu.
Exemplos e demonstrações
Confira o exemplo oficial do Chrome que usamos para este artigo.
Também recomendo conferir a demonstração interativa do Photowall. Esse app da Web permite que vários controladores apresentem de forma colaborativa uma apresentação de slides com fotos em uma tela de apresentação. O código está disponível em https://github.com/GoogleChromeLabs/presentation-api-samples.
Mais uma coisa
O Chrome tem um menu de navegação "Transmitir" que os usuários podem invocar a qualquer momento ao visitar um
site. Se você quiser controlar a apresentação padrão desse menu, atribua navigator.presentation.defaultRequest
a um objeto presentationRequest
personalizado criado anteriormente.
// Make this presentation the default one when using the "Cast" browser menu.
navigator.presentation.defaultRequest = presentationRequest;
Dicas para desenvolvedores
Para inspecionar e depurar a página do destinatário, acesse a página
chrome://inspect
interna, selecione "Outros" e clique no link "inspecionar" ao lado
do URL apresentado no momento.
Confira também a página interna chrome://media-router-internals
para se aprofundar nos processos internos de descoberta/disponibilidade.
A seguir
A partir do Chrome 66, as plataformas ChromeOS, Linux e Windows serão compatíveis. O suporte para Mac virá mais tarde.
Recursos
- Status do recurso do Chrome: https://www.chromestatus.com/features#presentation%20api
- Bugs de implementação: https://crbug.com/?q=component:Blink>PresentationAPI
- Especificação da API Presentation: https://w3c.github.io/presentation-api/
- Problemas com especificações: https://github.com/w3c/presentation-api/issues (em inglês)