护眼模式

简单有效的全网通用护眼模式、夜间模式、暗黑模式

当前为 2021-07-20 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name 护眼模式
  3. // @version 1.2.0
  4. // @author X.I.U
  5. // @description 简单有效的全网通用护眼模式、夜间模式、暗黑模式
  6. // @match *://*/*
  7. // @exclude *://v.qq.com/*
  8. // @exclude *://*.iqiyi.com/*
  9. // @exclude *://*.youku.com/*
  10. // @exclude *://*.mgtv.com/*
  11. // @exclude *://tv.cctv.com/*
  12. // @icon https://i.loli.net/2021/03/07/rdijeYm83pznxWq.png
  13. // @grant GM_registerMenuCommand
  14. // @grant GM_unregisterMenuCommand
  15. // @grant GM_getValue
  16. // @grant GM_setValue
  17. // @grant GM_notification
  18. // @noframes
  19. // @license GPL-3.0 License
  20. // @run-at document-start
  21. // @namespace https://github.com/XIU2/UserScript
  22. // @supportURL https://github.com/XIU2/UserScript
  23. // @homepageURL https://github.com/XIU2/UserScript
  24. // ==/UserScript==
  25.  
  26. 'use strict';
  27. (function() {
  28. var menu_ALL = [
  29. ['menu_disable', '✅ 已启用 (点击对当前网站禁用)', '❎ 已禁用 (点击对当前网站启用)', []],
  30. ['menu_runDuringTheDay', '白天保持开启 (比晚上亮一点点)', '白天保持开启', true],
  31. ['menu_autoRecognition', '智能排除自带暗黑模式的网页 (beta)', '智能排除自带暗黑模式的网页 (beta)', true],
  32. ['menu_forcedToEnable', '✅ 已强制当前网站启用护眼模式 (👆)', '❎ 未强制当前网站启用护眼模式 (👆)', []],
  33. ['menu_darkModeType', '点击切换模式', '点击切换模式', 1],
  34. ['menu_customMode', '自定义当前模式', '自定义当前模式', true], ['menu_customMode1',,,'80|70'], ['menu_customMode2',,,'80|20|70|30'], ['menu_customMode3',,,'80']
  35. ], menu_ID = [];
  36. for (let i=0;i<menu_ALL.length;i++){ // 如果读取到的值为 null 就写入默认值
  37. if (GM_getValue(menu_ALL[i][0]) == null){GM_setValue(menu_ALL[i][0], menu_ALL[i][3])};
  38. }
  39. registerMenuCommand();
  40. if (menu_ID.length > 1) {addStyle();}
  41.  
  42.  
  43. // 注册脚本菜单
  44. function registerMenuCommand() {
  45. if (menu_ID.length > menu_ALL.length){ // 如果菜单ID数组多于菜单数组,说明不是首次添加菜单,需要卸载所有脚本菜单
  46. for (let i=0;i<menu_ID.length;i++){
  47. GM_unregisterMenuCommand(menu_ID[i]);
  48. }
  49. }
  50. for (let i=0;i<menu_ALL.length;i++){ // 循环注册脚本菜单
  51. menu_ALL[i][3] = GM_getValue(menu_ALL[i][0]);
  52. if (menu_ALL[i][0] === 'menu_disable')
  53. { // 启用/禁用护眼模式 (当前网站)
  54. if (menu_disable('check')) { // 当前网站是否已存在禁用列表中
  55. menu_ID[i] = GM_registerMenuCommand(`${menu_ALL[i][2]}`, function(){menu_disable('del')});
  56. return
  57. } else {
  58. menu_ID[i] = GM_registerMenuCommand(`${menu_ALL[i][1]}`, function(){menu_disable('add')});
  59. }
  60. }
  61. else if (menu_ALL[i][0] === 'menu_darkModeType')
  62. { // 点击切换模式
  63. if (menu_ALL[i][3] > 3) { // 避免在减少 raw 数组后,用户储存的数据大于数组而报错
  64. menu_ALL[i][3] = 1;
  65. GM_setValue(menu_ALL[i][0], menu_ALL[i][3]);
  66. }
  67. menu_ID[i] = GM_registerMenuCommand(`${menu_num(menu_ALL[i][3])} ${menu_ALL[i][1]}`, function(){menu_toggle(`${menu_ALL[i][3]}`,`${menu_ALL[i][0]}`)});
  68. }
  69. else if (menu_ALL[i][0] === 'menu_customMode')
  70. { // 自定义当前模式
  71. GM_setValue(menu_ALL[i][0], menu_ALL[i][3]);
  72. menu_ID[i] = GM_registerMenuCommand(`#️⃣ ${menu_ALL[i][1]}`, function(){menu_customMode()});
  73. }
  74. else if (menu_ALL[i][0] === 'menu_customMode1' || menu_ALL[i][0] === 'menu_customMode2' || menu_ALL[i][0] === 'menu_customMode3')
  75. { // 当前模式值
  76. GM_setValue(menu_ALL[i][0], menu_ALL[i][3]);
  77. }
  78. else if (menu_ALL[i][0] === 'menu_forcedToEnable')
  79. { // 强制当前网站启用护眼模式
  80. if (menu_value('menu_autoRecognition')) { // 自动排除自带暗黑模式的网页 (beta)
  81. if (menu_forcedToEnable('check')) { // 当前网站是否已存在列表中
  82. menu_ID[i] = GM_registerMenuCommand(`${menu_ALL[i][1]}`, function(){menu_forcedToEnable('del')});
  83. } else {
  84. menu_ID[i] = GM_registerMenuCommand(`${menu_ALL[i][2]}`, function(){menu_forcedToEnable('add')});
  85. }
  86. }
  87. }
  88. else
  89. {
  90. menu_ID[i] = GM_registerMenuCommand(`${menu_ALL[i][3]?'✅':'❎'} ${menu_ALL[i][1]}`, function(){menu_switch(`${menu_ALL[i][3]}`,`${menu_ALL[i][0]}`,`${menu_ALL[i][2]}`)});
  91. }
  92. }
  93. menu_ID[menu_ID.length] = GM_registerMenuCommand('💬 反馈 & 建议', function () {window.GM_openInTab('https://github.com/XIU2/UserScript#xiu2userscript', {active: true,insert: true,setParent: true});window.GM_openInTab('https://greasyfork.org/zh-CN/scripts/412212/feedback', {active: true,insert: true,setParent: true});});
  94. }
  95.  
  96.  
  97. function menu_num(num) {
  98. switch(num) {
  99. case 1:
  100. return '1️⃣'
  101. break;
  102. case 2:
  103. return '2️⃣'
  104. break;
  105. case 3:
  106. return '3️⃣'
  107. break;
  108. }
  109. }
  110.  
  111.  
  112. // 自定义当前模式
  113. function menu_customMode() {
  114. let newMods, tip, defaults, name;
  115. switch(menu_value('menu_darkModeType')) {
  116. case 1:
  117. tip = '自定义 [模式 1],修改后立即生效 (部分网页可能需要刷新)~\n格式:亮度 (白天)|亮度 (晚上)\n默认:80|70(均为百分比 1~100,不需要 % 符号)';
  118. defaults = '80|70';
  119. name = 'menu_customMode1';
  120. break;
  121. case 2:
  122. tip = '自定义 [模式 2],修改后立即生效 (部分网页可能需要刷新)~\n格式:亮度 (白天)|暖色 (白天)|亮度 (晚上)|暖色 (晚上)\n默认:80|20|70|30(均为百分比 1~100,不需要 % 符号)';
  123. defaults = '80|20|70|30';
  124. name = 'menu_customMode2';
  125. break;
  126. case 3:
  127. tip = '自定义 [模式 3],修改后立即生效 (部分网页可能需要刷新)~\n格式:反色\n默认:80(均为百分比 50~100,不需要 % 符号)';
  128. defaults = '80';
  129. name = 'menu_customMode3';
  130. break;
  131. }
  132. newMods = prompt(tip, GM_getValue(`${name}`));
  133. if (newMods === '') {
  134. GM_setValue(`${name}`, defaults);
  135. registerMenuCommand(); // 重新注册脚本菜单
  136. } else if (newMods != null) {
  137. GM_setValue(`${name}`, newMods);
  138. registerMenuCommand(); // 重新注册脚本菜单
  139. }
  140. if (document.getElementById('XIU2DarkMode')) {
  141. document.getElementById('XIU2DarkMode').remove(); // 即时修改样式
  142. addStyle();
  143. }
  144. }
  145.  
  146.  
  147. // 强制当前网站启用护眼模式
  148. function menu_forcedToEnable(type) {
  149. switch(type) {
  150. case 'check':
  151. if(check()) return true
  152. return false
  153. break;
  154. case 'add':
  155. add();
  156. break;
  157. case 'del':
  158. del();
  159. break;
  160. }
  161.  
  162. function check() { // 存在返回真,不存在返回假
  163. let websiteList = menu_value('menu_forcedToEnable'); // 读取网站列表
  164. for (let num = 0;num<websiteList.length;num++) { // 判断是否已存在
  165. if (websiteList[num] === location.host) {
  166. return true
  167. }
  168. };
  169. return false
  170. }
  171.  
  172. function add() {
  173. if (check()) return
  174. let websiteList = menu_value('menu_forcedToEnable'); // 读取网站列表
  175. websiteList.push(location.host); // 追加网站域名
  176. GM_setValue('menu_forcedToEnable', websiteList); // 写入配置
  177. location.reload(); // 刷新网页
  178. }
  179.  
  180. function del() {
  181. if (!check()) return
  182. let websiteList = menu_value('menu_forcedToEnable'), // 读取网站列表
  183. index = websiteList.indexOf(location.host);
  184. websiteList.splice(index, 1); // 删除网站域名
  185. GM_setValue('menu_forcedToEnable', websiteList); // 写入配置
  186. location.reload(); // 刷新网页
  187. }
  188. }
  189.  
  190.  
  191. // 启用/禁用护眼模式 (当前网站)
  192. function menu_disable(type) {
  193. switch(type) {
  194. case 'check':
  195. if(check()) return true
  196. return false
  197. break;
  198. case 'add':
  199. add();
  200. break;
  201. case 'del':
  202. del();
  203. break;
  204. }
  205.  
  206. function check() { // 存在返回真,不存在返回假
  207. let websiteList = menu_value('menu_disable'); // 读取网站列表
  208. for (let num = 0;num<websiteList.length;num++) { // 判断是否已存在
  209. if (websiteList[num] === location.host) {
  210. return true
  211. }
  212. };
  213. return false
  214. }
  215.  
  216. function add() {
  217. if (check()) return
  218. let websiteList = menu_value('menu_disable'); // 读取网站列表
  219. websiteList.push(location.host); // 追加网站域名
  220. GM_setValue('menu_disable', websiteList); // 写入配置
  221. location.reload(); // 刷新网页
  222. }
  223.  
  224. function del() {
  225. if (!check()) return
  226. let websiteList = menu_value('menu_disable'), // 读取网站列表
  227. index = websiteList.indexOf(location.host);
  228. websiteList.splice(index, 1); // 删除网站域名
  229. GM_setValue('menu_disable', websiteList); // 写入配置
  230. location.reload(); // 刷新网页
  231. }
  232. }
  233.  
  234.  
  235. // 切换暗黑模式
  236. function menu_toggle(menu_status, Name) {
  237. menu_status = parseInt(menu_status)
  238. if (menu_status >= 3){
  239. menu_status = 1;
  240. } else {
  241. menu_status += 1;
  242. }
  243. GM_setValue(`${Name}`, menu_status);
  244. location.reload(); // 刷新网页
  245. };
  246.  
  247.  
  248. // 菜单开关
  249. function menu_switch(menu_status, Name, Tips) {
  250. if (menu_status == 'true'){
  251. GM_setValue(`${Name}`, false);
  252. GM_notification({text: `已关闭 [${Tips}] 功能\n(刷新网页后生效)`, timeout: 3500});
  253. }else{
  254. GM_setValue(`${Name}`, true);
  255. GM_notification({text: `已开启 [${Tips}] 功能\n(刷新网页后生效)`, timeout: 3500});
  256. }
  257. registerMenuCommand(); // 重新注册脚本菜单
  258. };
  259.  
  260.  
  261. // 返回菜单值
  262. function menu_value(menuName) {
  263. for (let menu of menu_ALL) {
  264. if (menu[0] == menuName) {
  265. return menu[3]
  266. }
  267. }
  268. }
  269.  
  270.  
  271. // 添加样式
  272. function addStyle() {
  273. let remove = false, style_Add = document.createElement('style'),
  274. hours = new Date().getHours(),
  275. style_10 = menu_value('menu_customMode1').split('|'),
  276. style_20 = menu_value('menu_customMode2').split('|'),
  277. style_30 = menu_value('menu_customMode3').split('|'),
  278. style = ``,
  279. style_00 = `html, body {background-color: #ffffff;}`,
  280. style_11 = `html {filter: brightness(${style_10[0]}%) !important;}`,
  281. style_11_firefox = `html {filter: brightness(${style_10[0]}%) !important; background-image: url();}`,
  282. style_12 = `html {filter: brightness(${style_10[1]}%) !important;}`,
  283. style_12_firefox = `html {filter: brightness(${style_10[1]}%) !important; background-image: url();}`,
  284. style_21 = `html {filter: brightness(${style_20[0]}%) sepia(${style_20[1]}%) !important;}`,
  285. style_21_firefox = `html {filter: brightness(${style_20[0]}%) sepia(${style_20[1]}%) !important; background-image: url();}`,
  286. style_22 = `html {filter: brightness(${style_20[2]}%) sepia(${style_20[3]}%) !important;}`,
  287. style_22_firefox = `html {filter: brightness(${style_20[2]}%) sepia(${style_20[3]}%) !important; background-image: url();}`,
  288. style_31 = `html {filter: invert(${style_30[0]}%) !important;} img, video {filter: invert(1) !important;}`,
  289. style_31_firefox = `html {filter: invert(${style_30[0]}%) !important; background-image: url();} img, video {filter: invert(1) !important;}`;
  290.  
  291. // Firefox 浏览器需要特殊对待
  292. if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
  293. style_11 = style_11_firefox
  294. style_12 = style_12_firefox
  295. style_21 = style_21_firefox
  296. style_22 = style_22_firefox
  297. style_31 = style_31_firefox
  298. }
  299.  
  300. // 白天(7点到19点)
  301. if (hours > 6 && hours < 19) {
  302. if (menu_value('menu_runDuringTheDay')) {
  303. style_12 = style_11
  304. style_22 = style_21
  305. } else {
  306. style_12 = style_22 = ''
  307. }
  308. }
  309.  
  310. switch(menu_value('menu_darkModeType')) {
  311. case 1:
  312. style += style_12;
  313. break;
  314. case 2:
  315. style += style_22;
  316. break;
  317. case 3:
  318. style += style_31;
  319. break;
  320. }
  321. style_Add.id = 'XIU2DarkMode';
  322. style_Add.type = 'text/css';
  323. //console.log(document,document.lastChild,document.querySelector('html'))
  324. if (document.lastChild) {
  325. document.lastChild.appendChild(style_Add).textContent = style;
  326. } else { // 发现个别网站速度太慢的话,就会出现脚本运行太早,连 html 标签都还没加载。。。
  327. let timer1 = setInterval(function(){ // 每 5 毫秒检查一下 html 是否已存在
  328. if (document.lastChild) {
  329. clearInterval(timer1); // 取消定时器
  330. document.lastChild.appendChild(style_Add).textContent = style;
  331. }
  332. }, 5);
  333. }
  334.  
  335. let websiteList = menu_value('menu_forcedToEnable'); // 强制当前网站启用护眼模式
  336.  
  337. // 为了避免 body 还没加载导致无法检查是否设置背景颜色
  338. let timer = setInterval(function(){ // 每 10 毫秒检查一下 body 是否已存在
  339. if (document.body) {
  340. clearInterval(timer); // 取消定时器(每 10 毫秒一次的)
  341. setTimeout(function(){ // 为了避免太快 body 的 CSS 还没加载上,先延迟 150 毫秒(缺点就是可能会出现短暂一闪而过的暗黑滤镜)
  342. console.log('html:', window.getComputedStyle(document.lastChild).backgroundColor, 'body:', window.getComputedStyle(document.body).backgroundColor)
  343. if (window.getComputedStyle(document.body).backgroundColor === 'rgba(0, 0, 0, 0)' && window.getComputedStyle(document.lastChild).backgroundColor === 'rgba(0, 0, 0, 0)') {
  344. // 如果 body 没有 CSS 背景颜色,那就需要添加一个背景颜色,否则影响滤镜效果
  345. let style_Add2 = document.createElement('style');
  346. style_Add2.id = 'XIU2DarkMode2';
  347. document.lastChild.appendChild(style_Add2).textContent = style_00;
  348. } else if (window.getComputedStyle(document.body).backgroundColor === 'rgb(0, 0, 0)' || getColorValue(document.body) > 0 && getColorValue(document.body) < 898989 || getColorValue(document.lastChild) > 0 && getColorValue(document.lastChild) < 898989 || window.getComputedStyle(document.body).backgroundColor === 'rgba(0, 0, 0, 0)' && window.getComputedStyle(document.lastChild).backgroundColor === 'rgb(0, 0, 0)') {
  349. // 如果是黑色 (等于0,0,0) 或深色 (小于 89,89,89),就停用本脚本滤镜
  350. if (menu_value('menu_autoRecognition')) { // 排除自带暗黑模式的网页 (beta)
  351. for (let i=0;i<websiteList.length;i++){ // 这些网站强制启用护眼模式滤镜
  352. if (websiteList[i] === location.host) return
  353. }
  354. console.log('检测到当前网页自带暗黑模式,停用本脚本滤镜...')
  355. document.getElementById('XIU2DarkMode').remove();
  356. remove = true;
  357. }
  358. }
  359. }, 150);
  360.  
  361. // 用来解决一些 CSS 加载缓慢的网站,可能会出现没有正确排除的问题,在没有找到更好的办法之前,先这样凑活着用
  362. setTimeout(function(){
  363. console.log('html:', window.getComputedStyle(document.lastChild).backgroundColor, 'body:', window.getComputedStyle(document.body).backgroundColor)
  364. if (window.getComputedStyle(document.body).backgroundColor === 'rgb(0, 0, 0)' || getColorValue(document.body) > 0 && getColorValue(document.body) < 898989 || getColorValue(document.lastChild) > 0 && getColorValue(document.lastChild) < 898989 || window.getComputedStyle(document.body).backgroundColor === 'rgba(0, 0, 0, 0)' && window.getComputedStyle(document.lastChild).backgroundColor === 'rgb(0, 0, 0)') {
  365. // 如果是黑色 (等于0,0,0) 或深色 (小于 89,89,89),就停用本脚本滤镜
  366. if (menu_value('menu_autoRecognition')) { // 排除自带暗黑模式的网页 (beta)
  367. for (let i=0;i<websiteList.length;i++){ // 这些网站强制启用护眼模式滤镜
  368. if (websiteList[i] === location.host) return
  369. }
  370. if (remove) return
  371. console.log('检测到当前网页自带暗黑模式,停用本脚本滤镜...')
  372. if (document.getElementById('XIU2DarkMode')) document.getElementById('XIU2DarkMode').remove();
  373. if (document.getElementById('XIU2DarkMode2')) document.getElementById('XIU2DarkMode2').remove();
  374. }
  375. }
  376. }, 1500);
  377. }
  378. }, 10);
  379.  
  380. // 用来解决一些 CSS 加载缓慢的网站,可能会出现没有正确排除的问题,在没有找到更好的办法之前,先这样凑活着用
  381. /*setTimeout(function(){
  382. console.log('html:', window.getComputedStyle(document.lastChild).backgroundColor, 'body:', window.getComputedStyle(document.body).backgroundColor)
  383. if (window.getComputedStyle(document.body).backgroundColor === 'rgb(0, 0, 0)' || getColorValue(document.body) > 0 && getColorValue(document.body) < 898989 || getColorValue(document.lastChild) > 0 && getColorValue(document.lastChild) < 898989) {
  384. // 如果是黑色 (等于0,0,0) 或深色 (小于 89,89,89),就停用本脚本滤镜
  385. if (menu_value('menu_autoRecognition')) { // 排除自带暗黑模式的网页 (beta)
  386. for (let i=0;i<websiteList.length;i++){ // 这些网站强制启用护眼模式滤镜
  387. if (websiteList[i] === location.host) return
  388. }
  389. if (remove) return
  390. console.log('检测到当前网页自带暗黑模式,停用本脚本滤镜...')
  391. if (document.getElementById('XIU2DarkMode')) document.getElementById('XIU2DarkMode').remove();
  392. if (document.getElementById('XIU2DarkMode2')) document.getElementById('XIU2DarkMode2').remove();
  393. }
  394. }
  395. }, 3000);*/
  396.  
  397. // 解决远景论坛会清理掉前面插入的 CSS 样式的问题
  398. if (location.hostname === 'bbs.pcbeta.com') {
  399. let timer1 = setInterval(function(){
  400. if (!document.getElementById('XIU2DarkMode')) {
  401. document.lastChild.appendChild(style_Add).textContent = style;
  402. clearInterval(timer1);
  403. }
  404. }, 10);
  405. }
  406. }
  407.  
  408. // 获取背景颜色值
  409. function getColorValue(e) {
  410. let rgbValueArry = window.getComputedStyle(e).backgroundColor.replace(/rgba|rgb|\(|\)| /g, '').split (',')
  411. return parseInt(rgbValueArry[0] + rgbValueArry[1] + rgbValueArry[2])
  412. }
  413. })();