MiniblogImgPop - 微博浮图

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

目前為 2018-02-03 提交的版本,檢視 最新版本

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