主要分为三个部分:WebSocket 服务器、电脑展示端页面和手机控制端页面。
1. WebSocket 服务器(Node.js)
首先需要搭建一个 WebSocket 服务器作为中间层,转发控制指令:
javascript
// server.js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
// 存储连接的客户端,区分控制端和展示端
let controller = null; // 手机控制端
let display = null; // 电脑展示端
console.log('WebSocket服务器启动,端口: 8080');
wss.on('connection', (ws) => {
console.log('新客户端连接');
// 接收客户端消息
ws.on('message', (message) => {
try {
const data = JSON.parse(message.toString());
// 客户端类型注册
if (data.type === 'register') {
if (data.role === 'controller') {
controller = ws;
console.log('手机控制端已连接');
// 通知控制端是否已有展示端连接
ws.send(JSON.stringify({
type: 'status',
hasDisplay: !!display
}));
} else if (data.role === 'display') {
display = ws;
console.log('电脑展示端已连接');
// 通知展示端是否已有控制端连接
ws.send(JSON.stringify({
type: 'status',
hasController: !!controller
}));
}
}
// 转发控制指令
else if (data.type === 'control' && controller === ws && display) {
console.log('转发控制指令:', data);
display.send(JSON.stringify(data));
}
} catch (e) {
console.error('消息处理错误:', e);
}
});
// 客户端断开连接
ws.on('close', () => {
if (ws === controller) {
controller = null;
console.log('手机控制端已断开');
// 通知展示端控制端已断开
if (display) {
display.send(JSON.stringify({
type: 'status',
hasController: false
}));
}
} else if (ws === display) {
display = null;
console.log('电脑展示端已断开');
// 通知控制端展示端已断开
if (controller) {
controller.send(JSON.stringify({
type: 'status',
hasDisplay: false
}));
}
}
});
// 错误处理
ws.on('error', (error) => {
console.error('WebSocket错误:', error);
});
});
2. 电脑展示端页面(display.html)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>电脑展示端</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
transition: background-color 0.5s;
}
#status {
position: fixed;
top: 20px;
padding: 10px 20px;
border-radius: 5px;
background-color: #f0f0f0;
}
.connected {
background-color: #4CAF50;
color: white;
}
.disconnected {
background-color: #f44336;
color: white;
}
#displayArea {
width: 80%;
height: 60%;
border: 2px solid #333;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
transition: all 0.3s;
}
</style>
</head>
<body>
<div id="status" class="disconnected">未连接到控制端</div>
<div id="displayArea">等待控制指令...</div>
<script>
let ws;
let reconnectInterval;
const reconnectDelay = 3000; // 重连间隔3秒
// 连接WebSocket服务器
function connect() {
// 关闭可能存在的旧连接
if (ws) {
ws.close();
}
// 获取当前页面的主机地址,确保使用相同的IP
const host = window.location.hostname;
ws = new WebSocket(`ws://${host}:8080`);
// 连接成功
ws.onopen = () => {
console.log('WebSocket连接成功');
// 注册为展示端
ws.send(JSON.stringify({
type: 'register',
role: 'display'
}));
// 清除重连计时器
clearInterval(reconnectInterval);
updateStatus(true);
};
// 接收消息
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('收到消息:', data);
// 处理状态更新
if (data.type === 'status') {
updateStatus(data.hasController);
}
// 处理控制指令
else if (data.type === 'control') {
handleControl(data.command, data.value);
}
};
// 连接关闭
ws.onclose = () => {
console.log('WebSocket连接关闭');
updateStatus(false);
// 启动重连机制
startReconnect();
};
// 连接错误
ws.onerror = (error) => {
console.error('WebSocket错误:', error);
updateStatus(false);
};
}
// 启动重连机制
function startReconnect() {
console.log(`将在${reconnectDelay/1000}秒后尝试重连...`);
reconnectInterval = setInterval(connect, reconnectDelay);
}
// 更新连接状态显示
function updateStatus(connected) {
const statusEl = document.getElementById('status');
if (connected) {
statusEl.textContent = '已连接到控制端';
statusEl.className = 'connected';
} else {
statusEl.textContent = '未连接到控制端,正在尝试重连...';
statusEl.className = 'disconnected';
}
}
// 处理控制指令
function handleControl(command, value) {
const displayArea = document.getElementById('displayArea');
switch(command) {
case 'changeColor':
document.body.style.backgroundColor = value;
displayArea.textContent = `背景色已更改为: ${value}`;
break;
case 'changeText':
displayArea.textContent = value;
break;
case 'scale':
displayArea.style.transform = `scale(${value})`;
displayArea.textContent = `缩放比例: ${value}`;
break;
case 'rotate':
displayArea.style.transform = `rotate(${value}deg)`;
displayArea.textContent = `旋转角度: ${value}°`;
break;
default:
console.log('未知指令:', command);
}
}
// 初始化连接
connect();
</script>
</body>
</html>
3. 手机控制端页面(controller.html)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>手机控制端</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
margin: 0;
background-color: #f5f5f5;
}
#status {
padding: 10px;
border-radius: 5px;
text-align: center;
margin-bottom: 20px;
}
.connected {
background-color: #4CAF50;
color: white;
}
.disconnected {
background-color: #f44336;
color: white;
}
.control-group {
background-color: white;
padding: 15px;
border-radius: 10px;
margin-bottom: 15px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
h3 {
margin-top: 0;
color: #333;
}
button {
width: 100%;
padding: 12px;
margin-bottom: 10px;
border: none;
border-radius: 5px;
background-color: #2196F3;
color: white;
font-size: 16px;
cursor: pointer;
}
button:hover {
background-color: #0b7dda;
}
input[type="color"], input[type="text"] {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ddd;
border-radius: 5px;
box-sizing: border-box;
}
input[type="range"] {
width: 100%;
margin: 15px 0;
}
.range-value {
text-align: center;
font-weight: bold;
}
</style>
</head>
<body>
<div id="status" class="disconnected">未连接到展示端</div>
<div class="control-group">
<h3>背景颜色控制</h3>
<input type="color" id="bgColor" value="#ffffff">
<button onclick="sendCommand('changeColor', document.getElementById('bgColor').value)">
更改背景色
</button>
</div>
<div class="control-group">
<h3>文本控制</h3>
<input type="text" id="displayText" placeholder="输入要显示的文本">
<button onclick="sendCommand('changeText', document.getElementById('displayText').value)">
更新显示文本
</button>
</div>
<div class="control-group">
<h3>缩放控制</h3>
<input type="range" id="scale" min="0.5" max="2" step="0.1" value="1">
<div class="range-value" id="scaleValue">1.0</div>
<button onclick="sendCommand('scale', document.getElementById('scale').value)">
应用缩放
</button>
</div>
<div class="control-group">
<h3>旋转控制</h3>
<input type="range" id="rotate" min="0" max="360" step="15" value="0">
<div class="range-value" id="rotateValue">0°</div>
<button onclick="sendCommand('rotate', document.getElementById('rotate').value)">
应用旋转
</button>
</div>
<script>
let ws;
let reconnectInterval;
const reconnectDelay = 3000; // 重连间隔3秒
let isConnected = false;
// 更新滑块显示值
document.getElementById('scale').addEventListener('input', function() {
document.getElementById('scaleValue').textContent = this.value;
});
document.getElementById('rotate').addEventListener('input', function() {
document.getElementById('rotateValue').textContent = this.value + '°';
});
// 连接WebSocket服务器
function connect() {
// 关闭可能存在的旧连接
if (ws) {
ws.close();
}
// 获取当前页面的主机地址,确保使用相同的IP
const host = window.location.hostname;
ws = new WebSocket(`ws://${host}:8080`);
// 连接成功
ws.onopen = () => {
console.log('WebSocket连接成功');
// 注册为控制端
ws.send(JSON.stringify({
type: 'register',
role: 'controller'
}));
// 清除重连计时器
clearInterval(reconnectInterval);
};
// 接收消息
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('收到消息:', data);
// 处理状态更新
if (data.type === 'status') {
isConnected = data.hasDisplay;
updateStatus(isConnected);
}
};
// 连接关闭
ws.onclose = () => {
console.log('WebSocket连接关闭');
isConnected = false;
updateStatus(false);
// 启动重连机制
startReconnect();
};
// 连接错误
ws.onerror = (error) => {
console.error('WebSocket错误:', error);
isConnected = false;
updateStatus(false);
};
}
// 启动重连机制
function startReconnect() {
console.log(`将在${reconnectDelay/1000}秒后尝试重连...`);
reconnectInterval = setInterval(connect, reconnectDelay);
}
// 更新连接状态显示
function updateStatus(connected) {
const statusEl = document.getElementById('status');
if (connected) {
statusEl.textContent = '已连接到展示端';
statusEl.className = 'connected';
} else {
statusEl.textContent = '未连接到展示端,正在尝试重连...';
statusEl.className = 'disconnected';
}
}
// 发送控制指令
function sendCommand(command, value) {
if (!isConnected || !ws) {
alert('未连接到展示端,请稍后重试');
return;
}
try {
ws.send(JSON.stringify({
type: 'control',
command: command,
value: value
}));
console.log(`发送指令: ${command} = ${value}`);
} catch (e) {
console.error('发送指令失败:', e);
alert('发送指令失败,请重试');
}
}
// 初始化连接
connect();
</script>
</body>
</html>
使用说明
-
环境准备:
- 安装 Node.js
- 安装 WebSocket 模块:
npm install ws
-
启动服务器:
- 运行命令:
node server.js - 服务器将在 8080 端口启动
- 运行命令:
-
部署页面:
- 将 display.html 和 controller.html 放在 Web 服务器(如 Nginx、Apache)或使用简单的 HTTP 服务器
- 例如:使用 Node.js 的 http-server:
npx http-server
-
使用方法:
- 确保手机和电脑连接到同一网络
- 在电脑上打开展示端页面:
http://[服务器IP]:8080/display.html - 在手机上打开控制端页面:
http://[服务器IP]:8080/controller.html - 现在可以通过手机控制电脑页面的各种属性了
断线重连机制说明
- 当连接断开时,客户端会自动尝试重连
- 重连间隔为 3 秒,可通过修改
reconnectDelay变量调整 - 重连过程中会显示状态提示
- 重连成功后会自动恢复通信,无需手动操作
这个实现提供了基本的控制功能(颜色、文本、缩放、旋转),你可以根据需要扩展更多控制指令和 UI 元素。