MiniblogImgPop - 微博浮图 - 兼容新版微博

微博浮图控件,鼠标移过小图弹出浮动大图的脚本

질문, 리뷰하거나, 이 스크립트를 신고하세요.
  1. // ==UserScript==
  2. // @name MiniblogImgPop - 微博浮图 - 兼容新版微博
  3. // @namespace wuuashen
  4. // @icon https://addons.cdn.mozilla.net/img/uploads/addon_icons/337/337281-64.png?modified=1361080128
  5. // @description 微博浮图控件,鼠标移过小图弹出浮动大图的脚本
  6. // @version 3.4.7
  7. // @include http://*qing.weibo.com/*
  8. // @include http://*weibo.com/*
  9. // @include https://*weibo.com/*
  10. // @include http://*t.163.com/*
  11. // @include http://*t.sohu.com/*
  12. // @include http://*t.qq.com/*
  13. // @include http://*t.ifeng.com/*
  14. // @include http://*t.titan24.com/*
  15. // @include http://*t.people.com.cn/*
  16. // @include http://*tianya.cn/*
  17. // @include http://*diandian.com/*
  18. // @include http://*.digu.com/*
  19. // @include http://i.taobao.com/*
  20. // @include http://*t.cntv.cn*
  21. // @include *://*tieba.baidu.com/f*
  22. // @include http://*tieba.baidu.com/i*
  23. // @include http://*xueqiu.com/*
  24. // @include https://*douban.com/*
  25. // @include https://*twitter.com/*
  26.  
  27. // @grant none
  28. // @license MIT
  29. // ==/UserScript==
  30.  
  31. // @date 2022.10.25
  32.  
  33.  
  34. (function() {
  35.  
  36. // 各微博站点的feed配置
  37. var MIPConfig = {
  38. 'qing.weibo.com':{
  39. feedSelector:'.imgZoomIn',
  40. sFrag :'',
  41. bFrag :''
  42. },
  43. 's.weibo.com':{
  44. feedSelector:'[action-type="fl_pics"]',
  45. sFrag :'thumb150',
  46. bFrag :'large'
  47. },
  48. 'q.weibo.com':{
  49. feedSelector:'.bigcursor',
  50. sFrag :'thumbnail',
  51. bFrag :'large'
  52. },
  53. 'weibo.com':{
  54. // feedSelector:'.bigcursor, .feed_img, .media_list img',
  55. feedSelector: '.woo-picture-main',
  56. sFrag :['thumb180', 'thumb150', 'orj480', 'orj360', 'thumbnail', 'square'],
  57. bFrag :['mw690', 'mw690', 'mw690', 'mw690', 'bmiddle', 'bmiddle']
  58. },
  59. 't.sohu.com':{
  60. feedSelector:'.pic',
  61. sFrag :['/f_','_1.jpg'],
  62. bFrag :['/m_','_0.jpg']
  63. },
  64. 't.163.com':{
  65. feedSelector:'.tweet-preview-pic',
  66. sFrag :['w=140&h=140', '&gif=1'],
  67. bFrag :['w=440', '&gif=0']
  68. },
  69. 't.qq.com':{
  70. feedSelector:'.pic img:not(.large)',
  71. sFrag :['/160', '/120'],
  72. bFrag :['/460', '/460']
  73. },
  74. 't.titan24.com':{
  75. feedSelector:'.imgBig',
  76. sFrag :'_thumbnail',
  77. bFrag :'_middle'
  78. },
  79. 't.people.com.cn':{
  80. feedSelector:'.list_s_pic img',
  81. sFrag:'/s_',
  82. bFrag:'/b_'
  83. },
  84. 't.ifeng.com':{
  85. feedSelector:'.zoom_in_image img',
  86. sFrag :'/128x160_',
  87. bFrag :'/520x0_'
  88. },
  89. 'www.tianya.cn':{
  90. feedSelector:'.pic-zoomin',
  91. bigSrc :'_middlepic',
  92. sFrag :'small',
  93. bFrag :'middle'
  94. },
  95. 'diandian.com':{
  96. feedSelector:'.feed-img',
  97. bigSrc :'imgsrc'
  98. },
  99. 'digu.com':{
  100. feedSelector:'.picture',
  101. sFrag :'_100x75',
  102. bFrag :'_640x480'
  103. },
  104. 't.cntv.cn':{
  105. feedSelector:'.zoom-move',
  106. sFrag :'/thumbnail',
  107. bFrag :'/bmiddle'
  108. },
  109. 'i.taobao.com':{
  110. feedSelector:'.thumb-image',
  111. sFrag :'_160x160',
  112. bFrag :'_450x10000'
  113. },
  114. 'tieba.baidu.com/f':{
  115. feedSelector:'.threadlist_media li',
  116. bigSrc: 'bpic'
  117. },
  118. 'tieba.baidu.com/i':{
  119. feedSelector:'.feat_img',
  120. bigSrc: 'data-field'
  121. },
  122. 'xueqiu.com':{
  123. feedSelector:'.expandable > img',
  124. sFrag :'!thumb',
  125. bFrag :'!custom'
  126. },
  127. 'douban.com':{
  128. feedSelector:'img',
  129. sFrag :'median',
  130. bFrag :'raw'
  131. },
  132. 'twitter.com':{
  133. feedSelector:'[aria-label][data-testid="tweetPhoto"]',
  134. sFrag :['900x900', 'small', '360x360', '240x240'],
  135. // small, medium, large, 4096x4096, orig
  136. bFrag :['medium', 'medium', 'medium', '4096x4096']
  137. },
  138. 'gelbooru.com':{
  139. feedSelector:'article.thumbnail-preview',
  140. sFrag :['thumbnails/', 'thumbnail_'],
  141. bFrag :['/images/', '']
  142. },
  143. };
  144.  
  145. // 居中显示的图片对象
  146. var PopImg = {
  147.  
  148. show: function(e) {
  149. this.allowMove = false;
  150. // fix firefox 22 beta 1
  151. this._hideTimer && window.clearTimeout(this._hideTimer);
  152.  
  153. var that = this;
  154. var smallImg = MiniblogImgPop.smallImg;
  155. var src = this.getBigImgsrc(smallImg);
  156. this.img.src = src;
  157. this.imgWidth = 500;
  158.  
  159. imgReady(src, function() {
  160. that.imgWidth = this.width;
  161. that.layoutImg(e);
  162.  
  163. that.img.style.opacity = 1;
  164. that.img.style.visibility = 'visible';
  165. that.img.style.marginTop = '-15px';
  166. Mask.show(e);
  167.  
  168. // 换算图片显示高度
  169. // 1. 宽度超过 500 时,高度要等比例压缩
  170. // 2. 加上边框高度
  171. var imgDisplayHeight;
  172. if (this.width > 500) {
  173. imgDisplayHeight = (this.height + 14) * 500 / this.width;
  174. } else {
  175. imgDisplayHeight = this.height + 14;
  176. }
  177.  
  178. if (window.innerHeight > imgDisplayHeight) {
  179. var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
  180. that.img.style.top = (scrollTop + (window.innerHeight - imgDisplayHeight)/2 + 15 - 160) + 'px';
  181. that.allowMove = false;
  182. } else {
  183. that.allowMove = true;
  184. that.move(e);
  185. }
  186. });
  187. },
  188.  
  189. // 设置大图的宽度与位置
  190. layoutImg: function(e) {
  191. var pos = offset(MiniblogImgPop.smallImg);
  192. var left = pos.x + pos.width + 30;
  193. var width = Math.min(this.imgWidth, 1000);
  194. // 如果小图右边放不下
  195. if (left + width > window.innerWidth) {
  196. left = pos.x - width - 30;
  197. // 如果左边也放不下
  198. if (left < 0) {
  199. // 根据鼠标位置,选择空间大的一侧放置
  200. if (e.pageX > window.innerWidth / 2) {
  201. // 放置在左边
  202. width = Math.min(width, e.pageX - 30);
  203. left = 0;
  204. } else {
  205. // 放置在右边
  206. width = Math.min(width, window.innerWidth - e.pageX - 30);
  207. left = window.innerWidth - width;
  208. }
  209. }
  210. }
  211. this.img.style.width = width + 'px';
  212. this.img.style.left = left + 'px';
  213. },
  214.  
  215. hide: function() {
  216. var that = this;
  217. this.img.style.opacity = 0;
  218. this.img.style.marginTop = '0px';
  219.  
  220. Mask.hide();
  221. this.shown = false;
  222.  
  223. this._hideTimer = window.setTimeout(function() {
  224. that.img.src = '';
  225. that.img.style.visibility = 'hidden';
  226. }, 200);
  227. },
  228.  
  229. init: function() {
  230. var node = document.createElement('img');
  231. node.id = 'miniblogImgPop';
  232. document.body.appendChild(node);
  233. this.img = node;
  234.  
  235. Mask.init();
  236. },
  237.  
  238. move: function(e) {
  239. this.layoutImg(e); // 重新计算大图宽度与位置
  240. if (!this.allowMove) {
  241. return;
  242. }
  243. Mask.move(e);
  244. // 根据 Mask 的位置算出大图的位置
  245. var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
  246. this.img.style.top = (scrollTop - Mask.top * Mask.scale) + 14 + 'px';
  247. },
  248.  
  249. getBigImgsrc: function(obj) {
  250. var tempimgs, tempimg, imgsrc, i, l,
  251. sname = MiniblogImgPop.sitename,
  252. config = MiniblogImgPop.config;
  253. if (obj.tagName === 'IMG' || obj.tagName === 'img') {
  254. tempimg = obj;
  255. } else {
  256. tempimgs = obj.getElementsByTagName('IMG');
  257. if (!tempimgs || tempimgs.length === 0) {
  258. throw 'cant found the img node.';
  259. }
  260. else{
  261. tempimg = tempimgs[0];
  262. }
  263. }
  264.  
  265. //针对使用额外属性保存大图地址的网站
  266. if (config.bigSrc) {
  267. return tempimg.getAttribute(config.bigSrc) || 'javascript:;';
  268. }
  269.  
  270. //一般处理
  271. imgsrc = tempimg.getAttribute('src');
  272. //console.info(imgsrc);
  273. imgsrc = decodeURIComponent(imgsrc);
  274. if (typeof config.sFrag === 'object') {
  275. for(i=0, l=config.sFrag.length; i<l; i++) {
  276. imgsrc = imgsrc.replace(config.sFrag[i], config.bFrag[i]);
  277. }
  278. }
  279. else{
  280. imgsrc = imgsrc.replace(config.sFrag, config.bFrag);
  281. }
  282. return imgsrc;
  283. }
  284.  
  285. };
  286.  
  287. // 图片上遮罩的阴影
  288. var Mask = {
  289. show: function(e) {
  290. var smallImg = MiniblogImgPop.smallImg,
  291. bigImg = PopImg.img;
  292.  
  293. this.sOffset = offset(smallImg);
  294.  
  295. // 表示放大的倍数
  296. this.scale = (bigImg.height + 14) * 1.0 / this.sOffset.height;
  297.  
  298. // 计算出bar的高度
  299. if (window.innerHeight < bigImg.height) {
  300. this.height = parseInt(window.innerHeight/this.scale, 10);
  301. } else {
  302. this.height = this.sOffset.height;
  303. }
  304.  
  305. // 计算bar的Top值可以允许的范围
  306. this.range = this.sOffset.height - this.height;
  307.  
  308. // 计算 mask 的位置
  309. this.nodes[0].style.left = this.sOffset.x + 'px';
  310. this.nodes[0].style.width = this.sOffset.width + 'px';
  311. this.nodes[0].style.top = this.sOffset.y + 'px';
  312. this.nodes[1].style.left = this.sOffset.x + 'px';
  313. this.nodes[1].style.width = this.sOffset.width + 'px';
  314. this.nodes[1].style.bottom = (window.innerHeight - this.sOffset.y - this.sOffset.height) + 'px';
  315. this.move(e);
  316.  
  317. this.nodes[0].style.opacity = 0.7;
  318. this.nodes[1].style.opacity = 0.7;
  319. },
  320. hide: function() {
  321. this.nodes[0].style.opacity = 0;
  322. this.nodes[1].style.opacity = 0;
  323. this.nodes[0].style.height = 0;
  324. this.nodes[1].style.height = 0;
  325. },
  326. move: function(e) {
  327. // 计算鼠标相对于元素的位置
  328. var x = e.pageX - this.sOffset.x,
  329. y = e.pageY - this.sOffset.y;
  330.  
  331. // 计算bar的top值
  332. var top = y - this.height/2;
  333. top = top < 0 ? 0 : top;
  334. top = top > this.range ? this.range : top;
  335. this.top = top;
  336.  
  337. this.nodes[0].style.height = top + 'px';
  338. this.nodes[1].style.height = (this.sOffset.height - top - this.height) + 'px';
  339. },
  340. init: function() {
  341. var node1 = document.createElement('div');
  342. node1.className = 'miniblogImgPop-mask';
  343. document.body.appendChild(node1);
  344. var node2 = document.createElement('div');
  345. node2.className = 'miniblogImgPop-mask';
  346. document.body.appendChild(node2);
  347. this.nodes = [node1, node2];
  348. }
  349. };
  350.  
  351. var MiniblogImgPop = {
  352.  
  353. preloadImg: function() {
  354. var that = this;
  355. window.setTimeout(function() {
  356. var nodes = $(that.config.feedSelector);
  357. for (var i=0; i<nodes.length; i++) {
  358. var preloadImg = new Image();
  359. preloadImg.src = PopImg.getBigImgsrc(nodes[i]);
  360. }
  361. }, 1500);
  362. },
  363.  
  364. prepare: function() {
  365. this.sitename = this._getSiteName();
  366. this.config = MIPConfig[this.sitename];
  367. console.log(MIPConfig[this.sitename], 123)
  368. },
  369.  
  370. addImgsEventListener: function() {
  371. var that = this;
  372. delegate(document.body, 'mouseover', function(e, node) {
  373. that.smallImg = node;
  374. node.style.opacity = 0.84;
  375. PopImg.show(e);
  376. }, this.config.feedSelector);
  377. delegate(document.body, 'mouseout', function(e, node) {
  378. node.style.opacity = '';
  379. PopImg.hide();
  380. }, this.config.feedSelector);
  381.  
  382. delegate(document.body, 'mousemove', function(e) {
  383. PopImg.move(e);
  384. }, this.config.feedSelector);
  385.  
  386. document.addEventListener('keydown', function(e) {
  387. if (e.key === 'Escape') {
  388. if(document.getElementById('miniblogImgPop').style.visibility === 'visible')
  389. PopImg.hide();
  390. }
  391. });
  392. },
  393.  
  394. // 获得当前站点名
  395. _getSiteName: function() {
  396. var i, each;
  397. for(each in MIPConfig) {
  398. if (location.href.indexOf(each) != -1) {
  399. return each;
  400. }
  401. }
  402. return '';
  403. },
  404.  
  405. init: function() {
  406. // 初始化两个节点
  407. PopImg.init();
  408. // 准备必要的数据
  409. this.prepare();
  410. // 绑定imgs hover事件
  411. this.addImgsEventListener();
  412. // 预加载大图
  413. this.preloadImg();
  414. }
  415.  
  416. };
  417.  
  418. // 启动
  419. MiniblogImgPop.init();
  420.  
  421.  
  422. // Helpers
  423. // ---
  424.  
  425. function $(selector) {
  426. return document.querySelectorAll(selector);
  427. }
  428.  
  429. function offset(source) {
  430. var pt = {
  431. x:0,
  432. y:0,
  433. width:source.offsetWidth,
  434. height:source.offsetHeight
  435. };
  436. do {
  437. pt.x += source.offsetLeft;
  438. pt.y += source.offsetTop;
  439. source = source.offsetParent;
  440. } while (source);
  441. return pt;
  442. }
  443.  
  444. function delegate(el, eventType, handler, selector) {
  445. el = el || document;
  446. el.addEventListener(eventType, function(e) {
  447. var node = getHandlerNode(e, selector, el);
  448. node && handler.call(el, e, node);
  449. }, false);
  450.  
  451. function getHandlerNode(e, selector, el) {
  452. //返回我们handler需要的参数
  453. var nodes;
  454. el = el || document;
  455. if (e && e.target && selector) {
  456. nodes = el.querySelectorAll(selector);
  457. for(i=0; i<nodes.length; i++) {
  458. if (e.target == nodes[i] || isInDomChain(e.target, nodes[i], el)) {
  459. return nodes[i];
  460. }
  461. }
  462. return false;
  463. }
  464. }
  465.  
  466. function isInDomChain(target, parent, ancestor, maxDepth) {
  467. ancestor = ancestor || null;
  468. maxDepth = maxDepth || 100;
  469.  
  470. if (target == ancestor) {
  471. return false;
  472. }
  473. if (target == parent) {
  474. return true;
  475. }
  476. var i = 0;//防止过多嵌套
  477. while (target != ancestor && target !== null && (i++ < maxDepth)) {
  478. target = target.parentNode;
  479. if (target == parent) {
  480. return true;
  481. }
  482. }
  483. return false;
  484. }
  485. }
  486.  
  487. /**
  488. * 图片头数据加载就绪事件 - 更快获取图片尺寸
  489. * @version 2011.05.27
  490. * @author TangBin
  491. * @see http://www.planeart.cn/?p=1121
  492. * @param {String} 图片路径
  493. * @param {Function} 尺寸就绪
  494. * @param {Function} 加载完毕 (可选)
  495. * @param {Function} 加载错误 (可选)
  496. * @example imgReady('http://www.google.com.hk/intl/zh-CN/images/logo_cn.png', function () {
  497. alert('size ready: width=' + this.width + '; height=' + this.height);
  498. });
  499. */
  500. var imgReady = (function () {
  501. var list = [], intervalId = null,
  502.  
  503. // 用来执行队列
  504. tick = function () {
  505. var i = 0;
  506. for (; i < list.length; i++) {
  507. list[i].end ? list.splice(i--, 1) : list[i]();
  508. }
  509. !list.length && stop();
  510. },
  511.  
  512. // 停止所有定时器队列
  513. stop = function () {
  514. window.clearInterval(intervalId);
  515. intervalId = null;
  516. };
  517.  
  518. return function (url, ready, load, error) {
  519. var onready, width, height, newWidth, newHeight,
  520. img = new Image();
  521.  
  522. img.src = url;
  523.  
  524. // 如果图片被缓存,则直接返回缓存数据
  525. if (img.complete) {
  526. ready.call(img);
  527. load && load.call(img);
  528. return;
  529. }
  530.  
  531. width = img.width;
  532. height = img.height;
  533.  
  534. // 加载错误后的事件
  535. img.onerror = function () {
  536. error && error.call(img);
  537. onready.end = true;
  538. img = img.onload = img.onerror = null;
  539. };
  540.  
  541. // 图片尺寸就绪
  542. onready = function () {
  543. newWidth = img.width;
  544. newHeight = img.height;
  545. if (newWidth !== width || newHeight !== height ||
  546. // 如果图片已经在其他地方加载可使用面积检测
  547. newWidth * newHeight > 1024
  548. ) {
  549. ready.call(img);
  550. onready.end = true;
  551. }
  552. };
  553. onready();
  554.  
  555. // 完全加载完毕的事件
  556. img.onload = function () {
  557. // onload在定时器时间差范围内可能比onready快
  558. // 这里进行检查并保证onready优先执行
  559. !onready.end && onready();
  560.  
  561. load && load.call(img);
  562.  
  563. // IE gif动画会循环执行onload,置空onload即可
  564. img = img.onload = img.onerror = null;
  565. };
  566.  
  567. // 加入队列中定期执行
  568. if (!onready.end) {
  569. list.push(onready);
  570. // 无论何时只允许出现一个定时器,减少浏览器性能损耗
  571. if (intervalId === null) intervalId = setInterval(tick, 40);
  572. }
  573. };
  574. })();
  575.  
  576. // GM_addStyle function is not existed in chrome 27
  577. var GM_addStyle = GM_addStyle || function(css) {
  578. var style = document.createElement("style");
  579. style.type = "text/css";
  580. style.appendChild(document.createTextNode(css));
  581. document.getElementsByTagName("head")[0].appendChild(style);
  582. };
  583.  
  584. // 增加自定义样式
  585. GM_addStyle("\
  586. #miniblogImgPop {\
  587. border: 7px solid rgba(255,255,255,1);\
  588. box-shadow: 0 1px 30px rgba(0, 0, 0, 0.75), 0 0 40px rgba(0, 0, 0, 0.25) inset;\
  589. z-index: 12345;\
  590. opacity: 0;\
  591. margin-top: 0;\
  592. position: absolute;\
  593. visibility: hidden;\
  594. max-width: 800px;\
  595. transition: opacity 0.2s ease-out 0s, margin-top 0.2s ease-out 0s;\
  596. }\
  597. ");
  598.  
  599. // 增加自定义样式
  600. GM_addStyle("\
  601. .miniblogImgPop-mask {\
  602. background: rgb(0, 0, 0);\
  603. z-index: 999;\
  604. position: absolute;\
  605. transition: opacity 0.4s ease-out 0;\
  606. }\
  607. ");
  608.  
  609. })();