图片列表全屏

网页图片全屏查看,缩放, 切换

  1. // ==UserScript==
  2. // @name 图片列表全屏
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1.3
  5. // @description 网页图片全屏查看,缩放, 切换
  6. // @author liangxin
  7. // @match *://*/*
  8. // @grant GM_registerMenuCommand
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. // 图片数组
  13. let imgs;
  14.  
  15. // 图片浏览父容器
  16. let div;
  17. // 当前操作的 图片元素;
  18. let img;
  19. // 当前操作图片索引
  20. let imgIndex = 0;
  21. // 图片大小
  22. let imgSize;
  23. // 图片宽高比
  24. let imgAspectRatio;
  25. // 图片缩放比例
  26. let imgZoomNum = 1;
  27. // 初始缩放比例, 即第一次展示时的缩放比例
  28. let initImgZoomNum = 1;
  29.  
  30.  
  31. function loading() {
  32. if(!loadImg()){
  33. return;
  34. }
  35. initMenu();
  36. initEvent();
  37. // initDiv();
  38. }
  39. let loadImg=()=>{
  40. // 获取图片元素集合
  41. imgs = Array.from(document.getElementsByTagName("img"));
  42. // 过滤小图片
  43. let min=150
  44. imgs = imgs.filter(img => img.width > min && img.height > min);
  45. if (imgs.length <1) {
  46. return false;
  47. }
  48. return true;
  49. }
  50.  
  51.  
  52. /**
  53. * 加载菜单
  54. */
  55. let initMenu = () => {
  56. // 右键菜单
  57. let msg = `Ctrl + I : 开启图片全屏列表 Shift + I : 重新加载图片全屏列表
  58. ESC : 退出图片全屏
  59. 鼠标滚轮: 缩放图片 鼠标拖动: 移动图片
  60. `;
  61. const w = unsafeWindow || window;
  62. GM_registerMenuCommand('图片浏览快捷键表', alert.bind(w, msg));
  63. }
  64.  
  65.  
  66. /**
  67. * 加载父级容器
  68. */
  69. let initDiv = () => {
  70. // 每次初始索引
  71. imgIndex = 0;
  72. // 如果已有 容器, 则不再重新创建
  73. if (div) {
  74. div.style.width = window.innerWidth + "px";
  75. div.style.height = window.innerHeight + "px";
  76. div.style.display = "block";
  77. return;
  78. }
  79. div = document.createElement("div");
  80. div.style.width = window.innerWidth + "px";
  81. div.style.height = window.innerHeight + "px";
  82. div.style.backgroundColor = "#000";
  83. div.style.position = "fixed";
  84. div.style.top = 0;
  85. div.style.left = 0;
  86. div.style.zIndex = 9999999;
  87. div.style.display = "block";
  88. div.style.overflow = "hidden";
  89. document.body.append(div);
  90. }
  91.  
  92. /**
  93. * 显示图片
  94. */
  95. let imgShow = () => {
  96. let showImg = new Image();
  97. showImg.src = imgs[imgIndex].src;
  98. showImg.title = imgs[imgIndex].title === "" ? "第" + (imgIndex + 1) +"/"+imgs.length+ "张" : imgs[imgIndex].title;
  99.  
  100. showImg.style.position = "absolute";
  101. div.innerHTML = showImg.outerHTML;
  102. img = div.querySelector("img");
  103. // 判断是否已加载成功
  104. if (img.complete) {
  105. imgSize = {
  106. "width": img.width,
  107. "height": img.height
  108. };
  109. imgAspectRatio = getImgAspectRatio(img);
  110. imgZoomNum = 1;
  111. setImgSize();
  112. } else {
  113. // 如果还未加载成功, 则将 设置大小的操作,写在 图片的 onload 函数中
  114. img.onload = function() {
  115. imgSize = {
  116. "width": img.width,
  117. "height": img.height
  118. };
  119. imgAspectRatio = getImgAspectRatio(img);
  120. imgZoomNum = 1;
  121. setImgSize();
  122. }
  123. }
  124. imgZoomEvent();
  125. imgMove();
  126.  
  127. }
  128.  
  129.  
  130. /**
  131. * 初始 图片大小
  132. */
  133. function setImgSize() {
  134. let newHeight = window.innerHeight;
  135. let newWidth = 0;
  136. do {
  137. newHeight = newHeight - 5;
  138. newWidth = calculateWidth(newHeight, imgAspectRatio);
  139. } while (newWidth > window.innerWidth);
  140. imgZoom(newHeight / imgSize.height);
  141. initImgZoomNum = imgZoomNum;
  142. }
  143.  
  144. /**
  145. * 图片左右居中
  146. */
  147. function imgCenter() {
  148. img.style.marginLeft = (window.innerWidth - img.width) / 2 + "px";
  149. }
  150.  
  151. /**
  152. * 根据比例关系, 已知 高度, 计算 宽度
  153. */
  154. function calculateWidth(heigth, aspectRatio) {
  155. return parseInt(heigth * aspectRatio.width / aspectRatio.height);
  156. }
  157.  
  158. /**
  159. * 根据比例关系, 已知 宽度, 计算 高度
  160. */
  161. function calculateHeight(width, aspectRatio) {
  162. return parseInt(width * aspectRatio.height / aspectRatio.width);
  163. }
  164.  
  165. /**
  166. * 缩放图片
  167. */
  168. let imgZoom = (zoom = 1) => {
  169. let newHeight = float_calculator.mul(imgSize.height, zoom);
  170. img.height = parseInt(newHeight);
  171. img.width = calculateWidth(newHeight, imgAspectRatio);
  172. imgCenter();
  173. showMsg.show(parseInt(float_calculator.mul(zoom, 100)) + "%");
  174. imgZoomNum = zoom;
  175. return false;
  176. }
  177.  
  178. /**
  179. * 鼠标滚轮缩放事件
  180. */
  181. let imgZoomEvent = () => {
  182. // 缩放基数
  183. let zoom = 0.1;
  184. let zoomEvent = (zoomOpt) => {
  185. if (initImgZoomNum == imgZoomNum) {
  186. let i = (imgZoomNum * 100) % (zoom * 100);
  187. if (i > 0) {
  188. imgZoomNum = (imgZoomNum * 100 + zoom * 100 - i) / 100
  189. }
  190. }
  191. if (zoomOpt) {
  192. // 向上 放大
  193. imgZoomNum = float_calculator.add(imgZoomNum, zoom)
  194. } else {
  195. // 向下 // 缩小
  196. imgZoomNum = float_calculator.add(imgZoomNum, -zoom)
  197. }
  198. if (imgZoomNum < zoom) {
  199. imgZoomNum=zoom;
  200. return;
  201. }
  202. imgZoom(imgZoomNum);
  203. return false;
  204. }
  205. div.onmousewheel = function(e) {
  206. zoomEvent(e.wheelDelta > 0);
  207. };
  208.  
  209.  
  210. // 双击回复为 初始缩放比例
  211. div.addEventListener("dblclick", e => {
  212. imgZoom(initImgZoomNum);
  213. })
  214. }
  215.  
  216. let imgMove = () => {
  217.  
  218. let imgTop = 0;
  219. let imgLeft = 0;
  220. let mx = 0;
  221. let my = 0;
  222.  
  223. img.ondragstart = e => {
  224. mx = e.screenX;
  225. my = e.screenY;
  226. if (img.style.top === "") {
  227. imgTop = 0;
  228. imgLeft = 0;
  229. }
  230. }
  231.  
  232. img.ondrag = e => {
  233. if (e.screenY == 0 && e.screenX == 0) {
  234. return
  235. }
  236. imgTop = imgTop + e.screenY - my;
  237. imgLeft = imgLeft + e.screenX - mx;
  238. mx = e.screenX;
  239. my = e.screenY;
  240. img.style.top = imgTop + "px";
  241. img.style.left = imgLeft + "px";
  242.  
  243. }
  244.  
  245. }
  246.  
  247. /**
  248. * 初始化事件
  249. */
  250. let initEvent = () => {
  251. window.addEventListener("keydown", e => {
  252. // <- 和 -> 切换图片
  253. if (e.keyCode == 37 || e.keyCode == 39) {
  254. if (e.keyCode == 39) {
  255. imgIndex++;
  256. imgIndex = imgIndex >= imgs.length ? 0 : imgIndex;
  257. } else {
  258. imgIndex--;
  259. imgIndex = imgIndex < 0 ? imgs.length - 1 : imgIndex;
  260. }
  261. imgShow();
  262. }
  263. // ESC 键 隐藏
  264. if (e.keyCode == 27) {
  265. div.style.display = "none";
  266. }
  267. // Ctrl + I 开启浏览
  268. if (e.ctrlKey && e.keyCode == 73) {
  269. initDiv();
  270. imgShow();
  271. }
  272. // Shift + I 重新加载图片
  273. if(e.shiftKey & e.keyCode == 73){
  274. loadImg();
  275. initDiv();
  276. imgShow();
  277. }
  278. })
  279.  
  280. }
  281.  
  282. window.addEventListener("load",e=>{
  283. loading();
  284. });
  285.  
  286.  
  287.  
  288.  
  289.  
  290. //---------------------------- 工具方法 ------------------
  291.  
  292. /**
  293. * 消息对象
  294. */
  295. window.showMsg = {
  296. flag: false,
  297. msgSpan: null,
  298. msgTimeOut: null,
  299. show: function(m, s = 2) {
  300. // 如果 已有消息, 则清除, 并清除之前的延迟计时器
  301. if (this.msgSpan) {
  302. this.msgSpan.remove();
  303. clearTimeout(this.msgTimeOut);
  304. }
  305. this.msgSpan = document.createElement("span");
  306. document.body.append(this.msgSpan);
  307. this.msgSpan.innerHTML = m;
  308. this.msgSpan.style.position = "fixed";
  309. this.msgSpan.style.top = "50px"
  310. this.msgSpan.style.left = "49vw";
  311. this.msgSpan.style.backgroundColor = "#000";
  312. this.msgSpan.style.color = "#fff";
  313. this.msgSpan.style.lineHeight = "30px";
  314. this.msgSpan.style.padding = "0 10px";
  315. this.msgSpan.style.opacity = 0.5;
  316. this.msgSpan.style.zIndex = 2147483647;
  317. this.msgSpan.style.display = "block";
  318. this.msgSpan.style.borderRadius = "10px";
  319. this.msgSpan.style.minWidth = "105px";
  320. this.msgSpan.style.textAlign = "center";
  321.  
  322. // 延迟删除
  323. this.msgTimeOut = setTimeout(() => {
  324. this.msgSpan.remove();
  325. this.flag = false;
  326. }, s * 1000);
  327. }
  328. }
  329.  
  330. /**
  331. * 获取图片比例 [宽,高]
  332. */
  333. function getImgAspectRatio(img) {
  334. let i = img.width / img.height;
  335. console.log("比例: " + img.width + " / " + img.height + " = " + i);
  336. return i > 1 ? {
  337. "width": i,
  338. "height": 1
  339. } : {
  340. "width": 1,
  341. "height": 1 / i
  342. };
  343. }
  344.  
  345.  
  346. // 自定义高精度浮点数运算
  347. // 对象格式写法
  348. var float_calculator = {
  349. /**
  350. * 1.记录两个运算数小数点后的位数
  351. * 2.将其转化为整数类型进行运算
  352. * 3.移动小数点的位置
  353. **/
  354. add: function(arg1, arg2) {
  355. var r1, r2, m;
  356. try {
  357. //取小数位长度
  358. r1 = arg1.toString().split(".")[1].length;
  359. r2 = arg2.toString().split(".")[1].length;
  360. } catch (e) {
  361. r1 = 0;
  362. r2 = 0;
  363. }
  364. m = Math.pow(10, Math.max(r1, r2)); //计算因子
  365.  
  366. return (arg1 * m + arg2 * m) / m;
  367. },
  368. minus: function(arg1, arg2) {
  369. return this.add(arg1, -arg2);
  370. },
  371. mul: function(arg1, arg2) {
  372. var r1, r2, m;
  373. try {
  374. //取小数位长度
  375. r1 = arg1.toString().split(".")[1].length;
  376. r2 = arg2.toString().split(".")[1].length;
  377. } catch (e) {
  378. r1 = 0;
  379. r2 = 0;
  380. }
  381. m = Math.pow(10, Math.max(r1, r2)); //计算因子
  382.  
  383. return (arg1 * m) * (arg2 * m) / (m * m);
  384. }
  385. };
  386. })();