您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
图片预览:通过双击按键,把图片固定在页面上,边读文字边看图,同时支持缩放、移动功能
当前为
// ==UserScript== // @name 边读边看图 // @namespace http://tampermonkey.net/ // @version 0.3.1 // @description 图片预览:通过双击按键,把图片固定在页面上,边读文字边看图,同时支持缩放、移动功能 // @author Enjoy // @icon https://foruda.gitee.com/avatar/1671100286067517749/4867929_enjoy_li_1671100285.png!avatar60 // @match *://*/* // @exclude *hrwork* // @exclude *zhaopinyun* // @exclude *localhost* // @exclude *127.0.0.1* // @grant GM_addElement // @grant GM_setClipboard // @license GPL License // ==/UserScript== (()=>{// webpackBootstrap /******/"use strict";// CONCATENATED MODULE: ./src/ImgPreview.js // 函数文档 https://www.tampermonkey.net/documentation.php#api:GM_addElement // @match *://mp.weixin.qq.com/s/* !function(){!// CONCATENATED MODULE: ./tools/GM.js // 函数文档 https://www.tampermonkey.net/documentation.php#api:GM_addElement function(t,e){var o=document.createElement(t);for(const i in e)Object.hasOwnProperty.call(e,i)&&"el"!==i&&(o[i]=e[i]);let{el:s=document.documentElement}=e;(s="string"==typeof s?document.querySelector(s):s).appendChild(o)}("style",{innerHTML:".pages_skin_pc .rich_media_area_primary_inner{margin-left:initial;}",id:"img_preview_style"});class a{constructor(t,e,o){this.state=Object.assign({},this.state,this.mergeOptions(t,e,o));t=this.appendImg(o);return this.state.cloneEl=t,this.fixPosition(t),this.addEvents(t),t}state={scale:1,offset:{left:0,top:0},origin:"center",initialData:{offset:{},origin:"center",scale:1},startPoint:{x:0,y:0}, // 记录初始触摸点位 isTouching:!1, // 标记是否正在移动 isMove:!1, // 正在移动中,与点击做区别 touches:new Map, // 触摸点数组 lastDistance:0,lastScale:1, // 记录下最后的缩放值 scaleOrigin:{x:0,y:0}};mergeOptions(t,e,o){var{innerWidth:s,innerHeight:i}=window,{offsetWidth:n,offsetHeight:a}=e,{top:r,left:l}=e.getBoundingClientRect();return{shadowRoot:t,originalEl:e,src:o,winWidth:s,winHeight:i,offsetWidth:n,offsetHeight:a,top:r,left:l,maskContent:t.querySelector(".modal")}} /** @描述 添加图片 */appendImg(t){var e=document.createElement("img");return e.src=t,this.state.maskContent.appendChild(e),e} /** @描述 添加监听事件 */addEvents(e,t=["dblclick","mousewheel","pointerdown","pointerup","pointermove","pointercancel"]){let o=this;t.forEach(t=>{"mousewheel"===t?e.addEventListener("mousewheel",o["on"+t],{passive:!1}):e.addEventListener(t,o["on"+t])})} /** @描述 双击事件 */ondblclick=t=>{t.preventDefault();let e=this,o=e.state;setTimeout(()=>{o.isMove?o.isMove=!1:(e.changeStyle(o.cloneEl,["transition: all .3s",`left: ${o.left}px`,`top: ${o.top}px`,"transform: translate(0,0)",`width: ${o.offsetWidth}px`]),setTimeout(()=>{o.maskContent.removeChild(o.cloneEl), // originalEl.style.opacity = 1 o.cloneEl.removeEventListener("dblclick",e.ondblclick)},300))},280)} /** @描述 指针按下事件*/;onpointerdown=t=>{t.preventDefault();var e=this.state;e.touches.set(t.pointerId,t), // TODO: 点击存入触摸点 e.isTouching=!0,e.startPoint={x:t.clientX,y:t.clientY},2===e.touches.size&&( // TODO: 判断双指触摸,并立即记录初始数据 e.lastDistance=this.getDistance(),e.lastScale=e.scale)} /** @描述 滚轮缩放 */;onmousewheel=t=>{var e;t.preventDefault(),t.deltaY&&((e=this.state).origin=`${t.offsetX}px ${t.offsetY}px`, // 缩放执行 t.deltaY<0? // 放大 e.scale+=.1:0<t.deltaY&&.2<=e.scale&&(e.scale-=.1),e.scale<e.initialData.scale&&this.reduction(),e.offset=this.getOffsetPageCenter(t.offsetX,t.offsetY),this.changeStyle(e.cloneEl,["transition: all .15s","transform-origin: "+e.origin,`transform: translate(${e.offset.left+"px"}, ${e.offset.top+"px"}) scale(${e.scale})`]))} /** @描述 获取中心改变的偏差 */;getOffsetPageCenter(t=0,e=0){var o=this.state,s=Array.from(o.touches),i=(2===s.length&&(i=s[0][1],s=s[1][1],t=(i.offsetX+s.offsetX)/2,e=(i.offsetY+s.offsetY)/2),o.origin=t+`px ${e}px`,(o.scale-1)*(t-o.scaleOrigin.x)+o.offset.left),s=(o.scale-1)*(e-o.scaleOrigin.y)+o.offset.top;return o.scaleOrigin={x:t,y:e},{left:i,top:s}} /** @描述 获取距离*/getDistance(){var t,e=Array.from(this.state.touches);return e.length<2?0:(t=e[0][1],e=e[1][1],Math.hypot(e.x-t.x,e.y-t.y))} /** @描述 修改样式,减少回流重绘*/changeStyle(t,e){var o=t.style.cssText.split(";");o.pop(),t.style.cssText=o.concat(e).join(";")+";"} /** @描述 还原记录,用于边界处理 */reduction(){let t=this,e=t.state;t.timer&&clearTimeout(t.timer),t.timer=setTimeout(()=>{t.changeStyle(e.cloneEl,[`transform: translate(${e.offset.left+"px"}, ${e.offset.top+"px"}) scale(${e.scale})`,"transform-origin: "+e.origin])},300)} /** @描述 松开指针 事件 */onpointerup=t=>{t.preventDefault();let e=this.state;e.touches.delete(t.pointerId), // TODO: 抬起移除触摸点 e.touches.size<=0?e.isTouching=!1:(t=Array.from(e.touches), // 更新点位 e.startPoint={x:t[0][1].clientX,y:t[0][1].clientY}),setTimeout(()=>{e.isMove=!1},300)} /** @描述 指针移动事件 */;onpointermove=t=>{t.preventDefault();var e=this,o=e.state;o.isTouching&&(o.isMove=!0,o.touches.size<2?( // 单指滑动 o.offset={left:o.offset.left+(t.clientX-o.startPoint.x),top:o.offset.top+(t.clientY-o.startPoint.y)},e.changeStyle(o.cloneEl,["transition: all 0s",`transform: translate(${o.offset.left+"px"}, ${o.offset.top+"px"}) scale(${o.scale})`,"transform-origin: "+origin]), // 更新点位 o.startPoint={x:t.clientX,y:t.clientY}):( // 双指缩放 o.touches.set(t.pointerId,t),t=e.getDistance()/o.lastDistance,o.scale=t*o.lastScale,o.offset=e.getOffsetPageCenter(),o.scale<o.initialData.scale&&e.reduction(),e.changeStyle(o.cloneEl,["transition: all 0s",`transform: translate(${o.offset.left+"px"}, ${o.offset.top+"px"}) scale(${o.scale})`,"transform-origin: "+o.origin])))} /** @描述 取消指针事件 */;onpointercancel=t=>{t.preventDefault(),this.state.touches.clear()} /** @描述 移动图片到屏幕中心位置 */;fixPosition(t){let e=this;var o=e.state /** @描述 原图片 中心点 */;const s=o.offsetWidth/2+o.left /** @描述 页面 中心点 */,i=o.offsetHeight/2+o.top,n=o.winWidth/2 /** @描述 新建图片的定位点:通过原图片中心点到页面中心点的 偏移量*/,a=o.winHeight/2,r={left:n-s+o.left,top:a-i+o.top} /** @描述 放大后的 */;var l=this.adaptScale();const c={left:(l-1)*o.offsetWidth/2,top:(l-1)*o.offsetHeight/2};this.changeStyle(t,[`left: ${o.left}px`,`top: ${o.top}px`,"transition: all 0.3s","width: "+o.offsetWidth*l+"px",`transform: translate(${r.left-o.left-c.left}px, ${r.top-o.top-c.top}px)`]) /** @描述 消除偏差:让图片相对于window 0 0定位,通过translate设置中心点重合*/,setTimeout(()=>{e.changeStyle(t,["transition: all 0s","left: 0","top: 0",`transform: translate(${r.left-c.left}px, ${r.top-c.top}px)`]),e.state.offset={left:r.left-c.left,top:r.top-c.top}, // 记录值 e.record()},300)} /** @描述 记录初始化数据 */record(){var t=this.state;t.initialData=Object.assign({},{offset:t.offset,origin:t.origin,scale:t.scale})} /** @描述 计算自适应屏幕的缩放 */adaptScale(){var{winWidth:t,winHeight:e,originalEl:o}=this.state,{offsetWidth:o,offsetHeight:s}=o;let i=t/o;return i=s*i>e-80?(e-80)/s:i}}new class{constructor(t={}){return this.state=this.mergeOptions(t),this.shadowRoot=this.createShadowRoot(),this.onPreviwerEvent(),this.shadowRoot} /** @描述 状态 */state=null;shadowRoot=null /** @描述 创建 shadowRoot */;createShadowRoot(t="#imgPreview"){let e=document.querySelector(""+t);return e||((e=document.createElement("div")).setAttribute("id",t.replace(/[.#]/g,"")),e.setAttribute("style","width:0;height:0"),document.documentElement.appendChild(e)),e.shadowRoot||((t=document.createElement("div")).classList.add("modal"),t.appendChild(this.createStyle(this.state)), // 添加在body下 e.attachShadow({mode:"open"}),e.shadowRoot.appendChild(t)),e.shadowRoot} /** @描述 合并选项 */mergeOptions(t){var e={};return Object.assign(e,{contentSelector:"body",selector:"img",showRootSelector:"#img_preview",backgroundColor:"rgba(0,0,0,0)",extraStyle:""},t),e} /** @描述 创建shadowbox中的样式 */createStyle({contentSelector:t,selector:e,backgroundColor:o,extraStyle:s}){var i=document.createElement("style");return i.innerHTML=t+` ${e} { cursor: zoom-in; } /* 图片预览 */ .modal { touch-action: none; position: fixed; z-index: 99; top: 0; left: 0; width: 100vw; height: 100vh; background-color: ${o}; user-select: none; pointer-events: none; } .modal>*{ pointer-events: auto; } .modal>img { position: absolute; padding: 0; margin: 0; box-shadow: #09818f 0px 0px 15px 0px; border-radius: 10px; /* transition: all var(--delay_time); */ transform: translateZ(0); } img.active { animation: activeImg 0.5s 4 ease-out forwards; transition: all; } @keyframes activeImg { 0% { box-shadow: #09818f 0px 0px 15px 0px; } 50% { box-shadow: red 0px 0px 50px 0px; } 100% { box-shadow: #09818f 0px 0px 15px 0px; } } ${s} `,i} /** @描述 预览操作 */onPreviwerEvent(){let s=this,{contentSelector:t,selector:i}=s.state,n=document.querySelector(t)||window.document.body;n.addEventListener("dblclick",function(e){let o=e.target.src||window.getComputedStyle(e.target).backgroundImage.match(/^url\("([^\s]+)"\)$/i)?.[1];if(o&&(e.preventDefault(),[...n.querySelectorAll(i)].find(t=>t===e.target))){let t=[...s.shadowRoot.querySelectorAll(i)].find(t=>t.src===o);if(t){if(!t.classList.contains("active"))return void t.classList.add("active");t.remove(),t=null}t|| // originalEl.style.opacity = 0 new a(s.shadowRoot,e.target,o)}})}}({backgroundColor:"rgba(0,0,0,0)"})} /******/()})();