img_preview

微信公证号文章中,图片预览

目前為 2023-07-14 提交的版本,檢視 最新版本

// ==UserScript==
// @name         img_preview
// @namespace    http://tampermonkey.net/
// @version      0.0.8
// @description  微信公证号文章中,图片预览
// @author       Enjoy
// @icon         https://foruda.gitee.com/avatar/1671100286067517749/4867929_enjoy_li_1671100285.png!avatar60
// @match        *://mp.weixin.qq.com/s/*
// @grant        GM_addElement
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @license      GPL License
// ==/UserScript==

// 函数文档 https://www.tampermonkey.net/documentation.php#api:GM_addElement

(function () {
  GM_addElement('script',{
    textContent: 'onImgPreview({ onContentSelector: \'.js_content\',selector: \'img.wxw-img\' })\n  function onImgPreview(options = {}) {\n    let defaultOptions = {}\n    let { onContentSelector,selector = \'img\' } = Object({},defaultOptions,options)\n    let scale = 1\n    let offset = { left: 0,top: 0 }\n    let origin = \'center\'\n    let initialData = { offset: {},origin: \'center\',scale: 1 }\n    let startPoint = { x: 0,y: 0 } // 记录初始触摸点位\n    let isTouching = false // 标记是否正在移动\n    let isMove = false // 正在移动中,与点击做区别\n    let touches = new Map() // 触摸点数组\n    let lastDistance = 0\n    let lastScale = 1 // 记录下最后的缩放值\n    let scaleOrigin = { x: 0,y: 0,}\n\n    const { innerWidth: winWidth,innerHeight: winHeight } = window\n    // let cloneEl = null\n    let cloneEl = document.createElement(\'img\')\n    let originalEl = null\n\n    let imgDoms = [...document.querySelectorAll(selector)]\n    let onContent = onContentSelector ? document.querySelector(onContentSelector) : window\n    onContent.addEventListener(\'click\',function (e) {\n      e.preventDefault()\n      if (imgDoms.find(item => item === e.target)) {\n        originalEl = e.target\n        // cloneEl = originalEl.cloneNode(true)\n        cloneEl.src = originalEl.src\n        originalEl.style.opacity = 0\n        openPreview()\n      }\n    })\n\n    function createStyle() {\n\n      const style = document.createElement(\'style\')\n      style.innerHTML = `/* 图片预览 */\n  .modal {\n    touch-action: none;\n    position: fixed;\n    z-index: 99;\n    top: 0;\n    left: 0;\n    width: 100vw;\n    height: 100vh;\n    background-color: rgba(0, 0, 0, 0.75);\n    user-select: none;\n  }\n\n  .modal>img {\n    position: absolute;\n    padding: 0;\n    margin: 0;\n    /* transition: all var(--delay_time); */\n    transform: translateZ(0);\n  }`\n      return style\n    }\n    function openPreview() {\n      scale = 1\n      const { offsetWidth,offsetHeight } = originalEl\n      const { top,left } = originalEl.getBoundingClientRect()\n      // 创建蒙层\n      const mask = document.createElement(\'div\')\n      mask.classList.add(\'modal\')\n      mask.appendChild(createStyle())\n      // 添加在body下\n      document.body.appendChild(mask)\n      // 注册(不可用)事件\n      mask.addEventListener("click",clickFunc)\n      mask.addEventListener(\'mousewheel\',zoom,{ passive: false })\n      // 遮罩点击事件\n      function clickFunc() {\n        setTimeout(() => {\n          if (isMove) {\n            isMove = false\n          } else {\n            changeStyle(cloneEl,[\'transition: all .3s\',`left: ${left}px`,`top: ${top}px`,`transform: translate(0,0)`,`width: ${offsetWidth}px`])\n            setTimeout(() => {\n              document.body.removeChild(this)\n              originalEl.style.opacity = 1\n              mask.removeEventListener(\'click\',clickFunc)\n            },300)\n          }\n        },280)\n      }\n      // 添加图片\n      changeStyle(cloneEl,[`left: ${left}px`,`top: ${top}px`])\n      mask.appendChild(cloneEl)\n      // 移动图片到屏幕中心位置\n      const originalCenterPoint = { x: offsetWidth / 2 + left,y: offsetHeight / 2 + top }\n      const winCenterPoint = { x: winWidth / 2,y: winHeight / 2 }\n      const offsetDistance = { left: winCenterPoint.x - originalCenterPoint.x + left,top: winCenterPoint.y - originalCenterPoint.y + top }\n      const diffs = { left: ((adaptScale() - 1) * offsetWidth) / 2,top: ((adaptScale() - 1) * offsetHeight) / 2 }\n      changeStyle(cloneEl,[\'transition: all 0.3s\',`width: ${offsetWidth * adaptScale() + \'px\'}`,`transform: translate(${offsetDistance.left - left - diffs.left}px, ${offsetDistance.top - top - diffs.top}px)`])\n      // 消除偏差\n      setTimeout(() => {\n        changeStyle(cloneEl,[\'transition: all 0s\',`left: 0`,`top: 0`,`transform: translate(${offsetDistance.left - diffs.left}px, ${offsetDistance.top - diffs.top}px)`])\n        offset = { left: offsetDistance.left - diffs.left,top: offsetDistance.top - diffs.top } // 记录值\n        record()\n      },300)\n    }\n\n    // 滚轮缩放\n    const zoom = (event) => {\n      if (!event.deltaY) {\n        return\n      }\n      event.preventDefault()\n      origin = `${event.offsetX}px ${event.offsetY}px`\n      // 缩放执行\n      if (event.deltaY < 0) {\n        scale += 0.1 // 放大\n      } else if (event.deltaY > 0) {\n        scale >= 0.2 && (scale -= 0.1) // 缩小\n      }\n      if (scale < initialData.scale) {\n        reduction()\n      }\n      offset = getOffsetCorrection(event.offsetX,event.offsetY)\n      changeStyle(cloneEl,[\'transition: all .15s\',`transform-origin: ${origin}`,`transform: translate(${offset.left + \'px\'}, ${offset.top + \'px\'}) scale(${scale})`])\n    }\n\n    // 获取中心改变的偏差\n    function getOffsetCorrection(x = 0,y = 0) {\n      const touchArr = Array.from(touches)\n      if (touchArr.length === 2) {\n        const start = touchArr[0][1]\n        const end = touchArr[1][1]\n        x = (start.offsetX + end.offsetX) / 2\n        y = (start.offsetY + end.offsetY) / 2\n      }\n      origin = `${x}px ${y}px`\n      const offsetLeft = (scale - 1) * (x - scaleOrigin.x) + offset.left\n      const offsetTop = (scale - 1) * (y - scaleOrigin.y) + offset.top\n      scaleOrigin = { x,y }\n      return { left: offsetLeft,top: offsetTop }\n    }\n\n    // 操作事件\n    window.addEventListener(\'pointerdown\',function (e) {\n      e.preventDefault()\n      touches.set(e.pointerId,e) // TODO: 点击存入触摸点\n      isTouching = true\n      startPoint = { x: e.clientX,y: e.clientY }\n      if (touches.size === 2) { // TODO: 判断双指触摸,并立即记录初始数据\n        lastDistance = getDistance()\n        lastScale = scale\n      }\n    })\n    window.addEventListener(\'pointerup\',function (e) {\n      touches.delete(e.pointerId) // TODO: 抬起移除触摸点\n      if (touches.size <= 0) {\n        isTouching = false\n      } else {\n        const touchArr = Array.from(touches)\n        // 更新点位\n        startPoint = { x: touchArr[0][1].clientX,y: touchArr[0][1].clientY }\n      }\n      setTimeout(() => {\n        isMove = false\n      },300);\n    })\n    window.addEventListener(\'pointermove\',(e) => {\n      e.preventDefault()\n      if (isTouching) {\n        isMove = true\n        if (touches.size < 2) { // 单指滑动\n          offset = {\n            left: offset.left + (e.clientX - startPoint.x),\n            top: offset.top + (e.clientY - startPoint.y),\n          }\n          changeStyle(cloneEl,[\'transition: all 0s\',`transform: translate(${offset.left + \'px\'}, ${offset.top + \'px\'}) scale(${scale})`,`transform-origin: ${origin}`])\n          // 更新点位\n          startPoint = { x: e.clientX,y: e.clientY }\n        } else {\n          // 双指缩放\n          touches.set(e.pointerId,e)\n          const ratio = getDistance() / lastDistance\n          scale = ratio * lastScale\n          offset = getOffsetCorrection()\n          if (scale < initialData.scale) {\n            reduction()\n          }\n          changeStyle(cloneEl,[\'transition: all 0s\',`transform: translate(${offset.left + \'px\'}, ${offset.top + \'px\'}) scale(${scale})`,`transform-origin: ${origin}`])\n        }\n      }\n    })\n    window.addEventListener(\'pointercancel\',function (e) {\n      touches.clear() // 可能存在特定事件导致中断,真机操作时 pointerup 在某些边界情况下不会生效,所以需要清空\n    })\n\n    // 修改样式,减少回流重绘\n    function changeStyle(el,arr) {\n      const original = el.style.cssText.split(\';\')\n      original.pop()\n      el.style.cssText = original.concat(arr).join(\';\') + \';\'\n    }\n\n    // 计算自适应屏幕的缩放值\n    function adaptScale() {\n      const { offsetWidth: w,offsetHeight: h } = originalEl\n      let scale = 0\n      scale = winWidth / w\n      if (h * scale > winHeight - 80) {\n        scale = (winHeight - 80) / h\n      }\n      return scale\n    }\n\n    // 获取距离\n    function getDistance() {\n      const touchArr = Array.from(touches)\n      if (touchArr.length < 2) {\n        return 0\n      }\n      const start = touchArr[0][1]\n      const end = touchArr[1][1]\n      return Math.hypot(end.x - start.x,end.y - start.y)\n    }\n\n    // 记录初始化数据\n    function record() {\n      initialData = Object.assign({},{ offset,origin,scale })\n    }\n\n    // 还原记录,用于边界处理\n    let timer = null\n    function reduction() {\n      timer && clearTimeout(timer)\n      timer = setTimeout(() => {\n        // offset = initialData.offset\n        // origin = initialData.origin\n        // scale = initialData.scale\n        changeStyle(cloneEl,[`transform: translate(${offset.left + \'px\'}, ${offset.top + \'px\'}) scale(${scale})`,`transform-origin: ${origin}`])\n      },300)\n    }\n  }'
  })
  return
  onImgPreview({ onContentSelector: '.js_content',selector: 'img.wxw-img' })
  function onImgPreview(options = {}) {
    let defaultOptions = {}
    let { onContentSelector,selector = 'img' } = Object({},defaultOptions,options)
    let scale = 1
    let offset = { left: 0,top: 0 }
    let origin = 'center'
    let initialData = { offset: {},origin: 'center',scale: 1 }
    let startPoint = { x: 0,y: 0 } // 记录初始触摸点位
    let isTouching = false // 标记是否正在移动
    let isMove = false // 正在移动中,与点击做区别
    let touches = new Map() // 触摸点数组
    let lastDistance = 0
    let lastScale = 1 // 记录下最后的缩放值
    let scaleOrigin = { x: 0,y: 0,}

    const { innerWidth: winWidth,innerHeight: winHeight } = window
    // let cloneEl = null
    let cloneEl = document.createElement('img')
    let originalEl = null

    let imgDoms = [...document.querySelectorAll(selector)]
    let onContent = onContentSelector ? document.querySelector(onContentSelector) : window
    onContent.addEventListener('click',function (e) {
      e.preventDefault()
      if (imgDoms.find(item => item === e.target)) {
        originalEl = e.target
        // cloneEl = originalEl.cloneNode(true)
        cloneEl.src = originalEl.src
        originalEl.style.opacity = 0
        openPreview()
      }
    })

    function createStyle() {

      const style = document.createElement('style')
      style.innerHTML = `/* 图片预览 */
  .modal {
    touch-action: none;
    position: fixed;
    z-index: 99;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background-color: rgba(0, 0, 0, 0.75);
    user-select: none;
  }

  .modal>img {
    position: absolute;
    padding: 0;
    margin: 0;
    /* transition: all var(--delay_time); */
    transform: translateZ(0);
  }`
      return style
    }
    function openPreview() {
      scale = 1
      const { offsetWidth,offsetHeight } = originalEl
      const { top,left } = originalEl.getBoundingClientRect()
      // 创建蒙层
      const mask = document.createElement('div')
      mask.classList.add('modal')
      mask.appendChild(createStyle())
      // 添加在body下
      document.body.appendChild(mask)
      // 注册(不可用)事件
      mask.addEventListener("click",clickFunc)
      mask.addEventListener('mousewheel',zoom,{ passive: false })
      // 遮罩点击事件
      function clickFunc() {
        setTimeout(() => {
          if (isMove) {
            isMove = false
          } else {
            changeStyle(cloneEl,['transition: all .3s',`left: ${left}px`,`top: ${top}px`,`transform: translate(0,0)`,`width: ${offsetWidth}px`])
            setTimeout(() => {
              document.body.removeChild(this)
              originalEl.style.opacity = 1
              mask.removeEventListener('click',clickFunc)
            },300)
          }
        },280)
      }
      // 添加图片
      changeStyle(cloneEl,[`left: ${left}px`,`top: ${top}px`])
      mask.appendChild(cloneEl)
      // 移动图片到屏幕中心位置
      const originalCenterPoint = { x: offsetWidth / 2 + left,y: offsetHeight / 2 + top }
      const winCenterPoint = { x: winWidth / 2,y: winHeight / 2 }
      const offsetDistance = { left: winCenterPoint.x - originalCenterPoint.x + left,top: winCenterPoint.y - originalCenterPoint.y + top }
      const diffs = { left: ((adaptScale() - 1) * offsetWidth) / 2,top: ((adaptScale() - 1) * offsetHeight) / 2 }
      changeStyle(cloneEl,['transition: all 0.3s',`width: ${offsetWidth * adaptScale() + 'px'}`,`transform: translate(${offsetDistance.left - left - diffs.left}px, ${offsetDistance.top - top - diffs.top}px)`])
      // 消除偏差
      setTimeout(() => {
        changeStyle(cloneEl,['transition: all 0s',`left: 0`,`top: 0`,`transform: translate(${offsetDistance.left - diffs.left}px, ${offsetDistance.top - diffs.top}px)`])
        offset = { left: offsetDistance.left - diffs.left,top: offsetDistance.top - diffs.top } // 记录值
        record()
      },300)
    }

    // 滚轮缩放
    const zoom = (event) => {
      if (!event.deltaY) {
        return
      }
      event.preventDefault()
      origin = `${event.offsetX}px ${event.offsetY}px`
      // 缩放执行
      if (event.deltaY < 0) {
        scale += 0.1 // 放大
      } else if (event.deltaY > 0) {
        scale >= 0.2 && (scale -= 0.1) // 缩小
      }
      if (scale < initialData.scale) {
        reduction()
      }
      offset = getOffsetCorrection(event.offsetX,event.offsetY)
      changeStyle(cloneEl,['transition: all .15s',`transform-origin: ${origin}`,`transform: translate(${offset.left + 'px'}, ${offset.top + 'px'}) scale(${scale})`])
    }

    // 获取中心改变的偏差
    function getOffsetCorrection(x = 0,y = 0) {
      const touchArr = Array.from(touches)
      if (touchArr.length === 2) {
        const start = touchArr[0][1]
        const end = touchArr[1][1]
        x = (start.offsetX + end.offsetX) / 2
        y = (start.offsetY + end.offsetY) / 2
      }
      origin = `${x}px ${y}px`
      const offsetLeft = (scale - 1) * (x - scaleOrigin.x) + offset.left
      const offsetTop = (scale - 1) * (y - scaleOrigin.y) + offset.top
      scaleOrigin = { x,y }
      return { left: offsetLeft,top: offsetTop }
    }

    // 操作事件
    window.addEventListener('pointerdown',function (e) {
      e.preventDefault()
      touches.set(e.pointerId,e) // TODO: 点击存入触摸点
      isTouching = true
      startPoint = { x: e.clientX,y: e.clientY }
      if (touches.size === 2) { // TODO: 判断双指触摸,并立即记录初始数据
        lastDistance = getDistance()
        lastScale = scale
      }
    })
    window.addEventListener('pointerup',function (e) {
      touches.delete(e.pointerId) // TODO: 抬起移除触摸点
      if (touches.size <= 0) {
        isTouching = false
      } else {
        const touchArr = Array.from(touches)
        // 更新点位
        startPoint = { x: touchArr[0][1].clientX,y: touchArr[0][1].clientY }
      }
      setTimeout(() => {
        isMove = false
      },300);
    })
    window.addEventListener('pointermove',(e) => {
      e.preventDefault()
      if (isTouching) {
        isMove = true
        if (touches.size < 2) { // 单指滑动
          offset = {
            left: offset.left + (e.clientX - startPoint.x),
            top: offset.top + (e.clientY - startPoint.y),
          }
          changeStyle(cloneEl,['transition: all 0s',`transform: translate(${offset.left + 'px'}, ${offset.top + 'px'}) scale(${scale})`,`transform-origin: ${origin}`])
          // 更新点位
          startPoint = { x: e.clientX,y: e.clientY }
        } else {
          // 双指缩放
          touches.set(e.pointerId,e)
          const ratio = getDistance() / lastDistance
          scale = ratio * lastScale
          offset = getOffsetCorrection()
          if (scale < initialData.scale) {
            reduction()
          }
          changeStyle(cloneEl,['transition: all 0s',`transform: translate(${offset.left + 'px'}, ${offset.top + 'px'}) scale(${scale})`,`transform-origin: ${origin}`])
        }
      }
    })
    window.addEventListener('pointercancel',function (e) {
      touches.clear() // 可能存在特定事件导致中断,真机操作时 pointerup 在某些边界情况下不会生效,所以需要清空
    })

    // 修改样式,减少回流重绘
    function changeStyle(el,arr) {
      const original = el.style.cssText.split(';')
      original.pop()
      el.style.cssText = original.concat(arr).join(';') + ';'
    }

    // 计算自适应屏幕的缩放值
    function adaptScale() {
      const { offsetWidth: w,offsetHeight: h } = originalEl
      let scale = 0
      scale = winWidth / w
      if (h * scale > winHeight - 80) {
        scale = (winHeight - 80) / h
      }
      return scale
    }

    // 获取距离
    function getDistance() {
      const touchArr = Array.from(touches)
      if (touchArr.length < 2) {
        return 0
      }
      const start = touchArr[0][1]
      const end = touchArr[1][1]
      return Math.hypot(end.x - start.x,end.y - start.y)
    }

    // 记录初始化数据
    function record() {
      initialData = Object.assign({},{ offset,origin,scale })
    }

    // 还原记录,用于边界处理
    let timer = null
    function reduction() {
      timer && clearTimeout(timer)
      timer = setTimeout(() => {
        // offset = initialData.offset
        // origin = initialData.origin
        // scale = initialData.scale
        changeStyle(cloneEl,[`transform: translate(${offset.left + 'px'}, ${offset.top + 'px'}) scale(${scale})`,`transform-origin: ${origin}`])
      },300)
    }
  }
})();

QingJ © 2025

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