安装peerjs-server
github地址:https://github.com/peers/peerjs-server
npm install peer -g
启动服务
peerjs --port 9000 --key peerjs --path /myapp
前端使用的配置为
<script>
const peer = new Peer('someid', {
host: 'localhost',
path: '/myapp',
port: 9000,
key: `peerjs`,
});
</script>
index.html为主页面,通过peerjs分享给副页面player.html
使用http-server启动本地服务
安装http-server
npm install http-server -g
启动
http-server
peerjs客户端库
github地址:https://github.com/peers/peerjs
文档地址:https://peerjs.com/
使用localhost访问
本文地址:http://localhost:8081/

index.html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>canvas webrtc</title>
<script src="https://unpkg.com/peerjs@1.3.2/dist/peerjs.min.js"></script>
<style>
.myCanvas {
background-color: #fff;
}
</style>
</head>
<body>
<div>canvas webrtc</div>
<input id="aa" />
<canvas id="myCanvas" class="myCanvas" width="200" height="100" style="border:1px solid #000000;"></canvas>
</body>
<script>
// 模拟键盘点击事件
function fireKeyEvent(el, evtType, keyCode) {
var evtObj;
if (document.createEvent) {
if (window.KeyEvent) {//firefox 浏览器下模拟事件
evtObj = document.createEvent('KeyEvents');
evtObj.initKeyEvent(evtType, true, true, window, true, false, false, false, keyCode, 0);
} else {//chrome 浏览器下模拟事件
evtObj = document.createEvent('UIEvents');
evtObj.initUIEvent(evtType, true, true, window, 1);
delete evtObj.keyCode;
if (typeof evtObj.keyCode === "undefined") {//为了模拟keycode
Object.defineProperty(evtObj, "keyCode", { value: keyCode });
} else {
evtObj.key = String.fromCharCode(keyCode);
}
if (typeof evtObj.ctrlKey === 'undefined') {//为了模拟ctrl键
Object.defineProperty(evtObj, "ctrlKey", { value: true });
} else {
evtObj.ctrlKey = true;
}
}
el.dispatchEvent(evtObj);
} else if (document.createEventObject) {//IE 浏览器下模拟事件
evtObj = document.createEventObject();
evtObj.keyCode = keyCode
el.fireEvent('on' + evtType, evtObj);
}
}
// 绘制边框
function renderBorder(ctx) {
// 首先获取画布的宽高
const width = ctx.canvas.width;
const height = ctx.canvas.height;
ctx.beginPath(); // 开始一个新的路径
ctx.moveTo(0, 0); // 将路径的起始点移动到左上角
ctx.lineTo(width, 0); // 使用直线连接到右上角(并不绘制)
ctx.lineTo(width, height); // ...右下角
ctx.lineTo(0, height); // ...左下角
ctx.closePath(); // 结束一个新的路径
ctx.stroke(); // 绘制当前已知路径
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, width, height)
}
// 绘制一个球
function renderBall(ctx, ball) {
const x = ball.x + ball.radius; // 圆弧中心(圆心)的 x 轴坐标。
const y = ball.y + ball.radius; // 圆弧中心(圆心)的 y 轴坐标。
const radius = ball.radius; // 半径
const startAngle = 0; // 圆弧的起始点
const endAngle = 2 * Math.PI; // 圆弧的结束点
ctx.beginPath(); // 开始一个新的路径
ctx.arc(x, y, radius, startAngle, endAngle);
ctx.closePath(); // 结束一个新的路径
ctx.fillStyle = ball.color; // 颜色
ctx.fill(); // 填充
}
// 更新ball
function updateBall(ctx, ball) {
const width = ctx.canvas.width;
const height = ctx.canvas.height;
ball.x += ball.vx;
ball.y += ball.vy;
ball.vy += ball.g;
if (ball.y + ball.radius >= height) {
ball.y = height - ball.radius;
ball.vy = -ball.vy * 0.5;
}
if (ball.y + ball.radius <= 0) {
ball.y = 0 - ball.radius;
ball.vy = -ball.vy * 0.5;
}
if (ball.x + ball.radius >= width) {
ball.x = width - ball.radius;
ball.vx = -ball.vx * 0.5;
}
if (ball.x + ball.radius <= 0) {
ball.x = 0 - ball.radius;
ball.vx = -ball.vx * 0.5;
}
return ball;
};
window.onload = () => {
// 监听当前页面键盘点击事件
document.onkeydown = function (event) {
var e = event || window.event
console.log(`主页面键盘事件`, e.keyCode)
};
console.log(`设置canvas`)
const canvas = document.getElementById('myCanvas')
// 获取canvasDom
// 手动设置宽高
canvas.width = 800;
canvas.height = 800;
// 获取canvas上下文
const ctx = canvas.getContext("2d");
let ball = {
x: 0, // 当前x轴坐标
y: 0, // 当前y轴坐标
radius: 10, // 半径
g: 0.1, // 重力加速度
vx: 8, // x轴移动速度
vy: 4, // y轴移动速度
color: "red", // 颜色
};
setInterval(() => {
// 先将之前绘制的擦除
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// 绘制边框
renderBorder(ctx);
// 绘制球
renderBall(ctx, ball);
ball = updateBall(ctx, ball);
}, 20);
};
// console.log(`连接peer`, Peer)
// 需要建立webrtc服务
// https://github.com/peers/peerjs-server
// 浏览器使用
// https://github.com/peers/peerjs
var peer = null;
var startId = null;
var receiveId = null;
var conn = null;
let idStr = `123`
peer = new Peer(idStr, {
host: 'localhost',
path: '/myapp',
port:9000,
key: `peerjs`,
});
peer.on('open', function (id) {
startId = id;
console.log('My peer ID is: ' + id);
});
peer.on('connection', function (conn) {
console.log("被连接", conn);
receiveId = conn.peer;
conn.on('data', (data) => {
console.log("收到消息", data, receiveId);
if (data == `screen`) {
// 获取当前屏幕视频流
// navigator.mediaDevices.getDisplayMedia({ video: true })
// .then(stream => {
// // 成功回调的流,将它赋给video元素;
// console.log(`成功捕获屏幕数据`)
// conn6 = peer.connect(receiveId);
// conn6.on('open', () => {
// //直接发送消息
// console.log("发送消息 22")
// conn6.send("我来了 22");
// // videoElement.srcObject = stream;
// var call = peer.call(receiveId,
// stream);
// });
// }, error => {
// console.log("Unable to acquire screen capture", error);
// });
// 获取canvas画布的视频流
var canvasElt = document.getElementById('myCanvas')
var stream = canvasElt.captureStream();
console.log(`成功捕获屏幕数据 canvas`)
conn6 = peer.connect(receiveId);
conn6.on('open', () => {
//直接发送消息
console.log("发送消息 22")
conn6.send("我来了 22");
stream.getTracks().forEach(track => {
console.log(`1111`)
// pc.addTrack(track, stream)
var call = peer.call(receiveId,
stream);
});
});
} else {
console.log(`设置键盘事件`, data)
fireKeyEvent(document.getElementById('aa'), 'keydown', data);
}
});
});
</script>
</html>
使用localhost访问
本文地址:http://localhost:8081/player.html?type=call&peerid=123

player.html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>player</title>
<script src="https://unpkg.com/peerjs@1.3.2/dist/peerjs.min.js"></script>
<style>
.videoDom {
width: 500px;
height: 300px;
border: 1px solid red;
}
</style>
</head>
<body>
<div>player</div>
<video id="videotest" class="videoDom" controls></video>
</body>
<script>
// 获取参数
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
if (pair[0] == variable) { return pair[1]; }
}
return (false);
}
// 类型(call为副屏玩家)
let dataType = getQueryVariable(`type`)
// 房间id(peerJs)
let peerId = getQueryVariable(`peerid`)
// 玩家角色(玩家一,玩家二,玩家三,玩家四)
let playNum = getQueryVariable(`playNum`)
if (dataType == `call`) {
var peer = null;
var startId = null;
var receiveId = null;
var conn = null;
startId = peerId
console.log(`type call`, peerId)
peer = new Peer(``, {
host: 'localhost',
path: '/myapp',
port: 9000,
key: `peerjs`,
});
// console.log(peer)
peer.on('open', function (id) {
receiveId = id;
console.log('My peer ID is: ' + id, startId);
//这里先向start建立连接,让start知道这个端的id(reveivedId)
conn = peer.connect(startId);
conn.on('open', () => {
//直接发送消息
console.log("发送消息")
conn.send("screen");
document.onkeydown = function (event) {
var e = event || window.event
console.log(`e`, e.keyCode, e)
conn.send(e.keyCode);
};
});
});
peer.on('connection', function (conn) {
console.log("被连接3333");
conn.on('data', (data) => {
console.log("收到消息222", data);
});
peer.on('call', call => {
console.log(' peer call');
call.answer();
call.on('stream', stream => {
// somthing to do
let videoDom = document.getElementById("videotest")
console.log(`接收到视频流`, videoDom, stream)
videoDom.srcObject = stream
});
});
});
}
</script>
</html>
本文主页面peerid强制设置为123,副页面通过url获取对应的peerid连接
- webrtc通信,可以使用localhost或者https域名,建议不要使用ip访问
- 本文演示效果为主页面canvas或者chrome tab页面视频流分享给副页面(具体内容见代码实现)
- 使用webrtc协议(如本文的peerjs库),可以实现如nes,街机游戏在线联机对战(已实现,本文不放置代码)
END.