Leap Motion Controller Support

Control web page with your Leap Motion Controller

  1. // ==UserScript==
  2. // @name Leap Motion Controller Support
  3. // @namespace https://levelkro.com
  4. // @version 0.46.1
  5. // @description Control web page with your Leap Motion Controller
  6. // @author levelKro (https://levelkro.com)
  7. // @include http://*/*
  8. // @include https://*/*
  9. // @exclude *github.*
  10. // @exclude *yahoo.*
  11. // @exclude *asus.*
  12. // @noframes
  13. // @run-at document-idle
  14. // @grant window.close
  15. // @grant window.focus
  16. // @connect self
  17. // @connect localhost
  18. // @connect 127.0.0.1
  19. // @license MIT
  20. // @copyright 2018, levelKro (https://levelkro.com) (https://openuserjs.org/users/levelKro)
  21. // @license MIT
  22. // ==/UserScript==
  23.  
  24. /*
  25.  
  26. Leap Motion Controller Support
  27.  
  28. Source from;
  29. - JSonViewer in Leap Motion SDK 4.0.0
  30.  
  31. Modified by;
  32. - Mathieu <levelKro> Légaré <levelkro@yahoo.ca>
  33.  
  34. Excluded sites
  35. - Please read https://www.reddit.com/r/mturk/comments/ao3mkf/limited_runtime_host_permissions_might_break_some/
  36.  
  37. Know issues
  38. - GitHub and Yahoo have a content policy and block script.
  39. - Tou.tv scrolling freeze
  40.  
  41. Uses
  42. - Hand grab
  43. - Move : Scrolling page (open hand and close it for reset center of move)
  44. - Open (5 fingers) : reset hold time
  45. - 2,3 or 4 fingers (open hand and keep fingers you want open for enable center of move)
  46. - move left : back in history
  47. - move right : next in history
  48. - move up : go to top of page
  49. - move down : refresh page
  50. - move up-left :
  51. - move up-right :
  52. - move down-left :
  53. - move down-right :
  54.  
  55. Version History
  56. 0.46
  57. - Added 4 actions (up+left/up+right/down+left/down+right)
  58. - Minors design fix
  59. - Fix hands reports
  60. - Clean codes
  61. - First real stable release
  62.  
  63. */
  64. // Customs variables
  65. var LMDebug=false;// Enable debug messages (warning; slow motion)
  66. var LMScrollY=5;// Scroll jump effect (horizontal)
  67. var LMScrollX=1;// Scroll jump effect (vertical)
  68. var LMHold=150;// Holding time in ms for repeat/other command
  69. // Static variables, don't touch
  70. var handCount=0;
  71. var handLeft=false;
  72. var handRight=false;
  73. var handLeftGrab=false;
  74. var handRightGrab=false;
  75. var handLeftHeight=0;
  76. var handRightHeight=0;
  77. var handLeftWidth=0;
  78. var handRightWidth=0;
  79. var scrollStartY=0;
  80. var scrollStartX=0;
  81. var handLeftStartY=0;
  82. var handRightStartY=0;
  83. var handLeftStartX=0;
  84. var handRightStartX=0;
  85. var fingersCount=0;
  86. var fingersInc=0;
  87. var fingersStartX=0;
  88. var fingersStartY=0;
  89. var fingersHeight=0;
  90. var fingersWidth=0;
  91. var fingersWait=LMHold;
  92. var LMws;
  93. var focusListener;
  94. var blurListener;
  95. var LMtimeout;
  96. var drawUse='<div class="LMindicator Use">&nsbp;</div>';
  97. var drawOk='<div class="LMindicator Ok">&nsbp;</div>';
  98. var drawWarn='<div class="LMindicator Warn">&nsbp;</div>';
  99. var drawError='<div class="LMindicator Error">&nsbp;</div>';
  100. var toInput = '<style>';
  101. toInput+=' .LMindicator {line-height:0 !important;font-size:1px !important;border-radius:4px !important;border:1px solid #666 !important;overflow:hidden !important;width:8px !important;height:8px !important;display:inline-block !important;}';
  102. toInput+=' .LMindicator.Ok {background-color:#6ce20b !important;} .LMindicator.Warn {background-color:#eded10 !important;} .LMindicator.Use {background-color:#24a2e5 !important;} .LMindicator.Error {background-color:#ef1c46 !important;}';
  103. toInput+=' .LMwidget {cursor:not-allowed !important;position:fixed !important;top:0 !important;left:0 !important;border:none !important;border-radius:0 0 5px 0 !important; padding:3px 5px !important; float:left !important; display:inline-block !important; z-index:9999 !important; background-color:#333 !important;box-shadow:1px 1px 15px #333 !important; font-size:12px !important; font-family:Arial !important; color:#eee !important; line-height:0.8 !important;}';
  104. toInput+=' .LMhand {font-size:12px !important; font-family:Arial !important; color:#ddd !important;} .LMhand sup {font-size:8px !important;vertical-align: super !important;position:inherit !important;}';
  105. toInput+=' .LMlogo {background-color:#333 !important;font-family:Arial !important; color:#eee !important;} .LMlogo span {color:#d8ea64 !important;}';
  106. toInput+=' .LMsep {color:#666 !important;text-shadow:1px 1px 15px #333 !important;line-height: 0 !important;font-size: 13px !important;margin: -2px 0 !important;padding: 0 !important;} .LMdebug {font-size:12px !important;color:#222 !important; display:block !important; min-width:250px; min-height:300px; max-height:650px; padding:5px !important; background-color:#fff !important; border:1px solid #000;overflow:auto;}';
  107. toInput+='</style>';
  108. toInput+='<div class="LMwidget" title="Leap Motion Controller : Activities monitor">';
  109. toInput+='<span class="LMlogo">Leap <span>Motion</span> </span><span id="LMState">'+drawWarn+'</span> ';
  110. toInput+='<b class="LMsep"> | </b>';
  111. toInput+='<span class="LMhand"> left <span id="LMStateLeft">'+drawError+'</span></span>';
  112. toInput+='<span class="LMhand"> right <span id="LMStateRight">'+drawError+'</span></span>';
  113. toInput+='<span class="LMhand"> fingers <span id="LMStateFingers">'+drawError+'<sup>0</sup></span></span>';
  114. if(LMDebug) toInput+='<div id="LMMain" style="visibility:hidden;">Output:<div id="LMJSon" class="LMdebug"></div></div>';
  115. toInput+='</div>';
  116. // Support both the WebSocket and MozWebSocket objects
  117. if ((typeof(WebSocket) == 'undefined') &&
  118. (typeof(MozWebSocket) != 'undefined')) {
  119. WebSocket = MozWebSocket;
  120. }
  121. (function(){startLM();})(); // Run baby, RUN!
  122. function startLM(){
  123. console.log("[LM] Leap Motion Controller Support loaded");
  124. if(window.self !== window.top){
  125. // Is framed, or not at the top, stop script
  126. if(LMDebug) console.log("[LM] Frame detected, stop execution.");
  127. LMws.close();
  128. LMws = null;
  129. if(LMDebug) console.log("[LM] Turn off plugin");
  130. window.removeEventListener("focus", focusListener);
  131. window.removeEventListener("blur", blurListener);
  132. return;
  133. }
  134. else{
  135. console.log("[LM] Starting ...");
  136. var newHTML = document.createElement ('div');
  137. newHTML.innerHTML = toInput;
  138. document.body.appendChild (newHTML);
  139. document.getElementById("LMStateLeft").innerHTML=drawError;
  140. document.getElementById("LMStateRight").innerHTML=drawError;
  141. document.getElementById("LMStateFingers").innerHTML=drawError+"<sup>0</sup>";
  142. // Create the socket with event handlers
  143. // Create and open the socket
  144. if(LMDebug) console.log("[LM] Connecting to Leap Motion Web Socket Service");
  145. LMws = new WebSocket("ws://localhost:6437/v7.json");
  146. // On successful connection
  147. LMws.onopen = function(event) {
  148. document.getElementById("LMState").innerHTML=drawWarn;
  149. LMws.send(JSON.stringify({focused: true})); // claim focus
  150. focusListener = window.addEventListener('focus', function(e) {
  151. LMws.send(JSON.stringify({focused: true})); // claim focus
  152. });
  153. blurListener = window.addEventListener('blur', function(e) {
  154. LMws.send(JSON.stringify({focused: false})); // relinquish focus
  155. });
  156. if(LMDebug) document.getElementById("LMMain").style.visibility = "visible";
  157. if(LMDebug) console.log("[LM] Connected to Leap Motion Web Socket Service");
  158. console.log("[LM] Connected to device");
  159. };
  160. // On message received
  161. LMws.onmessage = function(event) {
  162. var obj = JSON.parse(event.data);
  163. if(LMDebug) var str = JSON.stringify(obj, undefined, 2);
  164. if(obj.hasOwnProperty("hands")){
  165. // Hands count detection
  166. if (obj["hands"].length==2){
  167. if(handCount!=2) {
  168. document.getElementById("LMStateLeft").innerHTML=drawOk;
  169. document.getElementById("LMStateRight").innerHTML=drawOk;
  170. document.getElementById("LMStateFingers").innerHTML=drawWarn+"<sup>"+fingersCount+"</sup>";
  171. handCount=2;
  172. }
  173. } else if (obj["hands"].length==1){
  174. if(handCount!=1) {
  175. if(handLeft) document.getElementById("LMStateLeft").innerHTML=drawOk;
  176. else document.getElementById("LMStateLeft").innerHTML=drawError;
  177. if(handRight) document.getElementById("LMStateRight").innerHTML=drawOk;
  178. else document.getElementById("LMStateRight").innerHTML=drawError;
  179. document.getElementById("LMStateFingers").innerHTML=drawWarn+"<sup>"+fingersCount+"</sup>";
  180. handCount=1;
  181. }
  182. }
  183. else {
  184. if(handCount!=0) {
  185. handCount=0;
  186. fingersCount=0;
  187. handLeft=false;
  188. handRight=false;
  189. document.getElementById("LMStateLeft").innerHTML=drawError;
  190. document.getElementById("LMStateRight").innerHTML=drawError;
  191. document.getElementById("LMStateFingers").innerHTML=drawError+"<sup>0</sup>";
  192. }
  193. }
  194.  
  195. // Hands Grips detection
  196. if(handCount>=1) {
  197. handLeft=false;
  198. handRight=false;
  199. obj["hands"].forEach(function(hand) {
  200. if(hand["type"]=="left") {
  201. document.getElementById("LMStateLeft").innerHTML=drawOk;
  202. handLeft=true;
  203. if(hand["grabStrength"]>=0.8 && !handLeftGrab) {
  204. handLeftGrab=true;
  205. handLeftStartY=hand["palmPosition"][1];
  206. handLeftStartX=hand["palmPosition"][0];
  207. scrollStartY=window.scrollY;
  208. scrollStartX=window.scrollX;
  209. }
  210. else if(hand["grabStrength"]<=0.8 && handLeftGrab) {
  211. handLeftGrab=false;
  212. }
  213. handLeftHeight=hand["palmPosition"][1];
  214. handLeftWidth=hand["palmPosition"][0];
  215. }
  216. else if(hand["type"]=="right") {
  217. document.getElementById("LMStateRight").innerHTML=drawOk;
  218. handRight=true;
  219. if(hand["grabStrength"]>=0.8 && !handRightGrab) {
  220. handRightGrab=true;
  221. handRightStartY=hand["palmPosition"][1];
  222. handRightStartX=hand["palmPosition"][0];
  223. scrollStartY=window.scrollY;
  224. scrollStartX=window.scrollX;
  225. }
  226. else if(hand["grabStrength"]<=0.8 && handRightGrab) {
  227. handRightGrab=false;
  228. }
  229. handRightHeight=hand["palmPosition"][1];
  230. handRightWidth=hand["palmPosition"][0];
  231. }
  232. });
  233. fingersInc=0;
  234. obj["pointables"].forEach(function(finger) {
  235. if(finger['extended']==true) fingersInc++;
  236. });
  237. var finger = obj["pointables"][0];
  238. if(fingersCount!=fingersInc) {
  239. fingersCount=fingersInc;
  240. fingersStartX=finger["dipPosition"][0];
  241. fingersStartY=finger["dipPosition"][1];
  242. if(fingersInc==5) {
  243. fingersWait=LMHold;
  244. document.getElementById("LMStateFingers").innerHTML=drawUse+"<sup>"+fingersInc+"</sup>";
  245. }
  246. }
  247. fingersWidth=finger["dipPosition"][0];
  248. fingersHeight=finger["dipPosition"][1];
  249.  
  250. }
  251. else {
  252. handLeftGrab=false;
  253. handRightGrab=false;
  254. handLeft=false;
  255. handRight=false;
  256. fingersCount=0;
  257. }
  258. if (!document.hidden) {
  259. // Hands actions
  260. if(handCount==2){
  261. }
  262. else if(handLeft){
  263. // Left hand actions
  264. if(handLeftGrab){
  265. // Scrolling page
  266. var LYscroll=Math.floor(-1*LMScrollY)*Math.floor(handLeftStartY - handLeftHeight);
  267. var LYnewScroll=Math.floor(scrollStartY + LYscroll);
  268. var LXscroll=LMScrollX*Math.floor(handLeftStartX - handLeftWidth);
  269. var LXnewScroll=Math.floor(scrollStartX + LXscroll);
  270. if(LXnewScroll<=0) LXnewScroll=0;
  271. if(LYnewScroll<=0) LYnewScroll=0;
  272. window.scrollTo(LXnewScroll, LYnewScroll);
  273. document.getElementById("LMStateLeft").innerHTML=drawUse;
  274. }
  275. }
  276. else if(handRight){
  277. // Right hand actions
  278. if(handRightGrab){
  279. // Scrolling page
  280. var RYscroll=Math.floor(-1*LMScrollY)*Math.floor(handRightStartY - handRightHeight);
  281. var RYnewScroll=Math.floor(scrollStartY + RYscroll);
  282. var RXscroll=LMScrollX*Math.floor(handRightStartX - handRightWidth);
  283. var RXnewScroll=Math.floor(scrollStartX + RXscroll);
  284. if(RXnewScroll<=0) RXnewScroll=0;
  285. if(RYnewScroll<=0) RYnewScroll=0;
  286. window.scrollTo(RXnewScroll, RYnewScroll);
  287. document.getElementById("LMStateRight").innerHTML=drawUse;
  288. }
  289. }
  290. if(fingersCount>=1){
  291. // Fingers actions
  292. document.getElementById("LMStateFingers").innerHTML=drawOk+"<sup>"+fingersCount+"</sup>";
  293. if(fingersCount<=4 && fingersCount>=2){
  294. fingersWait--; // Security wait for "flood"
  295. // Action by move position
  296. document.getElementById("LMStateFingers").innerHTML=drawUse+"<sup>"+fingersCount+"</sup>";
  297. if(fingersWait<=0){
  298. fingersWait=1;
  299. var FYmove=Math.floor(fingersStartY - fingersHeight);
  300. var FXmove=Math.floor(fingersStartX - fingersWidth);
  301. if(FYmove<=-100 && FXmove>=50) {
  302. return; // Up+Left :
  303. }
  304. else if(FYmove<=-100 && FXmove<=-50) {
  305. fingersWait=LMHold;
  306. return; // Up+Right :
  307. }
  308. else if(FYmove>=100 && FXmove>=50) {
  309. fingersWait=LMHold;
  310. return; // Down+Left :
  311. }
  312. else if(FYmove>=100 && FXmove<=-50) {
  313. fingersWait=LMHold;
  314. return; //Down+Right :
  315. }
  316. else if(FYmove<=-100) {
  317. window.scrollTo(0, 0); // Up : Go to top
  318. fingersWait=LMHold;
  319. }
  320. else if(FYmove>=100) {
  321. window.location.reload(); // Down : Refresh action
  322. fingersWait=LMHold;
  323. }
  324. else if(FXmove>=50) {
  325. window.history.go(-1); // Left : Back
  326. fingersWait=LMHold;
  327. }
  328. else if(FXmove<=-50) {
  329. window.history.go(+1); // Right: Next
  330. fingersWait=LMHold;
  331. }
  332. }
  333. }
  334. }
  335. else {
  336. // No fingers found
  337. document.getElementById("LMStateFingers").innerHTML=drawError+"<sup>0</sup>";
  338. fingersWait=LMHold;
  339. fingersCount=0;
  340. }
  341. }
  342. }
  343. if(obj.hasOwnProperty("timestamp")){
  344. if(handCount>=1) document.getElementById("LMState").innerHTML=drawUse;
  345. else if(obj.hasOwnProperty("hands")) document.getElementById("LMState").innerHTML=drawOk;
  346. else document.getElementById("LMState").innerHTML=drawWarn;
  347. if(LMDebug) document.getElementById("LMJSon").innerHTML = '<pre>' + str + '</pre>';
  348. }
  349. else if(obj.hasOwnProperty("serviceVersion")){
  350. document.getElementById("LMState").innerHTML=drawOk;
  351. if(LMDebug) console.log("[LM] Using Leap Motion software version : "+obj["serviceVersion"]+" with Web Socket Service version : "+obj["version"]);
  352. }
  353. else if(obj.hasOwnProperty("event")){
  354. document.getElementById("LMState").innerHTML=drawOk;
  355. if(!obj['event']['state']['streaming']) {
  356. document.getElementById("LMState").innerHTML=drawWarn;
  357. console.log("[LM] Device on pause, enable the device for use it");
  358. }
  359. }
  360. else{
  361. if(LMDebug) console.log("[LM] Bad answer from Leap Motion Web Socket Service: "+str);
  362. document.getElementById("LMState").innerHTML=drawError;
  363. }
  364. // Catch the pause of device
  365. window.clearTimeout(LMtimeout);
  366. LMtimeout=window.setTimeout(function(){document.getElementById("LMState").innerHTML=drawWarn;console.log("[LM] Device on pause, enable the device for use it");}, 100);
  367. };
  368. // On socket close
  369. LMws.onclose = function(event) {
  370. LMws = null;
  371. window.removeEventListener("focus", focusListener);
  372. window.removeEventListener("blur", blurListener);
  373. if(LMDebug) document.getElementById("LMMain").style.visibility = "hidden";
  374. document.getElementById("LMState").innerHTML=drawError;
  375. if(LMDebug) console.log("[LM] Deconnected from Leap Motion Web Socket Service");
  376. }
  377. // On socket error
  378. LMws.onerror = function(event) {
  379. var obj = JSON.parse(event.data);
  380. var str = JSON.stringify(obj, undefined, 2);
  381. document.getElementById("LMState").innerHTML=drawWarn;
  382. document.getElementById("LMStateLeft").innerHTML=drawError;
  383. document.getElementById("LMStateRight").innerHTML=drawError;
  384. document.getElementById("LMStateFingers").innerHTML=drawError+"<sup>0</sup>";
  385. console.log("[LM] Error: "+str);
  386. };
  387. }
  388. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址