W3C在2020年12月份出台了Presentation API规范,这个api接口允许用户自由控制投放在其他屏幕上,并和投放窗口通信,查看W3C规范。演示地址:投屏演示,详细demo(Vue3/HTML)见文章末尾
1、创建一个PresentationRequest对象,该对象将包含我们要在辅助附加显示器上显示的地址。const presentationRequest = new PresentationRequest('receiver.html');
2、做控制及监听事件
/*
* @Author: lzx
* @Date: 2022-07-08 15:34:16
* @LastEditors: lzx
* @LastEditTime: 2022-07-08 17:14:31
* @Description: Fuck Bug
* @FilePath: \test\js\index.js
*/
// 创建个投屏对象
const presentationRequest = new PresentationRequest(['https://blog.fxnws.com']); // 投屏网址
// const presentationRequest = new PresentationRequest(['test.html']);//投屏页面
// Make this presentation the default one when using the "Cast" browser menu.
navigator.presentation.defaultRequest = presentationRequest;
let presentationConnection;
//监视连接是否可用
presentationRequest.addEventListener('connectionavailable', function (event) {
presentationConnection = event.connection;
presentationConnection.addEventListener('close', function () {
console.log('> Connection closed.');
});
presentationConnection.addEventListener('terminate', function () {
console.log('> Connection terminated.');
});
presentationConnection.addEventListener('message', function (event) {
console.log('> ' + event.data);
});
});
//监视可用的显示器
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);
});
// 开启投屏
function startProjectionScreen () {
presentationRequest.start()
.then(connection => {
console.log('Connected to ' + connection.url + ', id: ' + connection.id);
})
.catch(error => {
console.log(error);
});
}
//发送消息
function sendMsg () {
const message = '发送消息测试';
const lang = document.body.lang || 'en-US';
console.log('Sending "' + message + '"...');
presentationConnection.send(JSON.stringify({ message, lang }));
}
//关闭连接,关闭连接后无法再控制弹出窗口
function closeLink () {
console.log('Closing connection...');
presentationConnection.close();
}
//关闭连接后,换可以重新再次连接,这里需要指定id
// presentationRequest.reconnect(presentationId);
//关闭弹窗,结束连接调用
function endLink () {
console.log('Terminating connection...');
presentationConnection.terminate();
}
3、连接端页面消息的展示(搬运的官方)
/*
* @Author: lzx
* @Date: 2022-07-08 15:34:16
* @LastEditors: lzx
* @LastEditTime: 2022-07-08 15:34:30
* @Description: Fuck Bug
* @FilePath: \test\js\test.js
*/
let connectionIdx = 0;
let messageIdx = 0;
//如果有连接建立,则调用该函数
function addConnection (connection) {
connection.connectionId = ++connectionIdx;
addMessage('New connection #' + connectionIdx);
connection.addEventListener('message', function (event) {
messageIdx++;
const data = JSON.parse(event.data);
const logString = 'Message ' + messageIdx + ' from connection #' +
connection.connectionId + ': ' + data.message;
addMessage(logString, data.lang);
maybeSetFruit(data.message);
connection.send('Received message ' + messageIdx);
});
connection.addEventListener('close', function (event) {
addMessage('Connection #' + connection.connectionId + ' closed, reason = ' +
event.reason + ', message = ' + event.message);
});
};
const fruitEmoji = {
'grapes': '\u{1F347}',
'watermelon': '\u{1F349}',
'melon': '\u{1F348}',
'tangerine': '\u{1F34A}',
'lemon': '\u{1F34B}',
'banana': '\u{1F34C}',
'pineapple': '\u{1F34D}',
'green apple': '\u{1F35F}',
'apple': '\u{1F34E}',
'pear': '\u{1F350}',
'peach': '\u{1F351}',
'cherries': '\u{1F352}',
'strawberry': '\u{1F353}'
};
function addMessage (content, language) {
const listItem = document.createElement("li");
if (language) {
listItem.lang = language;
}
listItem.textContent = content;
document.querySelector("#message-list").appendChild(listItem);
};
function maybeSetFruit (message) {
const fruit = message.toLowerCase();
if (fruit in fruitEmoji) {
document.querySelector('#main').textContent = fruitEmoji[fruit];
}
};
//文档载入后,监听连接
document.addEventListener('DOMContentLoaded', function () {
if (navigator.presentation.receiver) {
navigator.presentation.receiver.connectionList.then(list => {
list.connections.map(connection => addConnection(connection));
list.addEventListener('connectionavailable', function (event) {
addConnection(event.connection);
});
});
}
});
4、接收端页面构
<!--
* @Author: lzx
* @Date: 2022-07-08 15:34:05
* @LastEditors: lzx
* @LastEditTime: 2022-07-08 15:35:13
* @Description: Fuck Bug
* @FilePath: \test\test.html
-->
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Demo Receiver</title>
<style type="text/css">
html,
body {
height: 100%;
margin: 0;
font-family: sans-serif;
background: radial-gradient(ellipse at center, #333333 0%, #000000 100%);
color: #fff;
}
#main {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
font-size: 54px;
}
</style>
<script type="text/javascript" src="引入3的代码块"></script>
</head>
<body>
<div id="main">Hello World!</div>
<ul id="message-list">
</ul>
</body>
</html>
5、vue3页面
<template>
<div class="container">
<button @click="startProjectionScreen">开启投屏</button>
<button @click="sendMsg">发送消息</button>
<!-- <button @click="closeLink">关闭连接</button> -->
<button @click="endLink">结束调用</button>
</div>
</template>
<script lang="ts">
// @ts-nocheck 不在当前文件中执行类型检查
import { toRefs, reactive, onMounted, watchEffect, onUnmounted } from 'vue'
export default {
props: {},
components: {},
setup () {
const presentationRequest = new PresentationRequest(['https://blog.fxnws.com']); // 投屏网址
// const presentationRequest = new PresentationRequest(['test.html']); // 投屏本地页面
navigator.presentation.defaultRequest = presentationRequest;
let presentationConnection;
// 传参
const state = reactive({
})
const startProjectionScreen = () => {
presentationRequest.start()
.then(connection => {
console.log('Connected to ' + connection.url + ', id: ' + connection.id);
})
.catch(error => {
console.log(error);
});
}
//发送消息
const sendMsg = () => {
const message = '发送消息测试';
const lang = document.body.lang || 'en-US';
console.log('Sending "' + message + '"...');
presentationConnection.send(JSON.stringify({ message, lang }));
}
//关闭连接,关闭连接后无法再控制弹出窗口
const closeLink = () => {
console.log('Closing connection...');
presentationConnection.close();
}
//关闭连接后,换可以重新再次连接,这里需要指定id
// presentationRequest.reconnect(presentationId);
//关闭弹窗,结束连接调用
const endLink = () => {
console.log('Terminating connection...');
presentationConnection.terminate();
}
// 挂载
onMounted(() => {
//监视连接是否可用
presentationRequest.addEventListener('connectionavailable', function (event) {
presentationConnection = event.connection;
presentationConnection.addEventListener('close', function () {
console.log('> Connection closed.');
});
presentationConnection.addEventListener('terminate', function () {
console.log('> Connection terminated.');
});
presentationConnection.addEventListener('message', function (event) {
console.log('> ' + event.data);
});
});
//监视可用的显示器
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);
});
})
// 监听
watchEffect(() => {
console.log('监听')
})
// 页面卸载
onUnmounted(() => {
console.log('页面卸载')
})
return {
...toRefs(state), endLink, closeLink, sendMsg, startProjectionScreen
}
}
}
</script>
<style scoped>
</style>
6、详细demo附件下载
2024年11月04日更新,看到评论区有部分朋友,遇到问题,我更新了相关Git完整Demo地址见下文
1、vue3+ts+vite 报错 PresentationRequest 未定义
答:应该是ts未定义的报错,我当时做这个的时候,这个功能刚推出ts支持应该不完善,我也不想去定义相关的ts就直接使用// @ts-nocheck(这个指令用于告诉TypeScript编译器不在当前文件中执行类型检查)
2、发现用ip访问不行,用127.0.0.1可以
答:直接用IP不行是因为没有https支持吧,我记得这个协议是需要https的支持的,具体要看下报错信息了,127.0.0.1是本机地址所以没事
2024年11月04日更新,看到评论区有部分朋友,遇到了些问题,文章末尾有更新,相关的Git仓库地址也附上了
secondScreen.vue:16 Uncaught (in promise) ReferenceError: PresentationRequest is not defined
的确不行,会报这样的错,vue3.4+ts+vite5.3
我刚刚测试,并没有出现综上所出现的问题,我的版本信息如下
"vue": "^3.5.12"
"typescript": "~5.6.2",
"vite": "^5.4.10",
这个得空我看看,之前也有人说过不行
发现用ip访问不行,用127.0.0.1可以
用IP不行是因为没有https支持吧,我记得这个协议是需要https的支持的,具体要看下报错信息了
完了这个只能支持 https下使用,此功能仅在安全上下文(HTTPS)中可用
是的,所以我特别注明需要注意必须为https环境
学习下
1
vue 在方法中new PresentationRequest会提示Cannot read properties of undefined (reading 'PresentationRequest')
如果报错,请检查你的代码校验工具,将这个错误忽略。
1
求demo 大佬
你需要啥样的demo,我附件中是已经有原生的demo和vue3的了
是有vite打包后 提示ReferenceError: PresentationRequest is not defined 大佬知道如何解决么
文章的示例代码和示例代码文件,我都更新了,可以参考
这个是TS的报错吧,直接使用 // @ts-nocheck(这个指令用于告诉TypeScript编译器不在当前文件中执行类型检查)
vue3+ts+vite 报错 PresentationRequest 未定义 大佬知道什么问题么
向大神学习
学习一下
没看到vue demo啊
下载demo,里面有个vue的demo页
6666
demo
文章末尾有demo可以直接下载!
好东西
1111
666
学习一下
加油
想试试
直接下载demo尝试吧
猛猛猛
1
vue 报错
具体报啥错误,您可以看下,我的demo,之前下载链接失效了,现在更新了
vue都创建不了PresentationRequest对象呀,敢问大佬如何实现的
vue可以阿,直接new就行了,具体你可以看下我的demo,之前demo下载失效了,我现在更新了