Dark Mode+

Sketchful dark theme improvements

当前为 2020-07-31 提交的版本,查看 最新版本

  1. /* eslint-disable no-undef */
  2. // ==UserScript==
  3. // @name Dark Mode+
  4. // @match https://sketchful.io/
  5. // @grant none
  6. // @version 1.3
  7. // @description Sketchful dark theme improvements
  8. // @author bebell
  9. // @run-at document-end
  10. // jshint esversion: 6
  11. // @namespace https://greasyfork.org/users/281093
  12. // ==/UserScript==
  13.  
  14. let grayCanvas = localStorage.grayCanvas
  15. ? JSON.parse(localStorage.grayCanvas)
  16. : true;
  17. const pageHTML = document.querySelector('html');
  18. const canvas = document.querySelector('#canvas');
  19. const ctx = canvas.getContext('2d');
  20. const themes = document.querySelector('#menuSettingsTheme');
  21. const settingsContainer = document.querySelector(
  22. '#menuSettings > div.row.justify-content-center > div'
  23. );
  24. const styleRules = [
  25. '.dark .table { color: white !important; }',
  26. '.table { color: black !important; }',
  27. '.dark { background-color: #1d1f22 !important; }',
  28. '.dark .table th, html.dark .table thead, html.dark .table td { border-color: #454d55 !important; }',
  29. '.table th, .table thead, .table td { border-color: #dee2e6 !important; }',
  30. '.dark .table.table-striped tbody tr:nth-of-type(odd) { background-color: rgba(255,255,255,.05)!important; }',
  31. '.table.table-striped tbody tr:nth-of-type(odd) { background-color: rgba(0,0,0,.05)!important; }',
  32. '.dark #gameChatList li:not(.chatAdmin) b { color: #BBB }',
  33. '#gameChatList li:not(.chatAdmin) b { color: black }',
  34. '.dark .gameAvatarRank { color: #148FA2 }',
  35. '.dark #gameWinners li b { color: white !important }',
  36. '.dark .gameSticky { color: white !important }',
  37. '#gameSticky font { color: black!important }',
  38. '.dark #gameSticky font { color: #eee!important }'
  39. ];
  40.  
  41. const sheet =
  42. window.document.styleSheets[window.document.styleSheets.length - 1];
  43. styleRules.forEach((rule) => sheet.insertRule(rule));
  44.  
  45. let btn;
  46. const stored = localStorage.dark;
  47. let darkMode = stored
  48. ? JSON.parse(stored)
  49. : JSON.parse(localStorage.getItem('settings')).dark;
  50.  
  51. if (darkMode) pageHTML.classList.add('dark');
  52. else pageHTML.classList.remove('dark');
  53.  
  54. canvas.style.filter = darkMode
  55. ? `brightness(${localStorage.canvasBrightness})` || ''
  56. : '';
  57.  
  58. themes.onchange = (e) => {
  59. if (e.target.value === 'Dark' && !grayCanvas) {
  60. pageHTML.classList.add('dark');
  61. e.stopImmediatePropagation();
  62. }
  63. };
  64.  
  65. document.querySelector('#menu > div.menuNav > ul > li:nth-child(5) > a').onclick = () => {
  66. themes.value = darkMode ? 'Dark' : 'Light';
  67. };
  68.  
  69. function darkClassObserver(mutations) {
  70. for (const mutation of mutations) {
  71. if (mutation.attributeName !== 'class') return;
  72. darkMode = pageHTML.classList.contains('dark');
  73. localStorage.dark = darkMode;
  74. canvas.style.filter = darkMode
  75. ? `brightness(${localStorage.canvasBrightness})` || ''
  76. : '';
  77. fixColors();
  78. grayCanvas && toggleCanvasDarkMode();
  79. }
  80. }
  81.  
  82. const gameObserver = new MutationObserver(darkClassObserver);
  83.  
  84. gameObserver.observe(pageHTML, { attributes: true });
  85.  
  86. (function addBrightnessSlider() {
  87. const sliderContainer = document
  88. .querySelector('#menuSettingsVolumeIcon')
  89. .parentNode.cloneNode(true);
  90. sliderContainer.getElementsByTagName('h5')[0].textContent =
  91. 'Canvas Brightness';
  92. const icon = sliderContainer.firstChild;
  93. icon.remove();
  94. const newIcon = document.createElement('img');
  95. newIcon.setAttribute('style', 'width: 64px; height: 64px');
  96. newIcon.setAttribute('class', 'mr-3 lazy');
  97. newIcon.src = 'https://i.imgur.com/GkVJWun.gif';
  98. const slider = sliderContainer.querySelector('#menuSettingsVolume');
  99. slider.value = localStorage.canvasBrightness || 1;
  100. slider.onchange = changeBrightness;
  101. sliderContainer.insertBefore(newIcon, sliderContainer.firstChild);
  102.  
  103. const grayCanvasToggle = document.createElement('div');
  104. grayCanvasToggle.setAttribute(
  105. 'style',
  106. 'display: flex; justify-content: space-between'
  107. );
  108. const label = document.createElement('label');
  109.  
  110. label.style.fontSize = '20px';
  111. label.textContent = 'Gray Canvas';
  112.  
  113. const btnSwitch = document.createElement('label');
  114. const input = document.createElement('input');
  115. const span = document.createElement('span');
  116. span.setAttribute('class', 'slider round');
  117. input.type = 'checkbox';
  118. input.checked = grayCanvas;
  119. input.onchange = () => {
  120. grayCanvas = input.checked;
  121. localStorage.grayCanvas = grayCanvas;
  122. if (!grayCanvas && darkMode) {
  123. themes[0].selected = true;
  124. themes.dispatchEvent(new Event('change'));
  125. pageHTML.classList.add('dark');
  126. themes.value = darkMode ? 'Dark' : 'Light';
  127. }
  128. };
  129. btnSwitch.setAttribute('class', 'switch');
  130. btnSwitch.append(input, span);
  131. grayCanvasToggle.append(label, btnSwitch);
  132. settingsContainer.append(sliderContainer, grayCanvasToggle);
  133. })();
  134.  
  135. (function toggleButtons() {
  136. btn = document.querySelector('#saveButton').cloneNode();
  137. btn.style.right = '100px';
  138. btn.setAttribute('data-original-title', 'Toggle Theme');
  139. btn.title = 'Toggle Theme';
  140. updateToggleStyle();
  141. btn.onclick = toggleDarkMode;
  142.  
  143. document.querySelector('#gameInterface').append(btn);
  144. })();
  145.  
  146. function changeBrightness() {
  147. canvas.style.filter = `brightness(${this.value})`;
  148. localStorage.canvasBrightness = this.value;
  149. }
  150.  
  151. function updateToggleStyle() {
  152. btn.style.backgroundImage = darkMode ? 'url(https://i.imgur.com/imL02Mq.gif)' :
  153. 'url(https://i.imgur.com/2ZdZ9oh.gif)';
  154. }
  155.  
  156. function toggleDarkMode() {
  157. if (!grayCanvas) {
  158. pageHTML.classList.toggle('dark');
  159. }
  160. else {
  161. themes[darkMode ? 0 : 1].selected = true;
  162. themes.dispatchEvent(new Event('change'));
  163. }
  164. }
  165.  
  166. function toggleCanvasDarkMode() {
  167. const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  168. const data = imgData.data;
  169.  
  170. for (let i = 0; i < data.length; i += 4) {
  171. const [r, g, b] = [data[i], data[i + 1], data[i + 2]];
  172.  
  173. if (r === 255 && g === 255 && b === 255 && darkMode) {
  174. const color = 68;
  175. data[i] = color;
  176. data[i + 1] = color;
  177. data[i + 2] = color;
  178. }
  179. else if (r === 68 && g === 68 && b === 68 && !darkMode) {
  180. const color = 255;
  181. data[i] = color;
  182. data[i + 1] = color;
  183. data[i + 2] = color;
  184. }
  185. }
  186.  
  187. ctx.putImageData(imgData, 0, 0);
  188. }
  189.  
  190. function fixColors() {
  191. let names = document.getElementsByClassName('gameAvatarName');
  192. updateToggleStyle();
  193. if (!names.length) {names = document.getElementsByClassName('gameSettingsAvatarName');}
  194. for (const name of names) {
  195. if (darkMode && name.style.color === 'black') {
  196. name.style.color = '#ccc';
  197. }
  198. else if (!darkMode && name.style.color === 'rgb(204, 204, 204)') {
  199. name.style.color = 'black';
  200. }
  201. }
  202. }
  203.  
  204. const colorObserver = new MutationObserver(() => {
  205. fixColors();
  206. });
  207.  
  208. colorObserver.observe(document.querySelector('#gamePlayersList'), {
  209. childList: true
  210. });