Discord 放大小图示

放大滑鼠光标下的图像。

  1. // ==UserScript==
  2. // @name Discord: Zoom In Small Icon
  3. // @name:zh-TW Discord 放大小圖示
  4. // @name:zh-CN Discord 放大小图示
  5. // @name:ja Discord 小さいアイコンにズームイン
  6. // @name:ko Discord 작은 아이콘 확대
  7. // @name:ru Discord Увеличить маленькую иконку
  8. // @version 1.0.1
  9. // @description Zoom in the image which is under the cursor.
  10. // @description:zh-TW 放大滑鼠游標下的圖片。
  11. // @description:zh-CN 放大滑鼠光标下的图像。
  12. // @description:ja カーソルの下にある画像を拡大します。
  13. // @description:ko 커서 아래에있는 이미지를 확대합니다.
  14. // @description:ru Увеличьте изображение под курсором.
  15. // @author Hayao-Gai
  16. // @namespace https://github.com/HayaoGai
  17. // @icon https://i.imgur.com/rE9N0R7.png
  18. // @match https://discord.com/channels/*
  19. // @grant none
  20. // ==/UserScript==
  21.  
  22. /* jshint esversion: 6 */
  23.  
  24. (function() {
  25. 'use strict';
  26.  
  27. const targets = [ "avatar-3FKimL", "avatar-2e8lTP", "emoji" ];
  28. const textStyle = `
  29. .zoomin-canvas {
  30. border-radius: 8px;
  31. position: fixed;
  32. background-color: #e0e0e0;
  33. pointer-events: none;
  34. opacity: 0 !important;
  35. z-index: 1003;
  36. }
  37. .zoomin-canvas-show {
  38. transition: opacity 0.4s;
  39. opacity: 1 !important;
  40. }
  41. .zoomin-zoom {
  42. position: relative;
  43. left: 5px;
  44. top: 5px;
  45. border-radius: 8px;
  46. pointer-events: none;
  47. opacity: 0 !important;
  48. }
  49. .zoomin-zoom-show {
  50. transition: opacity 0.4s;
  51. opacity: 1 !important;
  52. }`;
  53. let currentUrl = document.location.href;
  54. let updating = false, showing = false;
  55. let canvas, zoom;
  56.  
  57. css();
  58.  
  59. init(10);
  60.  
  61. locationChange();
  62.  
  63. window.addEventListener("scroll", update, true);
  64.  
  65. function init(times) {
  66. for (let i = 0; i < times; i++) {
  67. setTimeout(createCanvas, 500 * i);
  68. setTimeout(createZoom, 500 * i);
  69. for (const target of targets) {
  70. setTimeout(() => eventListener(target), 500 * i);
  71. }
  72. }
  73. }
  74.  
  75. // create
  76. function createCanvas() {
  77. // check exist
  78. if (!!canvas) return;
  79. // create
  80. canvas = document.createElement("div");
  81. canvas.classList.add("zoomin-canvas");
  82. document.body.appendChild(canvas);
  83. }
  84.  
  85. function createZoom() {
  86. // check exist
  87. if (!canvas || !!zoom) return;
  88. // create
  89. zoom = document.createElement("img");
  90. zoom.classList.add("zoomin-zoom");
  91. zoom.style.backgroundColor = getTheme();
  92. canvas.appendChild(zoom);
  93. }
  94.  
  95. // event
  96. function eventListener(target) {
  97. // return if canvas or zoom doesn't exist.
  98. if (!canvas || !zoom) return;
  99. // add target mouse event.
  100. document.querySelectorAll(`.${target}:not(zoomin-listener)`).forEach(t => {
  101. t.classList.add("zoomin-listener");
  102. t.addEventListener("mousemove", showImage);
  103. t.addEventListener("mouseleave", hideImage);
  104. });
  105. }
  106.  
  107. function showImage() {
  108. // avoid calling this function multiple times.
  109. if (showing) return;
  110. showing = true;
  111. // detail
  112. const url = getSource(this);
  113. if (!url) return;
  114. zoom.setAttribute("src", url);
  115. zoomDetail();
  116. }
  117.  
  118. function hideImage() {
  119. showing = false;
  120. canvas.classList.remove("zoomin-canvas-show");
  121. zoom.classList.remove("zoomin-zoom-show");
  122. setTimeout(() => {
  123. if (!showing) zoom.removeAttribute("src");
  124. }, 500);
  125. }
  126.  
  127. function zoomDetail() {
  128. // wait until get the image size.
  129. if (!zoom.naturalWidth) {
  130. setTimeout(zoomDetail, 100);
  131. return;
  132. }
  133. // size
  134. const w = zoom.naturalWidth;
  135. const h = zoom.naturalHeight;
  136. canvas.style.width = `${w + 10}px`;
  137. canvas.style.height = `${h + 10}px`;
  138. zoom.style.width = `${w}px`;
  139. zoom.style.height = `${h}px`;
  140. // position
  141. let left = getCursorX();
  142. let top = getCursorY();
  143. const clientW = document.documentElement.clientWidth;
  144. // situation 1: the icon position is too right to show.
  145. if (left + w > clientW) left = left - w;
  146. // situation 2: the icon position is too top to show.
  147. if (top - h - 30 > 0) top = top - h - 30;
  148. canvas.style.left = `${left}px`;
  149. canvas.style.top = `${top}px`;
  150. // class
  151. canvas.classList.add("zoomin-canvas-show");
  152. zoom.classList.add("zoomin-zoom-show");
  153. }
  154.  
  155. // method
  156. function getSource(element) {
  157. // situation 1: image
  158. if (!!element.src) return resizeImage(element.src)
  159. // situation 2: div with style
  160. else if (!!element.style.backgroundImage) return resizeImage(element.style.backgroundImage.split(/"/)[1])
  161. // situation 3: div children
  162. else if (!!element.querySelector("img")) return element.querySelector("img").src;
  163. // situation 4: not an image
  164. else return null;
  165. }
  166.  
  167. function getTheme() {
  168. const theme = document.querySelector("html").className.includes("light") ? "#ffffff" : "#363940";
  169. return theme;
  170. }
  171.  
  172. function getCursorX() {
  173. const e = window.event;
  174. return e.pageX - document.documentElement.scrollLeft - document.body.scrollLeft;
  175. }
  176.  
  177. function getCursorY() {
  178. const e = window.event;
  179. return e.pageY - document.documentElement.scrollTop - document.body.scrollTop;
  180. }
  181.  
  182. function resizeImage(url) {
  183. const index = url.indexOf("?")
  184. if (index === -1) return url
  185. return url.replace(url.substring(index), "")
  186. }
  187.  
  188. // others
  189. function css() {
  190. const style = document.createElement("style");
  191. style.type = "text/css";
  192. style.innerHTML = textStyle;
  193. document.head.appendChild(style);
  194. }
  195.  
  196. function update() {
  197. if (updating) return;
  198. updating = true;
  199. init(3);
  200. setTimeout(() => { updating = false; }, 1000);
  201. }
  202.  
  203. function locationChange() {
  204. const observer = new MutationObserver(mutations => {
  205. mutations.forEach(() => {
  206. if (currentUrl !== document.location.href) {
  207. currentUrl = document.location.href;
  208. init(10);
  209. }
  210. });
  211. });
  212. const target = document.body;
  213. const config = { childList: true, subtree: true };
  214. observer.observe(target, config);
  215. }
  216.  
  217. })();