Twitter SafeView: Auto-Blur with Hover Reveal

自动打码推特所有图片(包含视频预览),悬停时才显示完整清晰图像,避免黄图泛滥的尴尬。Automatically blurs all images and displays full clear images only when hovering.

  1. // ==UserScript==
  2. // @name Twitter SafeView: Auto-Blur with Hover Reveal
  3. // @namespace http://hhtjim.com/
  4. // @version 1.1.5
  5. // @description 自动打码推特所有图片(包含视频预览),悬停时才显示完整清晰图像,避免黄图泛滥的尴尬。Automatically blurs all images and displays full clear images only when hovering.
  6. // @author Hootrix
  7. // @match https://twitter.com/*
  8. // @match https://x.com/*
  9. // @grant none
  10. // @license GPLv3
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. const debounceDelay = 100; // milliseconds
  17.  
  18. // Debounce function to limit the rate of execution
  19. function debounce(func, delay) {
  20. let debounceTimer;
  21. return function() {
  22. const context = this;
  23. const args = arguments;
  24. clearTimeout(debounceTimer);
  25. debounceTimer = setTimeout(() => func.apply(context, args), delay);
  26. };
  27. }
  28.  
  29.  
  30. let mouseX = 0, mouseY = 0;
  31.  
  32. document.addEventListener('mousemove', debounce(function(e) {
  33. mouseX = e.clientX;
  34. mouseY = e.clientY;
  35. updateImageBlur();
  36. }, debounceDelay));
  37.  
  38.  
  39. document.addEventListener('scroll', debounce(function(e) {
  40. updateImageBlur();
  41. }, debounceDelay));
  42.  
  43.  
  44. // Function to check if the mouse is over the element
  45. function isMouseOverElement(element) {
  46. const rect = element.getBoundingClientRect();
  47. return mouseX > rect.left && mouseX < rect.right && mouseY > rect.top && mouseY < rect.bottom;
  48. }
  49.  
  50. // Function to update image blur
  51. function updateImageBlur() {
  52. // console.log('updateImageBlur')
  53. //列表
  54. document.querySelectorAll('article[data-testid="tweet"]').forEach(function(tweetDiv) {
  55. // Apply or remove blur based on mouse position
  56. if (isMouseOverElement(tweetDiv)) {
  57. closeBlur(tweetDiv)
  58. } else {
  59. applyBlur(tweetDiv)
  60. }
  61. });
  62. }
  63.  
  64. // Apply blur to the div and nested img
  65. const applyBlur = (document) => {
  66. // 推文
  67. document.querySelectorAll('div[data-testid="tweetPhoto"], div[data-testid="card.layoutLarge.media"]').forEach(function(div) {
  68. div.style.filter = 'blur(8px)';
  69. const img = div.querySelector('img');
  70. if (img) img.style.filter = 'blur(8px)';
  71. });
  72. };
  73. const closeBlur = (document) => {
  74. document.querySelectorAll('div[data-testid="tweetPhoto"], div[data-testid="card.layoutLarge.media"]').forEach(function(div) {
  75. div.style.filter = '';
  76. const img = div.querySelector('img');
  77. if (img) img.style.filter = '';
  78. });
  79. };
  80.  
  81. // Observe for changes in the document
  82. const observer = new MutationObserver(debounce(function() {
  83. updateImageBlur();
  84. },debounceDelay));
  85.  
  86.  
  87. // Configuration of the observer
  88. const config = { childList: true, subtree: true };
  89.  
  90. // var target = document.querySelector('section[aria-labelledby="accessible-list-1"]')
  91. var target = document.body
  92.  
  93. // Start observing the target node for configured mutations
  94. if(target){
  95. observer.observe(target, config);
  96. }
  97.  
  98. // Initial application of blur to images
  99. updateImageBlur();
  100. })();