Skribbl Line Tool

Hold shift to enable line drawing, press Space to snap to right angles

  1. // ==UserScript==
  2. // @name Skribbl Line Tool
  3. // @namespace https://greasyfork.org/users/281093
  4. // @match *://skribbl.io/*
  5. // @grant none
  6. // @version 1.1
  7. // @license MIT
  8. // @copyright 2020, Faux (https://greasyfork.org/users/281093)
  9. // @author Ghibli
  10. // @description Hold shift to enable line drawing, press Space to snap to right angles
  11. // ==/UserScript==
  12. /* jshint esversion: 6 */
  13.  
  14. const canvas = document.querySelector('#canvasGame');
  15. const lineCanvas = document.createElement('canvas');
  16. const lineCtx = lineCanvas.getContext('2d');
  17.  
  18. lineCanvas.oncontextmenu = () => { return false; };
  19. [lineCanvas.width, lineCanvas.height] = [canvas.width, canvas.height];
  20. canvas.parentElement.insertBefore(lineCanvas, canvas);
  21.  
  22. lineCanvas.setAttribute('style', `
  23. position: absolute;
  24. cursor: crosshair;
  25. width: 100%;
  26. user-select: none;
  27. z-index: 2;
  28. filter: opacity(0.1);
  29. display: none;`
  30. );
  31.  
  32. lineCanvas.clear = () => {
  33. lineCtx.clearRect(0, 0, lineCanvas.width, lineCanvas.height);
  34. };
  35.  
  36. let origin = {
  37. preview: {},
  38. real: {}
  39. };
  40.  
  41. const pos = {
  42. preview: {},
  43. real: {}
  44. };
  45.  
  46. let canvasHidden = true;
  47. let drawingLine = false;
  48. let snap = false;
  49.  
  50. document.addEventListener('keydown', (e) => {
  51. if (!isDrawing()) return;
  52. if (e.code === 'ShiftLeft' && canvasHidden) {
  53. lineCanvas.style.display = '';
  54. disableScroll();
  55. canvasHidden = false;
  56. }
  57. else if (e.code === 'Space' && !snap && !canvasHidden) {
  58. snap = true;
  59. document.dispatchEvent(createMouseEvent('pointermove', pos.real));
  60. }
  61. });
  62.  
  63. document.addEventListener('keyup', (e) => {
  64. if (e.code === 'ShiftLeft' && !canvasHidden) {
  65. hideLineCanvas();
  66. document.removeEventListener('pointermove', savePos);
  67. document.removeEventListener('pointerup', mouseUpDraw);
  68. }
  69. else if (e.code === 'Space' && snap) {
  70. snap = false;
  71. document.dispatchEvent(createMouseEvent('pointermove', pos.real));
  72. }
  73. });
  74.  
  75. function hideLineCanvas() {
  76. lineCanvas.style.display = 'none';
  77. canvasHidden = true;
  78. enableScroll();
  79. resetLineCanvas();
  80. }
  81.  
  82. function savePos(e) {
  83. e.preventDefault();
  84. pos.preview = getPos(e);
  85. pos.real = getRealPos(e);
  86.  
  87. if (canvasHidden || !drawingLine) return;
  88. lineCanvas.clear();
  89. drawPreviewLine(pos.preview);
  90. }
  91.  
  92. lineCanvas.addEventListener('pointerdown', (e) => {
  93. if (!e.shiftKey) hideLineCanvas();
  94. origin = getPos(e);
  95. origin.real = getRealPos(e);
  96. drawingLine = true;
  97. document.addEventListener('pointerup', mouseUpDraw);
  98. document.addEventListener('pointermove', savePos);
  99. });
  100.  
  101. function mouseUpDraw(e) {
  102. document.removeEventListener('pointermove', savePos);
  103. document.removeEventListener('pointerup', mouseUpDraw);
  104. drawLine(origin.real.x, origin.real.y, pos.real.x, pos.real.y);
  105. pos.preview = getPos(e);
  106. pos.real = getRealPos(e);
  107. resetLineCanvas();
  108. }
  109.  
  110. function resetLineCanvas() {
  111. drawingLine = false;
  112. lineCanvas.clear();
  113. }
  114.  
  115. function getPos(event) {
  116. const canvasRect = canvas.getBoundingClientRect();
  117. const canvasScale = canvas.width / canvasRect.width;
  118. return {
  119. x: (event.clientX - canvasRect.left) * canvasScale,
  120. y: (event.clientY - canvasRect.top) * canvasScale
  121. };
  122. }
  123.  
  124. function getRealPos(event) {
  125. return {
  126. x: event.clientX,
  127. y: event.clientY
  128. };
  129. }
  130.  
  131. function drawPreviewLine(coords) {
  132. lineCtx.beginPath();
  133. lineCtx.moveTo(origin.x, origin.y);
  134.  
  135. if (snap) {
  136. if (Math.abs(coords.x - origin.x) < Math.abs(coords.y - origin.y)) {
  137. lineCtx.lineTo(origin.x, coords.y);
  138. }
  139. else {
  140. lineCtx.lineTo(coords.x, origin.y);
  141. }
  142. }
  143. else {
  144. lineCtx.lineTo(coords.x, coords.y);
  145. }
  146. // lineCtx.globalAlpha = 0.2;
  147. lineCtx.lineWidth = 3;
  148. lineCtx.stroke();
  149. }
  150.  
  151. function drawLine(x1, y1, x2, y2) {
  152. const coords = { x: x1, y: y1 };
  153. const newCoords = { x: x2, y: y2 };
  154.  
  155. if (snap) {
  156. if (Math.abs(x2 - x1) < Math.abs(y2 - y1)) {
  157. newCoords.x = x1;
  158. }
  159. else {
  160. newCoords.y = y1;
  161. }
  162. }
  163.  
  164. canvas.dispatchEvent(createMouseEvent('mousedown', coords, true));
  165. canvas.dispatchEvent(createMouseEvent('mousemove', newCoords, true));
  166. canvas.dispatchEvent(createMouseEvent('mouseup', newCoords, true));
  167. }
  168.  
  169. function createMouseEvent(name, coords, bubbles = false) {
  170. return new MouseEvent(name, {
  171. bubbles: bubbles,
  172. clientX: coords.x,
  173. clientY: coords.y,
  174. button: 0
  175. });
  176. }
  177.  
  178. const keys = { 32: 1, 37: 1, 38: 1, 39: 1, 40: 1 };
  179.  
  180. function preventDefault(e) {
  181. e.preventDefault();
  182. }
  183.  
  184. function preventDefaultForScrollKeys(e) {
  185. if (keys[e.keyCode]) {
  186. preventDefault(e);
  187. return false;
  188. }
  189. }
  190.  
  191. function isDrawing() {
  192. return document.querySelector('.containerTools').offsetParent !== null;
  193. }
  194.  
  195. let supportsPassive = false;
  196. try {
  197. window.addEventListener('test', null, Object.defineProperty({}, 'passive', {
  198. get: function() {
  199. supportsPassive = true;
  200. return true;
  201. }
  202. }));
  203. }
  204. catch(e) {
  205. console.log(e);
  206. }
  207.  
  208. const wheelOpt = supportsPassive ? { passive: false } : false;
  209. const wheelEvent = 'onwheel' in document.createElement('div') ? 'wheel' : 'mousewheel';
  210.  
  211. function disableScroll() {
  212. window.addEventListener('DOMMouseScroll', preventDefault, false);
  213. window.addEventListener(wheelEvent, preventDefault, wheelOpt);
  214. window.addEventListener('touchmove', preventDefault, wheelOpt);
  215. window.addEventListener('keydown', preventDefaultForScrollKeys, false);
  216. }
  217.  
  218. function enableScroll() {
  219. window.removeEventListener('DOMMouseScroll', preventDefault, false);
  220. window.removeEventListener(wheelEvent, preventDefault, wheelOpt);
  221. window.removeEventListener('touchmove', preventDefault, wheelOpt);
  222. window.removeEventListener('keydown', preventDefaultForScrollKeys, false);
  223. }
  224.  
  225. window.addEventListener('blur', hideLineCanvas);