新标签页图片缩放

允许在新标签页中打开的图片独立缩放,而不影响原网页

  1. // ==UserScript==
  2. // @name New Tab Image Zoom
  3. // @name:zh-CN 新标签页图片缩放
  4. // @namespace https://gf.qytechs.cn/zh-CN/users/193133-pana
  5. // @homepage https://gf.qytechs.cn/zh-CN/users/193133-pana
  6. // @version 1.0.1
  7. // @description Allows the image opened in the new tab to zoom independently without affecting the original page
  8. // @description:zh-CN 允许在新标签页中打开的图片独立缩放,而不影响原网页
  9. // @author pana
  10. // @license GNU General Public License v3.0 or later
  11. // @match *://*/*
  12. // @compatible chrome
  13. // @compatible firefox
  14. // @grant GM_addStyle
  15. // @grant GM_getValue
  16. // @grant GM_setValue
  17. // @grant GM_registerMenuCommand
  18. // @noframes
  19. // ==/UserScript==
  20.  
  21. (function () {
  22. 'use strict';
  23. class Picture {
  24. constructor(imgObj, shortcuts = false) {
  25. this.imgObj = imgObj;
  26. this.imgSrc = imgObj.src;
  27. this.shortcuts = shortcuts;
  28. this.wrapper = null;
  29. this.tip = null;
  30. this.ratio = 1;
  31. this.width = 0;
  32. this.height = 0;
  33. this.naturalWidth = 0;
  34. this.naturalHeight = 0;
  35. this.innerWidth = window.innerWidth;
  36. this.innerHeight = window.innerHeight;
  37. this.init();
  38. }
  39. sleep(ms, callback) {
  40. setTimeout(() => {
  41. console.info(ms / 1000 + ' second later.');
  42. if (typeof callback === 'function') {
  43. callback();
  44. }
  45. }, ms);
  46. }
  47. resetImgUrl(maxErrorNum) {
  48. if (maxErrorNum > 0) {
  49. this.imgObj.onerror = () => {
  50. console.warn('Image load error: ' + this.imgSrc + '\nReset count: ' + (12 - maxErrorNum));
  51. this.resetImgUrl(maxErrorNum - 1);
  52. };
  53. this.sleep(500, () => {
  54. this.imgObj.src = this.imgSrc;
  55. });
  56. } else {
  57. this.imgObj.onerror = null;
  58. }
  59. }
  60. tips() {
  61. const tip = document.createElement('div');
  62. tip.className = 'new-tab-image-zoom-tips';
  63. tip.title = 'Left-Click: Restore the original size(100%).\nRight-Click: Adapt to window size.';
  64. tip.addEventListener('click', event => {
  65. event.stopPropagation();
  66. this.restore();
  67. });
  68. tip.addEventListener('contextmenu', event => {
  69. event.preventDefault();
  70. this.adapt();
  71. });
  72. this.tip = tip;
  73. this.setTips();
  74. this.observer();
  75. }
  76. setTips() {
  77. if (this.tip) {
  78. this.tip.textContent = Math.round(this.ratio * 100) + '%';
  79. }
  80. }
  81. observer() {
  82. const ob = new ResizeObserver(() => {
  83. this.handler();
  84. });
  85. ob.observe(this.imgObj);
  86. }
  87. wrap() {
  88. this.tips();
  89. const wrapDiv = document.createElement('div');
  90. wrapDiv.className = 'new-tab-image-zoom-wrapper';
  91. document.body.insertBefore(wrapDiv, this.imgObj);
  92. document.body.removeChild(this.imgObj);
  93. wrapDiv.appendChild(this.imgObj);
  94. this.tip && wrapDiv.appendChild(this.tip);
  95. wrapDiv.addEventListener('wheel', event => {
  96. if (event.ctrlKey) {
  97. event.preventDefault();
  98. if (event.wheelDelta > 0) {
  99. this.zoomIn(0.1);
  100. } else if (event.wheelDelta < 0) {
  101. this.zoomOut(0.1);
  102. }
  103. return false;
  104. }
  105. });
  106. this.wrapper = wrapDiv;
  107. }
  108. handler() {
  109. this.width = this.imgObj.width || 0;
  110. this.height = this.imgObj.height || 0;
  111. this.naturalWidth = this.imgObj.naturalWidth || 0;
  112. this.naturalHeight = this.imgObj.naturalHeight || 0;
  113. if (this.width > 0 && this.height > 0 && this.naturalWidth > 0 && this.naturalHeight > 0) {
  114. this.ratio = this.width / this.naturalWidth || this.height / this.naturalHeight;
  115. this.setTips();
  116. }
  117. }
  118. init() {
  119. this.wrap();
  120. this.imgObj.classList.add('new-tab-image-zoom-image');
  121. if (this.imgObj.complete) {
  122. this.handler();
  123. } else {
  124. this.imgObj.onload = () => {
  125. this.handler();
  126. };
  127. this.imgObj.onerror = () => {
  128. console.warn('Image load error: ' + this.src + '\nReset count: 1');
  129. this.resetImgUrl(10);
  130. };
  131. }
  132. if (this.shortcuts) {
  133. document.onkeydown = e => {
  134. const ev = e || window.event;
  135. const eKeyCode = ev.keycode || ev.which || ev.charCode;
  136. const eCtrl = ev.ctrlKey || ev.metaKey;
  137. const eAlt = ev.altKey;
  138. if (eKeyCode === 187 && eCtrl) {
  139. e.preventDefault();
  140. this.zoomIn();
  141. return false;
  142. }
  143. if (eKeyCode === 189 && eCtrl) {
  144. e.preventDefault();
  145. this.zoomOut();
  146. return false;
  147. }
  148. if (eKeyCode === 48 && eCtrl && eAlt) {
  149. e.preventDefault();
  150. this.restore();
  151. return false;
  152. }
  153. if (eKeyCode === 48 && eCtrl) {
  154. e.preventDefault();
  155. this.adapt();
  156. return false;
  157. }
  158. };
  159. }
  160. }
  161. zoom() {
  162. this.imgObj.width = this.naturalWidth * this.ratio;
  163. this.imgObj.height = this.naturalHeight * this.ratio;
  164. }
  165. zoomIn(ratio = 0.1) {
  166. this.ratio += ratio;
  167. this.zoom();
  168. }
  169. zoomOut(ratio = 0.1) {
  170. if (this.ratio > ratio) {
  171. this.ratio -= ratio;
  172. this.zoom();
  173. }
  174. }
  175. restore() {
  176. this.ratio = 1;
  177. this.zoom();
  178. }
  179. adapt() {
  180. this.innerWidth = window.innerWidth;
  181. this.innerHeight = window.innerHeight;
  182. if (this.naturalWidth / this.naturalHeight >= this.innerWidth / this.innerHeight) {
  183. this.ratio = this.innerWidth / this.naturalWidth;
  184. } else {
  185. this.ratio = this.innerHeight / this.naturalHeight;
  186. }
  187. this.zoom();
  188. }
  189. }
  190. if (document.body.children.length === 1 && document.body.children[0].nodeName === 'IMG') {
  191. const zoomStyle = `
  192. .new-tab-image-zoom-wrapper {
  193. display: flex;
  194. background-color: #0e0e0e;
  195. width: 100%;
  196. height: 100%;
  197. justify-content: center;
  198. align-items: center;
  199. }
  200. .new-tab-image-zoom-image {
  201. margin: auto;
  202. }
  203. .new-tab-image-zoom-tips {
  204. position: fixed;
  205. z-index: 99;
  206. top: 1vh;
  207. right: 1vw;
  208. padding: 3px 6px;
  209. text-align: center;
  210. color: #eeeeee;
  211. font-size: 14px;
  212. line-height: 1;
  213. background-color: #666666;
  214. border: 1px solid #333333;
  215. box-shadow: 1px 1px 2px #030303;
  216. border-radius: 5px;
  217. box-sizing: border-box;
  218. overflow: hidden;
  219. user-select: none;
  220. cursor: pointer;
  221. }
  222. .new-tab-image-zoom-tips:hover {
  223. color: #449a46;
  224. background-color: #f0f9eb;
  225. border: 1px solid #d6e9c6;
  226. }
  227. `;
  228. GM_addStyle(zoomStyle);
  229. const { shortcuts } = GM_getValue('newTabConfig', { shortcuts: false });
  230. const _picture = new Picture(document.body.children[0], shortcuts);
  231. GM_registerMenuCommand(`Keyboard shortcuts [${shortcuts ? 'Enabled' : 'Disabled'}]`, () => {
  232. GM_setValue('newTabConfig', {
  233. shortcuts: !shortcuts,
  234. });
  235. location.reload();
  236. });
  237. }
  238. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址