修复wifi模式
优化网页操作页面
This commit is contained in:
1 parent
067af8f2cc
commit
c91b0507ac
13 files changed
+898
-4213
No files matched your search
@@ -1,442 +1,472 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>控制器</title>
|
||||
<style>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>legged wheel robot web ctrl</title>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;background-color:#fff;font-family:'Segoe UI',Arial,sans-serif;padding:20px}
|
||||
.page-title{color:#333;margin-bottom:40px;text-align:center;font-weight:600}
|
||||
.joysticks-container{display:flex;gap:60px;align-items:center;justify-content:center;flex-wrap:wrap;width:100%;max-width:1200px}
|
||||
.joystick-item{display:flex;flex-direction:column;align-items:center;gap:15px}
|
||||
.joystick-label{color:#333;font-weight:500;font-size:16px;text-align:center;padding:5px 10px;border-radius:4px;background-color:rgba(255,255,255,.8)}
|
||||
.joystick-wrapper{display:flex;position:relative}
|
||||
.joystick-wrapper.vertical{flex-direction:column;align-items:center}
|
||||
.joystick-wrapper.horizontal{flex-direction:row;align-items:center}
|
||||
.joystick-track{position:relative;box-shadow:0 4px 12px rgba(0,0,0,.1);background-color:#e0e0e0;border-radius:30px;overflow:hidden;transition:box-shadow .2s ease}
|
||||
.joystick-track:hover{box-shadow:0 6px 16px rgba(0,0,0,.15)}
|
||||
.joystick-track.vertical{width:60px;height:300px}
|
||||
.joystick-track.horizontal{width:300px;height:60px}
|
||||
.joystick-handle{position:absolute;width:50px;height:50px;background-color:#4a90e2;border-radius:50%;box-shadow:0 3px 8px rgba(0,0,0,.25);cursor:grab;transition:transform .1s ease-out,background-color .2s ease;user-select:none;-webkit-user-select:none;border:2px solid rgba(255,255,255,.7)}
|
||||
.joystick-handle:active{cursor:grabbing;background-color:#357abd;transform:scale(.95)}
|
||||
.joystick-wrapper.vertical .joystick-handle{left:5px;top:125px}
|
||||
.joystick-wrapper.horizontal .joystick-handle{left:125px;top:5px}
|
||||
.value-display{display:flex;align-items:center;justify-content:center;background-color:#e0e0e0;color:#fff;border-radius:8px;font-weight:600;font-size:18px;width:60px;height:40px;box-shadow:0 2px 5px rgba(0,0,0,.1);transition:background-color .2s ease}
|
||||
.value-display.active{background-color:#4a90e2}
|
||||
.joystick-wrapper.vertical .value-display{margin-top:20px}
|
||||
.joystick-wrapper.horizontal .value-display{margin-left:20px}
|
||||
.usage-info{margin-top:40px;color:#666;font-size:14px;text-align:center;max-width:600px;line-height:1.5}
|
||||
@media (max-width:1024px){.joysticks-container{gap:40px}
|
||||
.joystick-track.vertical{width:50px;height:250px}
|
||||
.joystick-track.horizontal{width:250px;height:50px}
|
||||
.joystick-handle{width:40px;height:40px}
|
||||
.joystick-wrapper.vertical .joystick-handle{left:5px;top:105px}
|
||||
.joystick-wrapper.horizontal .joystick-handle{left:105px;top:5px}
|
||||
body{display:flex;flex-direction:column;align-items:center;min-height:100vh;background-color:#fff;font-family:'Segoe UI',Arial,sans-serif;padding:15px;overflow:hidden;width:100vw}
|
||||
.page-title{color:#333;margin-bottom:15px;text-align:center;font-weight:600;font-size:1.3rem;width:100%}
|
||||
.connection-status{margin-bottom:15px;padding:6px 12px;border-radius:4px;font-weight:500;font-size:14px}
|
||||
.status-connected{background-color:#e8f5e9;color:#2e7d32}
|
||||
.status-disconnected{background-color:#ffebee;color:#c62828}
|
||||
.btn{display:inline-block;font-size:15px;width:100px;height:60px;text-align:center;border-radius:12px;background-color:#fff;color:#6495ed;border:3px solid #6495ed;padding:0;transition:all .3s;margin:10px 0}
|
||||
.row{display:flex;width:100%;justify-content:space-between;align-items:center;gap:10px}
|
||||
.columnLateral{flex:1;min-width:0;text-align:center}
|
||||
#joy1Div,#joy2Div{width:100%;max-width:200px;height:200px;margin:10px auto}
|
||||
@media (max-width:360px){.page-title{font-size:1.1rem}
|
||||
#joy1Div,#joy2Div{height:160px}
|
||||
.btn{width:80px;height:50px;font-size:14px}
|
||||
}
|
||||
@media (max-width:768px){.joysticks-container{flex-direction:column;gap:60px}}
|
||||
.button-container {
|
||||
display: flex;
|
||||
gap: 50px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.control-btn {
|
||||
padding: 12px 24px;
|
||||
background: #fff;
|
||||
color: #2196F3;
|
||||
border: 2px solid #2196F3;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
</head>
|
||||
<body onload="javascript:socket_init()">
|
||||
<h1 class="page-title">控制器</h1>
|
||||
|
||||
<div class="button-container">
|
||||
<button class="control-btn" id="blink">blink</button>
|
||||
<button class="control-btn" id="dir">jump</button>
|
||||
</div>
|
||||
<div class="joysticks-container">
|
||||
<!-- 1. 水平摇杆 -->
|
||||
<div class="joystick-item">
|
||||
<div class="joystick-label">摇摆</div>
|
||||
<div id="roll" class="joystick-wrapper horizontal"></div>
|
||||
|
||||
<!-- onload 事件在页面载入完成后立即触发 -->
|
||||
<body onload="javascript:socket_init()">
|
||||
<h1 class="page-title">WLROBOT WiFi遥控模式</h1>
|
||||
|
||||
<div class="connection-status status-disconnected" id="connectionStatus">未连接</div>
|
||||
|
||||
<center>
|
||||
<div class="row">
|
||||
<div class="columnLateral">
|
||||
<div id="joy2Div" style="width:200px;height:200px;margin:10px"></div>
|
||||
<div id="joy2Info">
|
||||
<span>高度: <span id="joy2YValue">30</span></span>
|
||||
<br>
|
||||
<span>倾角: <span id="joy2XValue">0</span></span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="columnLateral">
|
||||
<div id="joy1Div" style="width:200px;height:200px;margin:10px"></div>
|
||||
<div id="joy1Info">
|
||||
<span>前后: <span id="joy1YValue">0</span></span>
|
||||
<br>
|
||||
<span>左右: <span id="joy1XValue">0</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. 垂直摇杆 -->
|
||||
<div class="joystick-item">
|
||||
<div class="joystick-label">高度</div>
|
||||
<div id="height" class="joystick-wrapper vertical"></div>
|
||||
|
||||
|
||||
<div class="buttons">
|
||||
<button class="btn" id="jump">Jump</button>
|
||||
</div>
|
||||
|
||||
<!-- 3. 垂直摇杆 -->
|
||||
<div class="joystick-item">
|
||||
<div class="joystick-label">前后</div>
|
||||
<div id="joy_y" class="joystick-wrapper vertical"></div>
|
||||
</div>
|
||||
|
||||
<!-- 4. 水平摇杆 -->
|
||||
<div class="joystick-item">
|
||||
<div class="joystick-label">左右</div>
|
||||
<div id="joy_x" class="joystick-wrapper horizontal"></div>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
|
||||
<script>
|
||||
var socket; // socket通信
|
||||
var isConnected = false; // 连接状态
|
||||
|
||||
var g_roll = 0;
|
||||
var g_h = 30;
|
||||
var g_stable = 0;
|
||||
var joyX = 0;
|
||||
var joyY = 0;
|
||||
|
||||
// 更新连接状态显示
|
||||
function updateConnectionStatus(connected) {
|
||||
isConnected = connected;
|
||||
const statusElement = document.getElementById('connectionStatus');
|
||||
if (connected) {
|
||||
statusElement.textContent = '已连接';
|
||||
statusElement.className = 'connection-status status-connected';
|
||||
} else {
|
||||
statusElement.textContent = '未连接';
|
||||
statusElement.className = 'connection-status status-disconnected';
|
||||
}
|
||||
}
|
||||
|
||||
function socket_init() {
|
||||
socket = new WebSocket('ws://' + window.location.hostname + ':81/'); // sta模式
|
||||
}
|
||||
|
||||
// 摇杆控制器类 - 封装所有功能
|
||||
class Joystick {
|
||||
/**
|
||||
* 创建一个新的摇杆实例
|
||||
* @param {string} containerId - 容器元素的ID
|
||||
* @param {string} direction - 方向,可选值:'vertical' 或 'horizontal'
|
||||
*/
|
||||
constructor(containerId, direction = 'vertical', min = -100, max = 100) {
|
||||
// 初始化属性
|
||||
this.container = document.getElementById(containerId);
|
||||
this.direction = direction;
|
||||
this.id = containerId;
|
||||
this.isDragging = false;
|
||||
this.startPos = 0;
|
||||
this.currentPos = 0;
|
||||
this.centerPos = 0;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.range = max - min;
|
||||
try {
|
||||
// 根据环境选择合适的连接方式
|
||||
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
socket = new WebSocket(wsProtocol + '//' + window.location.hostname + ':81/');
|
||||
|
||||
// 创建摇杆元素
|
||||
this.createElements();
|
||||
// 连接成功事件
|
||||
socket.onopen = function() {
|
||||
// console.log('WebSocket连接已建立');
|
||||
updateConnectionStatus(true);
|
||||
};
|
||||
|
||||
// 获取元素引用
|
||||
this.handle = this.container.querySelector('.joystick-handle');
|
||||
this.track = this.container.querySelector('.joystick-track');
|
||||
this.valueDisplay = this.container.querySelector('.value-display');
|
||||
// 接收消息事件
|
||||
socket.onmessage = function(event) {
|
||||
console.log('收到消息:', event.data);
|
||||
// 可以在这里处理从服务器返回的消息
|
||||
};
|
||||
|
||||
// 初始化尺寸和位置
|
||||
this.initDimensions();
|
||||
// 连接关闭事件
|
||||
socket.onclose = function() {
|
||||
console.log('WebSocket连接已关闭');
|
||||
updateConnectionStatus(false);
|
||||
// 尝试重连
|
||||
setTimeout(socket_init, 3000);
|
||||
};
|
||||
|
||||
// 绑定事件处理函数
|
||||
this.bindEvents();
|
||||
|
||||
// 设置初始位置
|
||||
this.setHandlePosition(this.centerPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建摇杆所需的DOM元素
|
||||
*/
|
||||
createElements() {
|
||||
// 创建轨道元素
|
||||
const track = document.createElement('div');
|
||||
track.className = `joystick-track ${this.direction}`;
|
||||
|
||||
// 创建把手元素
|
||||
const handle = document.createElement('div');
|
||||
handle.className = 'joystick-handle';
|
||||
|
||||
// 创建数值显示元素
|
||||
const valueDisplay = document.createElement('div');
|
||||
valueDisplay.className = 'value-display';
|
||||
valueDisplay.textContent = '0';
|
||||
|
||||
// 组合元素
|
||||
track.appendChild(handle);
|
||||
this.container.appendChild(track);
|
||||
this.container.appendChild(valueDisplay);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化摇杆尺寸和位置参数
|
||||
*/
|
||||
initDimensions() {
|
||||
if (this.direction === 'vertical') {
|
||||
// 纵向摇杆尺寸
|
||||
this.trackSize = this.track.offsetHeight;
|
||||
this.handleSize = this.handle.offsetHeight;
|
||||
this.centerPos = (this.trackSize - this.handleSize) / 2;
|
||||
} else {
|
||||
// 横向摇杆尺寸
|
||||
this.trackSize = this.track.offsetWidth;
|
||||
this.handleSize = this.handle.offsetWidth;
|
||||
this.centerPos = (this.trackSize - this.handleSize) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 限制值在指定范围内
|
||||
* @param {number} value - 要限制的值
|
||||
* @param {number} min - 最小值
|
||||
* @param {number} max - 最大值
|
||||
* @returns {number} 限制后的值
|
||||
*/
|
||||
clamp(value, min, max) {
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新显示的数值
|
||||
* @param {number} position - 把手的位置
|
||||
*/
|
||||
updateValue(position) {
|
||||
// 计算原始值(-100~100)
|
||||
const offset = this.direction === 'vertical'
|
||||
? this.centerPos - position
|
||||
: position - this.centerPos;
|
||||
|
||||
const rawPercentage = (offset / this.centerPos) * 100;
|
||||
const rawValue = this.clamp(Math.round(rawPercentage), -100, 100);
|
||||
|
||||
// 计算映射值
|
||||
const mappedValue = Math.round(
|
||||
((rawValue + 100) / 200) * this.range + this.min
|
||||
);
|
||||
|
||||
// 更新显示映射值
|
||||
this.valueDisplay.textContent = mappedValue;
|
||||
|
||||
// 触发事件携带双重值
|
||||
this.dispatchValueChange({
|
||||
raw: rawValue,
|
||||
mapped: mappedValue,
|
||||
min: this.min,
|
||||
max: this.max
|
||||
});
|
||||
|
||||
// 活动状态样式
|
||||
if (mappedValue !== this.min && mappedValue !== this.max) {
|
||||
this.valueDisplay.classList.add('active');
|
||||
} else {
|
||||
this.valueDisplay.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发值变化事件
|
||||
* @param {number} value - 当前值
|
||||
*/
|
||||
dispatchValueChange({raw, mapped}) {
|
||||
const event = new CustomEvent('valuechange', {
|
||||
detail: {
|
||||
rawValue: raw,
|
||||
mappedValue: mapped,
|
||||
min: this.min,
|
||||
max: this.max,
|
||||
id: this.id,
|
||||
direction: this.direction,
|
||||
joystick: this
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置摇杆把手位置
|
||||
* @param {number} pos - 位置值(纵向是top,横向是left)
|
||||
*/
|
||||
setHandlePosition(pos) {
|
||||
// 限制在轨道范围内
|
||||
const minPos = 0;
|
||||
const maxPos = this.trackSize - this.handleSize;
|
||||
const constrainedPos = this.clamp(pos, minPos, maxPos);
|
||||
|
||||
// 设置位置
|
||||
if (this.direction === 'vertical') {
|
||||
this.handle.style.top = `${constrainedPos}px`;
|
||||
} else {
|
||||
this.handle.style.left = `${constrainedPos}px`;
|
||||
}
|
||||
|
||||
// 更新数值
|
||||
this.updateValue(constrainedPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回到中心位置(带动画效果)
|
||||
*/
|
||||
returnToCenter() {
|
||||
if (!this.isDragging) {
|
||||
const currentPosition = parseFloat(
|
||||
this.direction === 'vertical'
|
||||
? this.handle.style.top
|
||||
: this.handle.style.left
|
||||
) || this.centerPos;
|
||||
|
||||
const distance = this.centerPos - currentPosition;
|
||||
|
||||
// 如果距离很小,直接回到中心
|
||||
if (Math.abs(distance) < 1) {
|
||||
this.setHandlePosition(this.centerPos);
|
||||
return;
|
||||
}
|
||||
|
||||
// 平滑动画回到中心
|
||||
const step = distance * 0.5; // 每次移动剩余距离的20%
|
||||
this.setHandlePosition(currentPosition + step);
|
||||
|
||||
// 继续动画
|
||||
requestAnimationFrame(() => this.returnToCenter());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定事件处理函数
|
||||
*/
|
||||
bindEvents() {
|
||||
// 鼠标按下事件
|
||||
this.handle.addEventListener('mousedown', (e) => {
|
||||
this.isDragging = true;
|
||||
this.startPos = this.direction === 'vertical' ? e.clientY : e.clientX;
|
||||
this.currentPos = parseFloat(
|
||||
this.direction === 'vertical'
|
||||
? this.handle.style.top
|
||||
: this.handle.style.left
|
||||
) || this.centerPos;
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// 鼠标移动事件
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
if (this.isDragging) {
|
||||
const delta = (this.direction === 'vertical' ? e.clientY : e.clientX) - this.startPos;
|
||||
this.setHandlePosition(this.currentPos + delta);
|
||||
}
|
||||
});
|
||||
|
||||
// 鼠标释放事件
|
||||
document.addEventListener('mouseup', () => {
|
||||
if (this.isDragging) {
|
||||
this.isDragging = false;
|
||||
this.returnToCenter();
|
||||
}
|
||||
});
|
||||
|
||||
// 触摸屏开始事件
|
||||
this.handle.addEventListener('touchstart', (e) => {
|
||||
this.isDragging = true;
|
||||
this.startPos = this.direction === 'vertical'
|
||||
? e.touches[0].clientY
|
||||
: e.touches[0].clientX;
|
||||
this.currentPos = parseFloat(
|
||||
this.direction === 'vertical'
|
||||
? this.handle.style.top
|
||||
: this.handle.style.left
|
||||
) || this.centerPos;
|
||||
e.preventDefault(); // 防止触摸事件被浏览器默认处理
|
||||
});
|
||||
|
||||
// 触摸屏移动事件 - 修改事件绑定方式,添加passive选项
|
||||
document.addEventListener('touchmove', (e) => {
|
||||
if (this.isDragging) {
|
||||
const delta = (this.direction === 'vertical'
|
||||
? e.touches[0].clientY
|
||||
: e.touches[0].clientX) - this.startPos;
|
||||
this.setHandlePosition(this.currentPos + delta);
|
||||
e.preventDefault(); // 防止页面滚动
|
||||
}
|
||||
}, { passive: false }); // 关键修改:明确指定passive为false
|
||||
|
||||
// 触摸屏结束事件
|
||||
document.addEventListener('touchend', () => {
|
||||
if (this.isDragging) {
|
||||
this.isDragging = false;
|
||||
this.returnToCenter();
|
||||
}
|
||||
});
|
||||
|
||||
// 窗口大小变化时重新计算尺寸
|
||||
window.addEventListener('resize', () => {
|
||||
this.initDimensions();
|
||||
if (!this.isDragging) {
|
||||
this.setHandlePosition(this.centerPos);
|
||||
}
|
||||
});
|
||||
// 错误事件
|
||||
socket.onerror = function(error) {
|
||||
console.error('WebSocket错误:', error);
|
||||
updateConnectionStatus(false);
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('WebSocket初始化失败:', e);
|
||||
updateConnectionStatus(false);
|
||||
// 重试连接
|
||||
setTimeout(socket_init, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const controlState = {
|
||||
blink: 0,
|
||||
dir: "stop", // 4 = stop, 5 = jump
|
||||
roll: 0,
|
||||
height: 30,
|
||||
joy_y: 0,
|
||||
joy_x: 0
|
||||
};
|
||||
|
||||
// 统一发送函数
|
||||
function sendAllData() {
|
||||
const data = {
|
||||
dir: controlState.dir,
|
||||
roll: controlState.roll,
|
||||
height: controlState.height,
|
||||
joy_y: controlState.joy_y,
|
||||
joy_x: controlState.joy_x,
|
||||
blink: controlState.blink,
|
||||
stable: 1,
|
||||
function send_data() {
|
||||
var data = {
|
||||
'roll': g_roll,
|
||||
'height': g_h,
|
||||
'stable': g_stable,
|
||||
'mode': 'basic',
|
||||
'dir': "stop",
|
||||
'joy_y': joyY,
|
||||
'joy_x': joyX,
|
||||
|
||||
};
|
||||
console.log(data);
|
||||
// socket.send(JSON.stringify(data));
|
||||
|
||||
// 重置瞬时状态
|
||||
controlState.blink = 0;
|
||||
controlState.dir = "stop";
|
||||
// console.log(data);
|
||||
socket.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
// 初始化摇杆实例
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// 创建四个摇杆,按顺序:水平、垂直、垂直、水平
|
||||
const joysticks = [
|
||||
new Joystick('roll', 'horizontal', -30, 30),
|
||||
new Joystick('height', 'vertical', 0, 60),
|
||||
new Joystick('joy_y', 'vertical'),
|
||||
new Joystick('joy_x', 'horizontal')
|
||||
];
|
||||
function is_stable() {
|
||||
var obj = document.getElementById("stable");
|
||||
if(obj.checked) {
|
||||
// alert("is_stable checked");
|
||||
g_stable = 1;
|
||||
} else {
|
||||
// alert("is_stable unchecked");
|
||||
g_stable = 0;
|
||||
}
|
||||
send_data();
|
||||
}
|
||||
|
||||
// 绑定按钮
|
||||
var buttons = document.getElementsByClassName("btn");
|
||||
for(i=0;i<buttons.length;i++) {
|
||||
buttons[i].addEventListener("mousedown",btn_down,true);
|
||||
buttons[i].addEventListener("mouseup",btn_up,true);
|
||||
buttons[i].addEventListener("touchstart",btn_down,true);
|
||||
buttons[i].addEventListener("touchend",btn_up,true);
|
||||
}
|
||||
|
||||
function btn_down() {
|
||||
this.style = "background-color: cornflowerblue; color: white;";
|
||||
dir = (this.id == 'jump') ? 'jump' : 'stop';
|
||||
var data = {
|
||||
'dir': this.id,
|
||||
'mode': 'basic',
|
||||
'roll': g_roll,
|
||||
'height': g_h,
|
||||
'stable': g_stable,
|
||||
'joy_x': joyX,
|
||||
'joy_y': joyY,
|
||||
};
|
||||
// console.log(data);
|
||||
socket.send(JSON.stringify(data));
|
||||
|
||||
}
|
||||
|
||||
function btn_up() {
|
||||
this.style = "background-color: white; color: cornflowerblue;";
|
||||
|
||||
dir = (this.id == 'jump') ? 'stop' : 'jump';
|
||||
var data = {
|
||||
'dir': "stop",
|
||||
'mode': 'basic',
|
||||
'roll': g_roll,
|
||||
'height': g_h,
|
||||
'stable': g_stable,
|
||||
'joy_x': joyX,
|
||||
'joy_y': joyY,
|
||||
};
|
||||
// console.log(data); // 打印测试
|
||||
socket.send(JSON.stringify(data));
|
||||
|
||||
}
|
||||
|
||||
/*摇杆内容*/
|
||||
var JoyStick = (function(container, parameters) {
|
||||
parameters = parameters || {};
|
||||
var title = (typeof parameters.title === "undefined" ? "joystick" : parameters.title),
|
||||
width = (typeof parameters.width === "undefined" ? 0 : parameters.width),
|
||||
height = (typeof parameters.height === "undefined" ? 0 : parameters.height),
|
||||
internalFillColor = (typeof parameters.internalFillColor === "undefined" ? "#4a90e2" : parameters.internalFillColor),
|
||||
internalLineWidth = (typeof parameters.internalLineWidth === "undefined" ? 2 : parameters.internalLineWidth),
|
||||
internalStrokeColor = (typeof parameters.internalStrokeColor === "undefined" ? "#4a90e2" : parameters.internalStrokeColor),
|
||||
externalLineWidth = (typeof parameters.externalLineWidth === "undefined" ? 2 : parameters.externalLineWidth),
|
||||
externalStrokeColor = (typeof parameters.externalStrokeColor === "undefined" ? "#4a90e2" : parameters.externalStrokeColor),
|
||||
autoReturnToCenter = (typeof parameters.autoReturnToCenter === "undefined" ? true : parameters.autoReturnToCenter);
|
||||
|
||||
// 监听所有摇杆的数值变化事件
|
||||
document.querySelectorAll('.joystick-wrapper').forEach((wrapper, id) => {
|
||||
wrapper.addEventListener('valuechange', (e) => {
|
||||
// console.log(`摇杆 ${e.detail.id} (${e.detail.direction}) 值: ${e.detail.value}`);
|
||||
// console.log(e.detail);
|
||||
switch(e.detail.id) {
|
||||
case 'roll':
|
||||
controlState.roll = e.detail.mappedValue;
|
||||
break;
|
||||
case 'height':
|
||||
controlState.height = e.detail.mappedValue;
|
||||
break;
|
||||
case 'joy_y':
|
||||
controlState.joy_y = e.detail.mappedValue;
|
||||
break;
|
||||
case 'joy_x':
|
||||
controlState.joy_x = e.detail.mappedValue;
|
||||
break;
|
||||
}
|
||||
sendAllData();
|
||||
});
|
||||
});
|
||||
document.querySelectorAll('.control-btn').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
switch(e.target.id) {
|
||||
case 'blink':
|
||||
controlState.blink = 1;
|
||||
break;
|
||||
case 'dir':
|
||||
controlState.dir = "jump";
|
||||
break;
|
||||
// Create Canvas element and add it in the Container object
|
||||
var objContainer = document.getElementById(container);
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.id = title;
|
||||
if(width === 0) { width = objContainer.clientWidth; }
|
||||
if(height === 0) { height = objContainer.clientHeight; }
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
objContainer.appendChild(canvas);
|
||||
var context = canvas.getContext("2d");
|
||||
|
||||
var isPressing = 0;
|
||||
var isMoving = 0;
|
||||
var isRelease = 0;
|
||||
|
||||
var circumference = 2 * Math.PI;
|
||||
var internalRadius = (canvas.width - ((canvas.width / 2) + 10)) / 2;
|
||||
var maxMoveStick = internalRadius + 5;
|
||||
var externalRadius = internalRadius + 30;
|
||||
var centerX = canvas.width / 2;
|
||||
var centerY = canvas.height / 2;
|
||||
var directionHorizontalLimitPos = canvas.width / 10;
|
||||
var directionHorizontalLimitNeg = directionHorizontalLimitPos * -1;
|
||||
var directionVerticalLimitPos = canvas.height / 10;
|
||||
var directionVerticalLimitNeg = directionVerticalLimitPos * -1;
|
||||
// Used to save current position of stick
|
||||
var movedX = centerX;
|
||||
var movedY = centerY;
|
||||
|
||||
// Check if the device support the touch or not
|
||||
if("ontouchstart" in document.documentElement) {
|
||||
canvas.addEventListener("touchstart", onTouchStart, true);
|
||||
canvas.addEventListener("touchmove", onTouchMove, true);
|
||||
document.addEventListener("touchend", onTouchEnd, true);
|
||||
} else {
|
||||
canvas.addEventListener("mousedown", onMouseDown, true);
|
||||
canvas.addEventListener("mousemove", onMouseMove, true);
|
||||
document.addEventListener("mouseup", onMouseUp, true);
|
||||
}
|
||||
// Draw the object
|
||||
drawExternal();
|
||||
drawInternal();
|
||||
|
||||
/**
|
||||
* @desc Draw the external circle used as reference position
|
||||
*/
|
||||
function drawExternal() {
|
||||
context.beginPath();
|
||||
context.arc(centerX, centerY, externalRadius, 0, circumference, false);
|
||||
context.lineWidth = externalLineWidth;
|
||||
context.strokeStyle = externalStrokeColor;
|
||||
context.stroke();
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc Draw the internal stick in the current position the user have moved it
|
||||
*/
|
||||
function drawInternal() {
|
||||
context.beginPath();
|
||||
if(movedX < internalRadius) { movedX = maxMoveStick; }
|
||||
if((movedX + internalRadius) > canvas.width) { movedX = canvas.width - (maxMoveStick); }
|
||||
if(movedY < internalRadius) { movedY = maxMoveStick; }
|
||||
if((movedY + internalRadius) > canvas.height) { movedY = canvas.height - (maxMoveStick); }
|
||||
context.arc(movedX, movedY, internalRadius, 0, circumference, false);
|
||||
// create radial gradient
|
||||
var grd = context.createRadialGradient(centerX, centerY, 5, centerX, centerY, 200);
|
||||
// Light color
|
||||
grd.addColorStop(0, internalFillColor);
|
||||
// Dark color
|
||||
grd.addColorStop(1, internalStrokeColor);
|
||||
context.fillStyle = grd;
|
||||
context.fill();
|
||||
context.lineWidth = internalLineWidth;
|
||||
context.strokeStyle = internalStrokeColor;
|
||||
context.stroke();
|
||||
}
|
||||
|
||||
function postCoordinate(containerId) {
|
||||
var xValue = (100 * ((movedX - centerX) / maxMoveStick)).toFixed();
|
||||
var yValue = ((100 * ((movedY - centerY) / maxMoveStick)) * -1).toFixed();
|
||||
|
||||
if(containerId === 'joy1Div') {
|
||||
joyX = xValue;
|
||||
joyY = yValue;
|
||||
// 更新 joy1 的显示信息
|
||||
document.getElementById('joy1XValue').textContent = xValue;
|
||||
document.getElementById('joy1YValue').textContent = yValue;
|
||||
} else if(containerId === 'joy2Div') {
|
||||
// 限定 x 轴 roll 取值范围
|
||||
let rollValue = parseInt((xValue * 30 / 100).toFixed());
|
||||
if (rollValue < -30) {
|
||||
rollValue = -30;
|
||||
} else if (rollValue > 30) {
|
||||
rollValue = 30;
|
||||
}
|
||||
sendAllData();
|
||||
});
|
||||
});
|
||||
g_roll = rollValue;
|
||||
|
||||
// 限定 y 轴 小车高度 取值范围为 15 到 85
|
||||
let heightValue = parseInt(yValue) ;
|
||||
if (heightValue < 0) {
|
||||
heightValue = 0;
|
||||
} else if (heightValue > 60) {
|
||||
heightValue = 60;
|
||||
}
|
||||
g_h = heightValue;
|
||||
|
||||
// 更新 joy2 的显示信息,显示实际限制后的值
|
||||
document.getElementById('joy2XValue').textContent = g_roll;
|
||||
document.getElementById('joy2YValue').textContent = g_h;
|
||||
}
|
||||
|
||||
send_data();
|
||||
}
|
||||
|
||||
function releaseControl() {
|
||||
joyX = 0;
|
||||
joyY = 0;
|
||||
g_roll = 0;
|
||||
g_h = 30;
|
||||
// 重置显示信息
|
||||
document.getElementById('joy1XValue').textContent = '0';
|
||||
document.getElementById('joy1YValue').textContent = '0';
|
||||
document.getElementById('joy2XValue').textContent = '0';
|
||||
document.getElementById('joy2YValue').textContent = g_h;
|
||||
|
||||
send_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc Events for manage touch
|
||||
*/
|
||||
function noTouch(event) {
|
||||
isPressing = 0;
|
||||
isMoving = 0;
|
||||
isRelease = 0;
|
||||
}
|
||||
|
||||
function onTouchStart(event) {
|
||||
isPressing = 1;
|
||||
isMoving = 0;
|
||||
isRelease = 0;
|
||||
}
|
||||
|
||||
function onTouchMove(event) {
|
||||
// Prevent the browser from doing its default thing (scroll, zoom)
|
||||
event.preventDefault();
|
||||
if(isPressing === 1 && event.targetTouches[0].target === canvas) {
|
||||
isMoving = 1;
|
||||
isRelease = 0;
|
||||
|
||||
movedX = event.targetTouches[0].pageX;
|
||||
movedY = event.targetTouches[0].pageY;
|
||||
// Manage offset
|
||||
if(canvas.offsetParent.tagName.toUpperCase() === "BODY") {
|
||||
movedX -= canvas.offsetLeft;
|
||||
movedY -= canvas.offsetTop;
|
||||
} else {
|
||||
movedX -= canvas.offsetParent.offsetLeft;
|
||||
movedY -= canvas.offsetParent.offsetTop;
|
||||
}
|
||||
// Delete canvas
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
// Redraw object
|
||||
drawExternal();
|
||||
drawInternal();
|
||||
|
||||
postCoordinate(canvas.parentNode.id);
|
||||
}
|
||||
}
|
||||
|
||||
function onTouchEnd(event) {
|
||||
//event.preventDefault();
|
||||
isPressing = 0;
|
||||
isMoving = 0;
|
||||
isRelease = 1;
|
||||
|
||||
// If required reset position store variable
|
||||
if(autoReturnToCenter) {
|
||||
movedX = centerX;
|
||||
movedY = centerY;
|
||||
}
|
||||
// Delete canvas
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
// Redraw object
|
||||
drawExternal();
|
||||
drawInternal();
|
||||
|
||||
releaseControl();
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc Events for manage mouse
|
||||
*/
|
||||
function noMouse(event) {
|
||||
isPressing = 0;
|
||||
isMoving = 0;
|
||||
isRelease = 0;
|
||||
}
|
||||
|
||||
function onMouseDown(event) {
|
||||
isPressing = 1;
|
||||
isMoving = 0;
|
||||
isRelease = 0;
|
||||
}
|
||||
|
||||
function onMouseMove(event) {
|
||||
if(isPressing === 1) {
|
||||
isMoving = 1;
|
||||
isRelease = 0;
|
||||
|
||||
movedX = event.pageX;
|
||||
movedY = event.pageY;
|
||||
// Manage offset
|
||||
if(canvas.offsetParent.tagName.toUpperCase() === "BODY") {
|
||||
movedX -= canvas.offsetLeft;
|
||||
movedY -= canvas.offsetTop;
|
||||
} else {
|
||||
movedX -= canvas.offsetParent.offsetLeft;
|
||||
movedY -= canvas.offsetParent.offsetTop;
|
||||
}
|
||||
// Delete canvas
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
// Redraw object
|
||||
drawExternal();
|
||||
drawInternal();
|
||||
|
||||
postCoordinate(canvas.parentNode.id);
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseUp(event) {
|
||||
isPressing = 0;
|
||||
isMoving = 0;
|
||||
isRelease = 1;
|
||||
|
||||
// If required reset position store variable
|
||||
if(autoReturnToCenter) {
|
||||
movedX = centerX;
|
||||
movedY = centerY;
|
||||
}
|
||||
// Delete canvas
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
// Redraw object
|
||||
drawExternal();
|
||||
drawInternal();
|
||||
|
||||
releaseControl();
|
||||
}
|
||||
|
||||
this.GetX = function () {
|
||||
return (100 * ((movedX - centerX) / maxMoveStick)).toFixed();
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Normalizzed value of Y move of stick
|
||||
* @return Integer from -100 to +100
|
||||
*/
|
||||
this.GetY = function () {
|
||||
return ((100 * ((movedY - centerY) / maxMoveStick)) * -1).toFixed();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
var joy1Param = { "title": "1" };
|
||||
var Joy1 = new JoyStick('joy1Div', joy1Param);
|
||||
var joy2Param = { "title": "2" };
|
||||
var Joy2 = new JoyStick('joy2Div', joy2Param);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user