Gyropad

Simulate a gamepad with the device's gyroscope

目前为 2025-02-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Gyropad
  3. // @namespace https://github.com/aortizu/gyropad
  4. // @version 1.0.3
  5. // @description Simulate a gamepad with the device's gyroscope
  6. // @license MIT
  7. // @author Reklaw
  8. // @match https://play.geforcenow.com/*
  9. // @match https://www.xbox.com/*/play*
  10. // @match https://cloud.boosteroid.com/*
  11. // @icon https://icons.iconarchive.com/icons/paomedia/small-n-flat/72/gamepad-icon.png
  12. // @grant none
  13. // ==/UserScript==
  14. (function () {
  15. 'use strict';
  16. const MAX_ANGLE = 45;
  17. const MOVEMENT_THRESHOLD = 0.03;
  18. const DEFAULT_OPACITY = 0.3;
  19. const STICK_RADIUS = 45;
  20. const realGamepads = navigator.getGamepads.bind(navigator);
  21. const clamp = (num, min, max) => Math.max(min, Math.min(num, max));
  22. let enabled = true;
  23. let horizontal = false;
  24. let vertical = false;
  25. let controllerEnable = false;
  26. let showConfigController = false;
  27. let deadzone = 0.1;
  28. let alpha = 0.1;
  29. let smoothedX = 0;
  30. let smoothedY = 0;
  31. let isRight = true;
  32. let leftStickMoved = false;
  33. let rightStickMoved = false;
  34. let trigger = -1;
  35. let posX = 0;
  36. let posY = 0;
  37. let scale = 1;
  38. let elementSelected = null;
  39. let toggleButton = null;
  40. let opacity = DEFAULT_OPACITY;
  41. let simulatedStick = { x1: 0.0, y1: 0.0, x2: 0.0, y2: 0.0 };
  42. let simulatedGamepad = {
  43. id: "Xbox One Game Controller (STANDARD GAMEPAD)",
  44. index: 0,
  45. connected: true,
  46. mapping: "standard",
  47. buttons: Array(16).fill({ pressed: false, touched: false, value: 0 }),
  48. axes: [0.0, 0.0, 0.0, 0.0],
  49. timestamp: 0.0
  50. };
  51. const createButton = (text, styles, eventListeners) => {
  52. const button = document.createElement('button');
  53. button.textContent = text;
  54. Object.assign(button.style, styles);
  55. for (const event in eventListeners) {
  56. button.addEventListener(event, eventListeners[event]);
  57. }
  58. return button;
  59. };
  60.  
  61. function setTrigger(triggerValue) {
  62. trigger = triggerValue;
  63. enabled = trigger < 0;
  64. }
  65.  
  66. navigator.getGamepads = function () {
  67. const gamepads = realGamepads();
  68. if (gamepads[0] && !controllerEnable) {
  69. const gamepad = gamepads[0];
  70. toggleButton.style.background = enabled ? "#00A86B" : "#FF4D4D";
  71. simulatedGamepad.buttons = gamepad.buttons.map(btn => ({ pressed: btn.pressed, value: btn.value }));
  72. simulatedGamepad.axes = [...gamepad.axes];
  73. simulatedGamepad.timestamp = performance.now();
  74. const isUsingRightStick = isRight;
  75. const stickX = gamepad.axes[isUsingRightStick ? 2 : 0];
  76. const stickY = gamepad.axes[isUsingRightStick ? 3 : 1];
  77. const stickMoved = Math.abs(stickX) > MOVEMENT_THRESHOLD || Math.abs(stickY) > MOVEMENT_THRESHOLD;
  78. if (!stickMoved && (enabled || trigger === -1)) {
  79. simulatedGamepad.axes[isUsingRightStick ? 2 : 0] = simulatedStick[isUsingRightStick ? 'x2' : 'x1'];
  80. simulatedGamepad.axes[isUsingRightStick ? 3 : 1] = simulatedStick[isUsingRightStick ? 'y2' : 'y1'];
  81. simulatedGamepad.timestamp = performance.now();
  82. }
  83. } else if (controllerEnable) {
  84. toggleButton.style.background = enabled ? "#00A86B" : "#FF4D4D";
  85. simulatedGamepad.axes = [simulatedStick.x1, simulatedStick.y1, simulatedStick.x2, simulatedStick.y2];
  86. simulatedGamepad.timestamp = performance.now();
  87. }
  88. return [simulatedGamepad];
  89. };
  90.  
  91. function gameLoop() {
  92. const gamepads = navigator.getGamepads();
  93. if (gamepads[0] && trigger !== -1 && gamepads[0].buttons[trigger]) {
  94. const currentPressed = gamepads[0].buttons[trigger].pressed;
  95. if(currentPressed){
  96. enabled = true;
  97. }else{
  98. enabled = false;
  99. }
  100. }
  101. requestAnimationFrame(gameLoop);
  102. }
  103.  
  104. function handleDeviceMotion(event) {
  105. if (!enabled) return;
  106. const smoothFactor = 1.1 - alpha;
  107. let rawX = event.rotationRate.alpha || 0;
  108. let rawY = event.rotationRate.beta || 0;
  109. rawX = Math.abs(rawX) < deadzone ? 0 : rawX;
  110. rawY = Math.abs(rawY) < deadzone ? 0 : rawY;
  111. smoothedX = smoothFactor * rawX + (1 - smoothFactor) * smoothedX;
  112. smoothedY = smoothFactor * rawY + (1 - smoothFactor) * smoothedY;
  113. let normX = clamp(smoothedX / MAX_ANGLE, -1, 1);
  114. let normY = clamp(smoothedY / MAX_ANGLE, -1, 1);
  115. normX = horizontal ? -normX : normX;
  116. normY = vertical ? -normY : normY;
  117. if (isRight) {
  118. simulatedStick.x2 = normX;
  119. simulatedStick.y2 = normY;
  120. } else {
  121. simulatedStick.x1 = normX;
  122. simulatedStick.y1 = normY;
  123. }
  124. }
  125.  
  126. if (DeviceMotionEvent && typeof DeviceMotionEvent.requestPermission === "function") {
  127. DeviceOrientationEvent.requestPermission()
  128. .then(permissionState => {
  129. if (permissionState === "granted") {
  130. window.addEventListener("devicemotion", handleDeviceMotion);
  131. } else {
  132. console.error("Permission denied to access the gyroscope");
  133. }
  134. })
  135. .catch(console.error);
  136. } else {
  137. window.addEventListener("devicemotion", handleDeviceMotion);
  138. }
  139.  
  140. gameLoop();
  141.  
  142. function createUI() {
  143. const screenWidth = window.innerWidth;
  144. const screenHeight = window.innerHeight;
  145. const containerStyles = {
  146. position: "fixed",
  147. top: "10px",
  148. right: "10px",
  149. width: "40%",
  150. padding: "10px",
  151. background: "rgba(0,0,0,0.8)",
  152. color: "white",
  153. borderRadius: "10px",
  154. fontFamily: "Arial, sans-serif",
  155. fontSize: "3vh",
  156. zIndex: "9999",
  157. textAlign: "center",
  158. boxShadow: "0px 0px 10px rgba(255,255,255,0.2)",
  159. display: "none"
  160. };
  161. const elementsStyles = {
  162. position: "relative",
  163. background: "rgba(0,0,0,0)",
  164. color: "white",
  165. fontFamily: "Arial, sans-serif",
  166. fontSize: "3vh",
  167. zIndex: "9999",
  168. textAlign: "center",
  169. maxHeight: "70vh",
  170. overflowY: "auto"
  171. };
  172. const buttonStyles = {
  173. marginTop: "10px",
  174. width: "100%",
  175. background: "#009CDA",
  176. color: "white",
  177. border: "none",
  178. padding: "5px",
  179. cursor: "pointer"
  180. };
  181. const inputStyles = {
  182. width: "100%"
  183. };
  184. const selectStyles = {
  185. width: "100%",
  186. marginTop: "5px",
  187. backgroundColor: "#444",
  188. color: "white"
  189. };
  190.  
  191. let uiContainer = document.createElement("div");
  192. uiContainer.id = "gamepad-ui";
  193. Object.assign(uiContainer.style, containerStyles);
  194.  
  195. let uiElements = document.createElement("div");
  196. uiElements.id = "elements-ui";
  197. Object.assign(uiElements.style, elementsStyles);
  198.  
  199. let closeButton = createButton("❌", {
  200. fontSize: "4vh",
  201. textAlign: "left",
  202. paddingLeft: "10px",
  203. width: "100%",
  204. background: "#00000000",
  205. color: "white",
  206. border: "none",
  207. cursor: "pointer"
  208. }, {
  209. click: () => {
  210. uiContainer.style.display = "none";
  211. }
  212. });
  213.  
  214. let sensitivityLabel = document.createElement("label");
  215. sensitivityLabel.textContent = "Smoth Factor: " + Math.round((alpha * 10));
  216. sensitivityLabel.style.marginTop = "10px";
  217. let sensitivityInput = document.createElement("input");
  218. sensitivityInput.type = "range";
  219. sensitivityInput.min = "0.1";
  220. sensitivityInput.max = "1";
  221. sensitivityInput.step = "0.1";
  222. sensitivityInput.value = alpha;
  223. Object.assign(sensitivityInput.style, inputStyles);
  224. sensitivityInput.oninput = function () {
  225. alpha = parseFloat(this.value);
  226. sensitivityLabel.textContent = "Smoth Factor: " + Math.round(alpha * 10);
  227. };
  228.  
  229. let deadzoneLabel = document.createElement("label");
  230. deadzoneLabel.textContent = "Dead Zone: " + deadzone * 10;
  231. let deadzoneInput = document.createElement("input");
  232. deadzoneInput.type = "range";
  233. deadzoneInput.min = "0.1";
  234. deadzoneInput.max = "1";
  235. deadzoneInput.step = "0.1";
  236. deadzoneInput.value = deadzone;
  237. Object.assign(deadzoneInput.style, inputStyles);
  238. deadzoneInput.oninput = function () {
  239. deadzone = parseFloat(this.value);
  240. deadzoneLabel.textContent = "Dead Zone: " + deadzone * 10;
  241. };
  242.  
  243. let stickButton = createButton(isRight ? "Use Right Stick 🕹️➡️" : "Use Left Stick 🕹️⬅️", buttonStyles, {
  244. click: () => {
  245. isRight = !isRight;
  246. stickButton.textContent = isRight ? "Use Right Stick 🕹️➡️" : "Use Left Stick 🕹️⬅️";
  247. }
  248. });
  249.  
  250. let horizontalButton = createButton(
  251. `Reverse Horizontal: ${horizontal ? "ON 🔃↕️✅" : "OFF 🔃↕️🚫"}`,
  252. { ...buttonStyles, background: horizontal ? "#00A86B" : "#FF4D4D" },
  253. {
  254. click: () => {
  255. horizontal = !horizontal;
  256. horizontalButton.textContent = `Reverse Horizontal: ${horizontal ? "ON 🔃↕️✅" : "OFF 🔃↕️🚫"}`;
  257. horizontalButton.style.background = horizontal ? "#00A86B" : "#FF4D4D";
  258. }
  259. }
  260. );
  261.  
  262. let verticalButton = createButton(
  263. `Reverse Vertical: ${vertical ? "ON 🔃↔️✅" : "OFF 🔃↔️🚫"}`,
  264. { ...buttonStyles, background: vertical ? "#00A86B" : "#FF4D4D", marginBottom: "10px" },
  265. {
  266. click: () => {
  267. vertical = !vertical;
  268. verticalButton.textContent = `Reverse Vertical: ${vertical ? "ON 🔃↔️✅" : "OFF 🔃↔️🚫"}`;
  269. verticalButton.style.background = vertical ? "#00A86B" : "#FF4D4D";
  270. }
  271. }
  272. );
  273.  
  274. let buttonLabel = document.createElement("label");
  275. buttonLabel.textContent = "Trigger Button:";
  276. let triggerSelect = document.createElement("select");
  277. Object.assign(triggerSelect.style, selectStyles);
  278. let buttonNames = [
  279. "🚫-DISABLED-🚫", "A", "B", "X", "Y", "LB", "RB", "LT", "RT", "SELECT", "START", "LS", "RS",
  280. "DPAD UP", "DPAD DOWN", "DPAD LEFT", "DPAD RIGHT",
  281. ];
  282.  
  283. buttonNames.forEach((btnName, index) => {
  284. let option = document.createElement("option");
  285. option.value = index;
  286. option.textContent = btnName;
  287. triggerSelect.appendChild(option);
  288. });
  289.  
  290. triggerSelect.onchange = function () {
  291. let value = parseInt(this.value);
  292. setTrigger(value - 1);
  293. enabled = trigger === -1 ? true : false;
  294. toggleButton.style.background = enabled ? "#00A86B" : "#FF4D4D";
  295. };
  296.  
  297. let toggleEnabledButton = createButton(
  298. enabled ? "Disable Gyroscope 🚫" : "Activate Gyroscope 🔄",
  299. { ...buttonStyles, background: enabled ? "#FF4D4D" : "#00A86B", marginBottom: "10px" },
  300. {
  301. click: () => {
  302. enabled = !enabled;
  303. toggleEnabledButton.textContent = enabled ? "Disable Gyroscope 🚫" : "Activate Gyroscope 🔄";
  304. toggleEnabledButton.style.background = enabled ? "#FF4D4D" : "#00A86B";
  305. sensitivityLabel.style.display = enabled ? "block" : "none";
  306. sensitivityInput.style.display = enabled ? "block" : "none";
  307. deadzoneLabel.style.display = enabled ? "block" : "none";
  308. deadzoneInput.style.display = enabled ? "block" : "none";
  309. stickButton.style.display = enabled ? "block" : "none";
  310. horizontalButton.style.display = enabled ? "block" : "none";
  311. verticalButton.style.display = enabled ? "block" : "none";
  312. buttonLabel.style.display = enabled ? "block" : "none";
  313. triggerSelect.style.display = enabled ? "block" : "none";
  314. }
  315. }
  316. );
  317.  
  318. let enableController = createButton(
  319. controllerEnable ? "Disable Virtual Controller 🎮" : "Activate Virtual Controller 🎮",
  320. { ...buttonStyles, background: controllerEnable ? "#00A86B" : "#FF4D4D" },
  321. {
  322. click: () => {
  323. if (screen.orientation.type.includes('landscape')) {
  324. let gamepads = realGamepads();
  325. if (gamepads[0]) {
  326. showToast("There Is A Real Gamepad Connected 🎮 So Virtual Gamepad is Not Available 🚫");
  327. } else {
  328. controllerEnable = !controllerEnable;
  329. if (controllerEnable) {
  330. showControls();
  331. } else {
  332. hideControls();
  333. }
  334. }
  335. } else {
  336. showToast("Only In Landscape Orientation");
  337. }
  338. }
  339. }
  340. );
  341.  
  342. // configController Button to toggle "enabled"
  343. let configController = createButton(
  344. showConfigController ? "Close Controller Config ⚙️" : "Open Controller Config ⚙️",
  345. { ...buttonStyles, background: "#009CDA", display: "none" },
  346. {
  347. click: () => {
  348. showConfigController = !showConfigController;
  349. configController.textContent = showConfigController ? "Close Controller Config ⚙️" : "Open Controller Config ⚙️";
  350. uiControllerContainer.style.display = showConfigController ? "block" : "none";
  351. }
  352. }
  353. );
  354.  
  355. const createVirtualButton = (text, color, bottom, right, eventListeners) => {
  356. const button = createButton(text, {
  357. background: "#444",
  358. opacity: DEFAULT_OPACITY,
  359. position: "fixed",
  360. width: "10vh",
  361. height: "10vh",
  362. borderRadius: "50%",
  363. border: "none",
  364. color: color,
  365. fontSize: "3vh",
  366. fontFamily: "Arial, sans-serif",
  367. userSelect: "none",
  368. display: "none",
  369. bottom: bottom,
  370. right: right,
  371. textAlign: "center",
  372. zIndex: "9000",
  373. }, eventListeners);
  374.  
  375. const touchStartHandler = (e) => {
  376. e.preventDefault();
  377. button.style.filter = "brightness(150%)";
  378. button.style.transform = "scale(0.95)";
  379. button.style.transition = "all 0.1s";
  380. const buttonIndex = getButtonIndex(text);
  381. simulatedGamepad.buttons[buttonIndex] = { pressed: true, touched: true, value: 1 };
  382. simulatedGamepad.timestamp = performance.now();
  383. if (trigger === buttonIndex) {
  384. enabled = true;
  385. }
  386. };
  387.  
  388. const touchEndHandler = (e) => {
  389. e.preventDefault();
  390. button.style.filter = "brightness(100%)";
  391. button.style.transform = "scale(1)";
  392. const buttonIndex = getButtonIndex(text);
  393. simulatedGamepad.buttons[buttonIndex] = { pressed: false, touched: false, value: 0 };
  394. simulatedGamepad.timestamp = performance.now();
  395. if (trigger === buttonIndex) {
  396. enabled = false;
  397. }
  398. };
  399.  
  400. button.addEventListener('touchstart', touchStartHandler);
  401. button.addEventListener('touchend', touchEndHandler);
  402.  
  403. return button;
  404. };
  405.  
  406. const getButtonIndex = (buttonText) => {
  407. switch (buttonText) {
  408. case "A": return 0;
  409. case "B": return 1;
  410. case "X": return 2;
  411. case "Y": return 3;
  412. case "LB": return 4;
  413. case "RB": return 5;
  414. case "LT": return 6;
  415. case "RT": return 7;
  416. case "SELECT": return 8;
  417. case "START": return 9;
  418. case "L3": return 10;
  419. case "R3": return 11;
  420. case "↑": return 12;
  421. case "↓": return 13;
  422. case "←": return 14;
  423. case "→": return 15;
  424. default: return -1;
  425. }
  426. };
  427.  
  428. let buttonA = createVirtualButton("A", "#0F0", "25vh", "20vh");
  429. let buttonB = createVirtualButton("B", "#F00", "35vh", "10vh");
  430. let buttonX = createVirtualButton("X", "#00F", "35vh", "30vh");
  431. let buttonY = createVirtualButton("Y", "#FF0", "45vh", "20vh");
  432. let down = createVirtualButton("↓", "#FFF", "25vh", null, null);
  433. down.style.left = "20vh";
  434. down.style.borderRadius = "10%";
  435. let right = createVirtualButton("→", "#FFF", "35vh", null, null);
  436. right.style.left = "30vh";
  437. right.style.borderRadius = "10%";
  438. let left = createVirtualButton("←", "#FFF", "35vh", null, null);
  439. left.style.left = "10vh";
  440. left.style.borderRadius = "10%";
  441. let up = createVirtualButton("↑", "#FFF", "45vh", null, null);
  442. up.style.left = "20vh";
  443. up.style.borderRadius = "10%";
  444. let lt = createVirtualButton("LT", "#FFF", "89vh", null, null);
  445. lt.style.width = "15vw";
  446. lt.style.left = "3vw";
  447. lt.style.borderRadius = "10%";
  448. let rt = createVirtualButton("RT", "#FFF", "89vh", null, null);
  449. rt.style.width = "15vw";
  450. rt.style.right = "3vw";
  451. rt.style.borderRadius = "10%";
  452. let lb = createVirtualButton("LB", "#FFF", "75vh", null, null);
  453. lb.style.width = "15vw";
  454. lb.style.left = "3vw";
  455. lb.style.borderRadius = "10%";
  456. let rb = createVirtualButton("RB", "#FFF", "75vh", null, null);
  457. rb.style.width = "15vw";
  458. rb.style.right = "3vw";
  459. rb.style.borderRadius = "10%";
  460. let start = createVirtualButton("START", "#FFF", "8vh", null, null);
  461. start.style.width = "8vw";
  462. start.style.right = "40vw";
  463. start.style.height = "4vh";
  464. start.style.borderRadius = "10%";
  465. let select = createVirtualButton("SELECT", "#FFF", "8vh", null, null);
  466. select.style.width = "8vw";
  467. select.style.left = "40vw";
  468. select.style.height = "4vh";
  469. select.style.borderRadius = "10%";
  470. let l3 = createVirtualButton("L3", "#FFF", "8vh", null, null);
  471. l3.style.left = "10vh";
  472. l3.style.borderRadius = "10%";
  473. let r3 = createVirtualButton("R3", "#FFF", "8vh", null, null);
  474. r3.style.right = "10vh";
  475. r3.style.borderRadius = "10%";
  476.  
  477. const createStickContainer = (isLeft) => {
  478. const container = document.createElement('div');
  479. container.style.position = "fixed";
  480. container.style.width = "12vw";
  481. container.style.height = "12vw";
  482. container.style.background = "#444";
  483. container.style.opacity = DEFAULT_OPACITY;
  484. container.style.borderRadius = "50%";
  485. container.style.bottom = "8vh";
  486. container.style[isLeft ? 'left' : 'right'] = "20vw";
  487. container.style.display = "none";
  488. container.style.zIndex = "9000";
  489. const stick = document.createElement('div');
  490. stick.style.position = "absolute";
  491. stick.style.width = "50%";
  492. stick.style.height = "50%";
  493. stick.style.background = "#fff";
  494. stick.style.opacity = DEFAULT_OPACITY;
  495. stick.style.borderRadius = "50%";
  496. stick.style.display = "flex";
  497. stick.style.justifyContent = "center";
  498. stick.style.alignItems = "center";
  499. stick.style.left = "25%";
  500. stick.style.top = "25%";
  501. stick.style.transition = "transform 0.1s";
  502. stick.style.touchAction = "none";
  503. let activeTouch = null;
  504. let stickMoved = false;
  505. container.addEventListener('touchstart', (e) => {
  506. e.preventDefault();
  507. for (let touch of e.touches) {
  508. if (container.contains(touch.target)) {
  509. activeTouch = touch;
  510. break;
  511. }
  512. }
  513. stickMoved = true;
  514. container.style.filter = "brightness(150%)";
  515. container.style.transform = "scale(0.95)";
  516. container.style.transition = "all 0.1s";
  517. });
  518. container.addEventListener('touchmove', (e) => {
  519. if (!activeTouch) return;
  520. stick.style.transition = "none";
  521. const containerRect = container.getBoundingClientRect();
  522. const centerX = containerRect.width / 2;
  523. const centerY = containerRect.height / 2;
  524. const touch = Array.from(e.touches).find(t => t.identifier === activeTouch.identifier);
  525.  
  526. if (touch) {
  527. const dx = touch.clientX - containerRect.left - centerX;
  528. const dy = touch.clientY - containerRect.top - centerY;
  529.  
  530. let normX = dx / STICK_RADIUS;
  531. let normY = dy / STICK_RADIUS;
  532.  
  533. const magnitude = Math.hypot(normX, normY);
  534. if (magnitude > 1) {
  535. normX /= magnitude;
  536. normY /= magnitude;
  537. }
  538.  
  539. const realDistance = Math.hypot(dx, dy);
  540. const clampedDistance = Math.min(realDistance, STICK_RADIUS);
  541. const angle = Math.atan2(dy, dx);
  542. const dispX = Math.cos(angle) * clampedDistance;
  543. const dispY = Math.sin(angle) * clampedDistance;
  544.  
  545. stick.style.transform = `translate(${dispX}px, ${dispY}px)`;
  546. simulatedStick[isLeft ? 'x1' : 'x2'] = normX;
  547. simulatedStick[isLeft ? 'y1' : 'y2'] = normY;
  548. simulatedGamepad.timestamp = performance.now();
  549. }
  550. });
  551. container.addEventListener('touchend', (e) => {
  552. activeTouch = null;
  553. stick.style.transition = "all 0.1s";
  554. stick.style.transform = 'translate(0, 0)';
  555. container.style.filter = "brightness(100%)";
  556. container.style.transform = "scale(1)";
  557. simulatedStick[isLeft ? 'x1' : 'x2'] = 0;
  558. simulatedStick[isLeft ? 'y1' : 'y2'] = 0;
  559. simulatedGamepad.timestamp = performance.now();
  560. stickMoved = false;
  561. });
  562. container.appendChild(stick);
  563. return container;
  564. };
  565.  
  566. let stickRightContainer = createStickContainer(false);
  567. let stickLeftContainer = createStickContainer(true);
  568.  
  569. let uiControllerContainer = document.createElement("div");
  570. uiControllerContainer.id = "controls-ui";
  571. Object.assign(uiControllerContainer.style, {
  572. position: "fixed",
  573. top: "10px",
  574. left: "10px",
  575. width: "30%",
  576. padding: "10px",
  577. background: "rgba(0,0,0,0.8)",
  578. color: "white",
  579. borderRadius: "10px",
  580. fontFamily: "Arial, sans-serif",
  581. fontSize: "3vh",
  582. zIndex: "9999",
  583. textAlign: "center",
  584. boxShadow: "0px 0px 10px rgba(255,255,255,0.2)",
  585. display: "none",
  586. maxHeight: "80vh",
  587. overflowY: "auto"
  588. });
  589.  
  590. let closeControllerButton = createButton("❌", {
  591. fontSize: "4vh",
  592. textAlign: "right",
  593. paddingRight: "10px",
  594. width: "100%",
  595. background: "#0000",
  596. color: "white",
  597. border: "none",
  598. cursor: "pointer"
  599. }, {
  600. click: () => {
  601. showConfigController = !showConfigController;
  602. positionXLabel.style.display = "none";
  603. positionYLabel.style.display = "none";
  604. positionXInput.style.display = "none";
  605. positionYInput.style.display = "none";
  606. sizeLabel.style.display = "none";
  607. sizeInput.style.display = "none";
  608. if (elementSelected) {
  609. elementSelected.style.border = "none";
  610. elementSelected.style.opacity = opacity;
  611. elementSelected = null;
  612. }
  613. uiControllerContainer.style.display = "none";
  614. }
  615. });
  616.  
  617. let opacityLabel = document.createElement("label");
  618. opacityLabel.textContent = "Opacity: " + Math.round((opacity * 100));
  619. let opacityInput = document.createElement("input");
  620. opacityInput.type = "range";
  621. opacityInput.min = "0";
  622. opacityInput.max = "1";
  623. opacityInput.step = "0.01";
  624. opacityInput.value = opacity;
  625. Object.assign(opacityInput.style, inputStyles);
  626. opacityInput.oninput = function () {
  627. opacity = parseFloat(this.value);
  628. updateOpacity();
  629. opacityLabel.textContent = "Opacity: " + Math.round(opacity * 100);
  630. };
  631.  
  632. let elementLabel = document.createElement("label");
  633. elementLabel.textContent = "Select Element To Modify:";
  634. let elementSelect = document.createElement("select");
  635. Object.assign(elementSelect.style, selectStyles);
  636. let elementsNames = [
  637. "🚫-NONE-🚫", "A", "B", "X", "Y", "LB", "RB", "LT", "RT", "SELECT", "START", "LS", "RS",
  638. "DPAD UP", "DPAD DOWN", "DPAD LEFT", "DPAD RIGHT", "RIGHT STICK", "LEFT STICK"
  639. ];
  640. elementsNames.forEach((btnName, index) => {
  641. let option = document.createElement("option");
  642. option.value = index;
  643. option.textContent = btnName;
  644. elementSelect.appendChild(option);
  645. });
  646. elementSelect.onchange = function () {
  647. let value = parseInt(this.value);
  648. let index = value - 1;
  649. if (index == -1) {
  650. positionXLabel.style.display = "none";
  651. positionYLabel.style.display = "none";
  652. positionXInput.style.display = "none";
  653. positionYInput.style.display = "none";
  654. sizeLabel.style.display = "none";
  655. sizeInput.style.display = "none";
  656. if (elementSelected) {
  657. elementSelected.style.border = "none";
  658. elementSelected.style.opacity = opacity;
  659. elementSelected = null;
  660. }
  661. } else {
  662. uiContainer.style.display = "none";
  663. elementSelected = selectElement(index);
  664. elementSelected.style.border = "3px solid red";
  665. elementSelected.style.opacity = 1;
  666. posX = Math.round(elementSelected.getBoundingClientRect().x);
  667. posY = Math.round(elementSelected.getBoundingClientRect().y);
  668. positionXInput.value = posX;
  669. positionYInput.value = posY;
  670. positionXLabel.textContent = "Position In X: " + posX;
  671. positionYLabel.textContent = "Position In Y: " + posY;
  672. positionXLabel.style.display = "block";
  673. positionYLabel.style.display = "block";
  674. positionXInput.style.display = "block";
  675. positionYInput.style.display = "block";
  676. sizeLabel.style.display = "block";
  677. sizeInput.style.display = "block";
  678. }
  679. };
  680.  
  681. let positionXLabel = document.createElement("label");
  682. positionXLabel.textContent = "Position In X: " + posX;
  683. positionXLabel.style.marginTop = "10px";
  684. positionXLabel.style.display = "none";
  685. let positionXInput = document.createElement("input");
  686. positionXInput.type = "range";
  687. positionXInput.min = "0";
  688. positionXInput.max = screenWidth;
  689. positionXInput.step = "1";
  690. positionXInput.value = posX;
  691. positionXInput.style.display = "none";
  692. positionXInput.style.width = "100%";
  693. positionXInput.oninput = function () {
  694. posX = parseFloat(this.value);
  695. elementSelected.style.removeProperty("left");
  696. elementSelected.style.removeProperty("right");
  697. elementSelected.style.left = `${posX}px`;
  698. positionXLabel.textContent = "Position In X: " + posX;
  699. };
  700.  
  701. let positionYLabel = document.createElement("label");
  702. positionYLabel.textContent = "Position In Y: " + posY;
  703. positionYLabel.style.marginTop = "10px";
  704. positionYLabel.style.display = "none";
  705. let positionYInput = document.createElement("input");
  706. positionYInput.type = "range";
  707. positionYInput.min = "0";
  708. positionYInput.max = screenHeight;
  709. positionYInput.step = "1";
  710. positionYInput.value = posY;
  711. positionYInput.style.display = "none";
  712. positionYInput.style.width = "100%";
  713. positionYInput.oninput = function () {
  714. posY = parseFloat(this.value);
  715. elementSelected.style.removeProperty("top");
  716. elementSelected.style.removeProperty("down");
  717. elementSelected.style.top = `${posY}px`;
  718. positionYLabel.textContent = "Position In Y: " + posY;
  719. };
  720.  
  721. let sizeLabel = document.createElement("label");
  722. sizeLabel.textContent = "Scale: " + scale;
  723. sizeLabel.style.marginTop = "10px";
  724. sizeLabel.style.display = "none";
  725. let sizeInput = document.createElement("input");
  726. sizeInput.type = "range";
  727. sizeInput.min = "0.5";
  728. sizeInput.max = "3";
  729. sizeInput.step = "0.1";
  730. sizeInput.value = scale;
  731. sizeInput.style.display = "none";
  732. sizeInput.style.width = "100%";
  733. sizeInput.oninput = function () {
  734. scale = parseFloat(this.value);
  735. elementSelected.style.removeProperty("top");
  736. elementSelected.style.removeProperty("down");
  737. elementSelected.style.transform = `scale(${scale})`;
  738. sizeLabel.textContent = "Scale: " + scale;
  739. };
  740.  
  741. toggleButton = document.createElement("button");
  742. toggleButton.textContent = "✜";
  743. toggleButton.style.position = "fixed";
  744. toggleButton.style.fontSize = "5vh";
  745. toggleButton.style.textAlign = "center";
  746. toggleButton.style.fontFamily = "Arial, sans-serif";
  747. toggleButton.style.top = "10%";
  748. toggleButton.style.right = "0vw";
  749. toggleButton.style.background = enabled ? "#00A86B" : "#FF4D4D";
  750. toggleButton.style.opacity = 0.5;
  751. toggleButton.style.color = "#FFF8";
  752. toggleButton.style.border = "none";
  753. toggleButton.style.borderRadius = "50%";
  754. toggleButton.style.width = "8vh";
  755. toggleButton.style.height = "8vh";
  756. toggleButton.style.zIndex = "10000";
  757.  
  758. function showControls() {
  759. controllerEnable = true;
  760. enableController.textContent = controllerEnable ? "Disable Virtual Controller 🎮" : "Activate Virtual Controller 🎮";
  761. enableController.style.background = controllerEnable ? "#00A86B" : "#FF4D4D";
  762. configController.style.display = "block";
  763. buttonA.style.display = "block";
  764. buttonB.style.display = "block";
  765. buttonX.style.display = "block";
  766. buttonY.style.display = "block";
  767. up.style.display = "block";
  768. down.style.display = "block";
  769. left.style.display = "block";
  770. right.style.display = "block";
  771. lt.style.display = "block";
  772. rt.style.display = "block";
  773. lb.style.display = "block";
  774. rb.style.display = "block";
  775. l3.style.display = "block";
  776. r3.style.display = "block";
  777. select.style.display = "block";
  778. start.style.display = "block";
  779. stickLeftContainer.style.display = "block";
  780. stickRightContainer.style.display = "block";
  781. }
  782.  
  783. function hideControls() {
  784. controllerEnable = false;
  785. enableController.textContent = controllerEnable ? "Disable Virtual Controller 🎮" : "Activate Virtual Controller 🎮";
  786. enableController.style.background = controllerEnable ? "#00A86B" : "#FF4D4D";
  787. configController.style.display = "none";
  788. uiControllerContainer.style.display = "none";
  789. buttonA.style.display = "none";
  790. buttonB.style.display = "none";
  791. buttonX.style.display = "none";
  792. buttonY.style.display = "none";
  793. up.style.display = "none";
  794. down.style.display = "none";
  795. left.style.display = "none";
  796. right.style.display = "none";
  797. lt.style.display = "none";
  798. rt.style.display = "none";
  799. lb.style.display = "none";
  800. rb.style.display = "none";
  801. l3.style.display = "none";
  802. r3.style.display = "none";
  803. select.style.display = "none";
  804. start.style.display = "none";
  805. stickLeftContainer.style.display = "none";
  806. stickRightContainer.style.display = "none";
  807. }
  808.  
  809. function updateOpacity() {
  810. buttonA.style.opacity = opacity;
  811. buttonB.style.opacity = opacity;
  812. buttonX.style.opacity = opacity;
  813. buttonY.style.opacity = opacity;
  814. up.style.opacity = opacity;
  815. down.style.opacity = opacity;
  816. left.style.opacity = opacity;
  817. right.style.opacity = opacity;
  818. lt.style.opacity = opacity;
  819. rt.style.opacity = opacity;
  820. lb.style.opacity = opacity;
  821. rb.style.opacity = opacity;
  822. l3.style.opacity = opacity;
  823. r3.style.opacity = opacity;
  824. select.style.opacity = opacity;
  825. start.style.opacity = opacity;
  826. stickLeftContainer.style.opacity = opacity;
  827. stickRightContainer.style.opacity = opacity;
  828. toggleButton.style.opacity = opacity;
  829. }
  830.  
  831. function selectElement(element) {
  832. switch (element) {
  833. case 0:
  834. return buttonA;
  835. case 1:
  836. return buttonB;
  837. case 2:
  838. return buttonX;
  839. case 3:
  840. return buttonY;
  841. case 4:
  842. return lb;
  843. case 5:
  844. return rb;
  845. case 6:
  846. return lt;
  847. case 7:
  848. return rt;
  849. case 8:
  850. return select;
  851. case 9:
  852. return start;
  853. case 10:
  854. return l3;
  855. case 11:
  856. return r3;
  857. case 12:
  858. return up;
  859. case 13:
  860. return down;
  861. case 14:
  862. return left;
  863. case 15:
  864. return right;
  865. case 16:
  866. return stickRightContainer;
  867. case 17:
  868. return stickLeftContainer;
  869. }
  870. }
  871.  
  872. function showToast(message) {
  873. const toast = document.createElement("div");
  874. toast.textContent = message;
  875. toast.style.position = "fixed";
  876. toast.style.textAlign = "center";
  877. toast.style.bottom = "20px";
  878. toast.style.left = "50%";
  879. toast.style.transform = "translateX(-50%)";
  880. toast.style.background = "rgba(0, 0, 0, 0.8)";
  881. toast.style.color = "white";
  882. toast.style.padding = "12px 20px";
  883. toast.style.borderRadius = "8px";
  884. toast.style.fontSize = "3vh";
  885. toast.style.boxShadow = "0px 4px 10px rgba(0, 0, 0, 0.3)";
  886. toast.style.zIndex = "9999";
  887. toast.style.opacity = "0";
  888. toast.style.transition = "opacity 0.5s ease-in-out";
  889. document.body.appendChild(toast);
  890. setTimeout(() => {
  891. toast.style.opacity = "1";
  892. }, 100);
  893. setTimeout(() => {
  894. toast.style.opacity = "0";
  895. setTimeout(() => {
  896. toast.remove();
  897. }, 500);
  898. }, 5000);
  899.  
  900. }
  901.  
  902. screen.orientation.addEventListener("change", function () {
  903. if (screen.orientation.type.includes('portrait')) {
  904. hideControls();
  905. }
  906. });
  907.  
  908. toggleButton.ontouchstart = function (event) {
  909. event.preventDefault();
  910. let touch = event.touches[0];
  911. let rect = toggleButton.getBoundingClientRect();
  912. let shiftX = touch.clientX - rect.left;
  913. let shiftY = touch.clientY - rect.top;
  914. let startX = touch.clientX;
  915. let startY = touch.clientY;
  916. let moved = false;
  917. function moveAt(clientX, clientY) {
  918. toggleButton.style.left = clientX - shiftX + "px";
  919. toggleButton.style.top = clientY - shiftY + "px";
  920. }
  921. function onTouchMove(event) {
  922. let currentTouch = event.touches[0];
  923. moveAt(currentTouch.clientX, currentTouch.clientY);
  924. if (Math.abs(currentTouch.clientX - startX) > 30 ||
  925. Math.abs(currentTouch.clientY - startY) > 30) {
  926. moved = true;
  927. }
  928. }
  929. function onTouchEnd() {
  930. document.removeEventListener("touchmove", onTouchMove);
  931. document.removeEventListener("touchend", onTouchEnd);
  932. if (!moved) {
  933. uiContainer.style.display = "block";
  934. }
  935. }
  936. document.addEventListener("touchmove", onTouchMove);
  937. document.addEventListener("touchend", onTouchEnd);
  938. };
  939.  
  940. document.addEventListener("fullscreenchange", () => {
  941. let fullscreenElement = document.getElementById("fullscreen-container") ? document.getElementById("fullscreen-container") : document.getElementById("StreamHud");
  942. if (fullscreenElement) {
  943. fullscreenElement.appendChild(toggleButton);
  944. fullscreenElement.appendChild(uiContainer);
  945. fullscreenElement.appendChild(buttonA);
  946. fullscreenElement.appendChild(buttonB);
  947. fullscreenElement.appendChild(buttonX);
  948. fullscreenElement.appendChild(buttonY);
  949. fullscreenElement.appendChild(up);
  950. fullscreenElement.appendChild(down);
  951. fullscreenElement.appendChild(right);
  952. fullscreenElement.appendChild(left);
  953. fullscreenElement.appendChild(lt);
  954. fullscreenElement.appendChild(rt);
  955. fullscreenElement.appendChild(lb);
  956. fullscreenElement.appendChild(rb);
  957. fullscreenElement.appendChild(l3);
  958. fullscreenElement.appendChild(r3);
  959. fullscreenElement.appendChild(select);
  960. fullscreenElement.appendChild(start);
  961. fullscreenElement.appendChild(stickLeftContainer);
  962. fullscreenElement.appendChild(stickRightContainer);
  963. fullscreenElement.appendChild(uiControllerContainer);
  964. }
  965. });
  966.  
  967. uiContainer.appendChild(closeButton);
  968. uiContainer.appendChild(uiElements);
  969. uiElements.appendChild(enableController);
  970. uiElements.appendChild(configController);
  971. uiElements.appendChild(toggleEnabledButton);
  972. uiElements.appendChild(sensitivityLabel);
  973. uiElements.appendChild(sensitivityInput);
  974. uiElements.appendChild(deadzoneLabel);
  975. uiElements.appendChild(deadzoneInput);
  976. uiElements.appendChild(stickButton);
  977. uiElements.appendChild(horizontalButton);
  978. uiElements.appendChild(verticalButton);
  979. uiElements.appendChild(buttonLabel);
  980. uiElements.appendChild(triggerSelect);
  981. uiControllerContainer.appendChild(closeControllerButton);
  982. uiControllerContainer.appendChild(opacityLabel);
  983. uiControllerContainer.appendChild(opacityInput);
  984. uiControllerContainer.appendChild(elementLabel);
  985. uiControllerContainer.appendChild(elementSelect);
  986. uiControllerContainer.appendChild(positionXLabel);
  987. uiControllerContainer.appendChild(positionXInput);
  988. uiControllerContainer.appendChild(positionYLabel);
  989. uiControllerContainer.appendChild(positionYInput);
  990. uiControllerContainer.appendChild(sizeLabel);
  991. uiControllerContainer.appendChild(sizeInput);
  992. document.body.appendChild(buttonA);
  993. document.body.appendChild(buttonB);
  994. document.body.appendChild(buttonX);
  995. document.body.appendChild(buttonY);
  996. document.body.appendChild(up);
  997. document.body.appendChild(down);
  998. document.body.appendChild(right);
  999. document.body.appendChild(left);
  1000. document.body.appendChild(lt);
  1001. document.body.appendChild(rt);
  1002. document.body.appendChild(lb);
  1003. document.body.appendChild(rb);
  1004. document.body.appendChild(l3);
  1005. document.body.appendChild(r3);
  1006. document.body.appendChild(select);
  1007. document.body.appendChild(start);
  1008. document.body.appendChild(stickLeftContainer);
  1009. document.body.appendChild(stickRightContainer);
  1010. document.body.appendChild(toggleButton);
  1011. document.body.appendChild(uiContainer);
  1012. document.body.appendChild(uiControllerContainer);
  1013. }
  1014. createUI();
  1015. })();