Add touch controls for camera manipulation in mujoco_server.py
Browse files- Implemented touch event listeners for camera rotation and pinch-to-zoom functionality.
- Enhanced user interaction by allowing single touch for rotation and dual touch for zooming.
- Added logic to handle touch movements and reset states for improved user experience.
- mujoco_server.py +53 -0
mujoco_server.py
CHANGED
|
@@ -1219,6 +1219,7 @@ def index():
|
|
| 1219 |
|
| 1220 |
viewport.oncontextmenu = (e) => e.preventDefault();
|
| 1221 |
|
|
|
|
| 1222 |
viewport.onmousedown = (e) => {
|
| 1223 |
isDragging = true;
|
| 1224 |
lastX = e.clientX;
|
|
@@ -1244,6 +1245,58 @@ def index():
|
|
| 1244 |
send('camera', {action: 'zoom', dz: e.deltaY});
|
| 1245 |
};
|
| 1246 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1247 |
// Connect on load
|
| 1248 |
connect();
|
| 1249 |
</script>
|
|
|
|
| 1219 |
|
| 1220 |
viewport.oncontextmenu = (e) => e.preventDefault();
|
| 1221 |
|
| 1222 |
+
// Mouse controls
|
| 1223 |
viewport.onmousedown = (e) => {
|
| 1224 |
isDragging = true;
|
| 1225 |
lastX = e.clientX;
|
|
|
|
| 1245 |
send('camera', {action: 'zoom', dz: e.deltaY});
|
| 1246 |
};
|
| 1247 |
|
| 1248 |
+
// Touch controls for camera rotation and pinch-to-zoom
|
| 1249 |
+
let touchStartX, touchStartY;
|
| 1250 |
+
let lastPinchDist = null;
|
| 1251 |
+
|
| 1252 |
+
function getTouchDistance(touches) {
|
| 1253 |
+
const dx = touches[0].clientX - touches[1].clientX;
|
| 1254 |
+
const dy = touches[0].clientY - touches[1].clientY;
|
| 1255 |
+
return Math.sqrt(dx * dx + dy * dy);
|
| 1256 |
+
}
|
| 1257 |
+
|
| 1258 |
+
viewport.addEventListener('touchstart', (e) => {
|
| 1259 |
+
if (e.touches.length === 1) {
|
| 1260 |
+
// Single touch - rotation
|
| 1261 |
+
touchStartX = e.touches[0].clientX;
|
| 1262 |
+
touchStartY = e.touches[0].clientY;
|
| 1263 |
+
lastPinchDist = null;
|
| 1264 |
+
} else if (e.touches.length === 2) {
|
| 1265 |
+
// Two touches - pinch zoom
|
| 1266 |
+
lastPinchDist = getTouchDistance(e.touches);
|
| 1267 |
+
}
|
| 1268 |
+
}, {passive: true});
|
| 1269 |
+
|
| 1270 |
+
viewport.addEventListener('touchmove', (e) => {
|
| 1271 |
+
e.preventDefault();
|
| 1272 |
+
if (e.touches.length === 1 && lastPinchDist === null) {
|
| 1273 |
+
// Single touch drag - rotate camera
|
| 1274 |
+
const dx = e.touches[0].clientX - touchStartX;
|
| 1275 |
+
const dy = e.touches[0].clientY - touchStartY;
|
| 1276 |
+
touchStartX = e.touches[0].clientX;
|
| 1277 |
+
touchStartY = e.touches[0].clientY;
|
| 1278 |
+
send('camera', {action: 'rotate', dx, dy});
|
| 1279 |
+
} else if (e.touches.length === 2 && lastPinchDist !== null) {
|
| 1280 |
+
// Pinch zoom
|
| 1281 |
+
const dist = getTouchDistance(e.touches);
|
| 1282 |
+
const delta = lastPinchDist - dist;
|
| 1283 |
+
lastPinchDist = dist;
|
| 1284 |
+
// Scale delta for smoother zoom
|
| 1285 |
+
send('camera', {action: 'zoom', dz: delta * 3});
|
| 1286 |
+
}
|
| 1287 |
+
}, {passive: false});
|
| 1288 |
+
|
| 1289 |
+
viewport.addEventListener('touchend', (e) => {
|
| 1290 |
+
if (e.touches.length < 2) {
|
| 1291 |
+
lastPinchDist = null;
|
| 1292 |
+
}
|
| 1293 |
+
if (e.touches.length === 1) {
|
| 1294 |
+
// Reset for single finger after pinch
|
| 1295 |
+
touchStartX = e.touches[0].clientX;
|
| 1296 |
+
touchStartY = e.touches[0].clientY;
|
| 1297 |
+
}
|
| 1298 |
+
}, {passive: true});
|
| 1299 |
+
|
| 1300 |
// Connect on load
|
| 1301 |
connect();
|
| 1302 |
</script>
|