图片下载 | 页面所有图片打包下载 | 单个图片下载 | 想要哪张点哪张,简单有效

简单纯洁的网页图片下载小工具 | 图片打包 | 可打包页面上所有图片下载 | 可单选(Ctrl+鼠标右键)下载单个图片 | { 功能: 1.打包所有下载 2.Ctrl+鼠标右键,下载单个 } | 没有什么花里胡哨的东西简单有效 | A tool that helps you quickly capture web images and package them for download

// ==UserScript==
// @name         图片下载 | 页面所有图片打包下载 | 单个图片下载 | 想要哪张点哪张,简单有效
// @namespace    http://tampermonkey.net/
// @description  简单纯洁的网页图片下载小工具 | 图片打包 | 可打包页面上所有图片下载 | 可单选(Ctrl+鼠标右键)下载单个图片 |  { 功能: 1.打包所有下载 2.Ctrl+鼠标右键,下载单个 } | 没有什么花里胡哨的东西简单有效 | A tool that helps you quickly capture web images and package them for download
// @description:zh-CN  一个帮你快速捕获网页图片并打包下载、也可单选下载的小工具🔧
// @author       <shing0727@foxmail.com>
// @version      v3.4.3
// @license      GPLv3
// @icon         https://s21.ax1x.com/2024/05/14/pkmNM0s.png
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.6.0/jszip.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// @grant        GM_xmlhttpRequest
// @match        *://*/*

// ==/UserScript==

var btnStyle = `
    background-color: #fff;
    width: 90px;
    padding: 8px 0;
    border-radius: 6px;
    background: #333;
    justify-content: center;
    align-items: center;
    font-family: 'Damion', cursive;
    border: none;
    font-size: 14px;
    cursor: inherit;
    transition: 500ms;
    color: #eeeeee;
    box-shadow: 0 0 5px #444, 5px 5px 15px #222, inset 5px 5px 10px #444,inset -5px -5px 10px #222;
`;
var btn_text_style = `
    margin: 0;
    padding: 0;
    font-size: 14px;
`;
var load_tip_style = `
    position: fixed;
    width: 210px;
    padding: 12px;
    top: 20px;
    right: -220px;
    color: #333;
    background-color: #fff;
    box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 10px 5px;
    border-radius: 5px;
    transition: all 0.5s ease-in-out 0s, transform 0.5s ease-in-out 0s;
    font-family: 'Damion', cursive;
    font-weight: 600;
    z-index: 9999;
`;

var cssContent = `
#ccc_load_container{
    padding: 20px 10px 20px 20px;
    position: fixed;
    bottom: 10px;
    right: -92px;
    z-index: 99999;
    transition: all 0.5s ease-in-out;
    cursor: pointer;
    opacity: 0.3;
}
#ccc_load_container:hover{
    right: 0;
    opacity: 1;
}
#flashing-div {
    opacity: 1;
    transition: all 0.8s ease-in-out, transform 0.8s ease-in-out;
    animation: flashAndScale 2.5s infinite; /* 初始状态设置为无限循环,稍后通过JavaScript控制 */
    visibility: visible; /* 初始状态为可见 */
  }
  @keyframes flashAndScale {
    0% {
      opacity: 0.9;
      box-shadow: 0px 0px 8px 4px rgba(0, 0, 0, 0.5);
    }
    25% {
      opacity: 1;
      box-shadow: none;
      box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, 0.5);
    }
    50% {
      opacity: 0.9;
      box-shadow: 0px 0px 8px 4px rgba(0, 0, 0, 0.5);
    }
    75% {
      opacity: 1;
      box-shadow: none;
      box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, 0.5);
    }
    100% {
      opacity: 0.9;
      box-shadow: 0px 0px 8px 4px rgba(0, 0, 0, 0.5);
    }
  }
  /* 添加一个类来停止动画并隐藏div(如果需要的话) */
  #flashing-div.stopped {
    animation: none;
    visibility: hidden;
    opacity: 0;
    transform: scale(0);
    transition: none;
  }

`;
// -------全局-------------
var lock = true; // 防止事件重复调用
var allLoadDOM = null; // 按钮容器-打包下载所有
var LoadTipDOM = null; // 下载提示框
// --------------------
// 初始加载样式
const loadStyle = () => {
  // 创建一个新的style元素
  var style = document.createElement("style");

  // 将CSS样式内容设置为style元素的文本内容
  if (style.styleSheet) {
    // 对于老版本的IE浏览器
    style.styleSheet.cssText = cssContent;
  } else {
    // 对于其他浏览器
    style.appendChild(document.createTextNode(cssContent));
  }
  // 将style元素添加到head中
  var head = document.head || document.getElementsByTagName("head")[0];
  head.appendChild(style);
};
/**
 * 传入配置信息创建元素并返回DOM对象
 * @param {*
 *  option { el:元素, text: 元素文本, className: 类名, prop:属性 }
 *  mountE: 挂载元素
 * }
 * @returns 创建元素
 */
const myCreateEle = (option, mountE) => {
  let e = document.createElement(option.el || "div");
  option.className && e.classList.add(option.className);
  for (let p in option.prop || {}) {
    e.setAttribute(p, option.prop[p]);
  }
  e.innerText = option.text || "";
  e.style.cssText = option.style || "";
  mountE && mountE.appendChild(e);
  return e;
};

// 创建下载提示框
const createLoadTip = () => {
  LoadTipDOM = myCreateEle(
    {
      el: "div",
      style: `${load_tip_style}`,
      text: "稍等片刻,正在打包下载...",
    },
    document.body
  );
};

// 打包下载所有图片事件
const handleDownload = () => {
  let imageUrls = [];
  document.querySelectorAll("img").forEach((item) => {
    let src = item?.src;
    if (!src) {
      src = item.getAttribute("srcset");
    }
    if (!src) return;
    imageUrls.push({
      src,
      imgName: getImageFileNameFromUrl(src) + getImageExtension(src),
    });
  });
  if (!imageUrls.length) return;
  const zip = new JSZip();
  lock = false;
  !LoadTipDOM && createLoadTip();
  LoadTipDOM.style.right = "20px";
  Promise.all(
    imageUrls.map((item, index) => {
      return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
          method: "GET",
          url: item.src,
          responseType: "blob",
          onload: function (response) {
            if (response.status === 200) {
              let blob = response.response;
              const filename = item.imgName;
              zip.file(filename, blob, { binary: true });
            } else {
              console.error("Request failed with status " + response.status);
            }
            resolve();
          },
          onerror: function (e) {
            console.error("Request failed: " + e.message);
            resolve();
          },
        });
      });
    })
  )
    .then(() => {
      let domain = window.location.href.replace(/^https?:\/\//i, "");
      zip.generateAsync({ type: "blob" }).then((blob) => {
        saveAs(blob, `【${document.title}】【${domain}】.zip`);
        unlock();
      });
    })
    .catch((error) => {
      unlock();
    });
};
const unlock = (delay = 500) => {
  setTimeout(() => {
    lock = true;
    allLoadDOM.style.cursor = "pointer";
    LoadTipDOM.style.right = "-220px";
  }, delay);
};
const createEle = () => {
  allLoadDOM = document.createElement("div");
  allLoadDOM.setAttribute("id", "ccc_load_container");
  if (!localStorage.getItem("IS_IMAGE_DOWNLOAD_TIP")) {
    allLoadDOM.style.right = "0";
    allLoadDOM.style.opacity = "1";
    setTimeout(() => {
      allLoadDOM.style = "";
    }, 1500);
  }
  $(allLoadDOM).append(`
        <button style="${btnStyle}"><p style="${btn_text_style}">所有图片</p><p style="${btn_text_style}">打包下载</p></button>
    `);
  document.body.appendChild(allLoadDOM);
  $("#ccc_load_container > button").click(() => {
    lock && handleDownload();
    allLoadDOM.style.cursor = "not-allowed";
  });
};

// 获取图片名称
function getImageFileNameFromUrl(url) {
  // 正则表达式匹配最后一个'/'和第一个'?'之间的内容(如果存在)
  // 或者最后一个'/'和字符串结尾之间的内容(如果不存在查询参数)
  const regex = /\/([^?\/]+?)(?:\?|$|@)/;
  const match = url.match(regex);
  if (match && match[1]) {
    // match[1] 是文件名(不包括查询参数及其后面的部分)
    return match[1];
  }
  return "default.jpg";
}

// 获取图片扩展名
function getImageExtension(url) {
  var extension = url.match(/\.(jpg|jpeg|png|gif|bmp|webp|svg|tiff|tif)$/i);
  if (extension) {
    return extension[0];
  } else {
    return ".jpg";
  }
}

function downloadImageData(imgSrc, imgAlt = "default") {
  let a = document.createElement("a");
  a.href = imgSrc;
  a.download = imgAlt || "image.png"; // 设置下载的图片名
  a.style.display = "none";
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
}

function downloadImage(imageUrl, imageName = "image.png") {
  let url = imageUrl;
  if (url.includes("?")) {
    url = url.split("?")[0];
  }
  if (url.includes("@")) {
    url = url.split("@")[0];
  }  
  if (url.includes("#")) {
    url = url.split("#")[0];
  }  
  if( !url.match(/https?:\/\//)){
    url = window.location.protocol+'//'+window.location.hostname + '/' + url
  }
  GM_xmlhttpRequest({
    method: "GET",
    url,
    responseType: "blob",
    onload: function (response) {
      if (response.status === 200) {
        let blob = response.response;
        
      // 创建一个Blob URL
      const url = window.URL.createObjectURL(blob);
      // 创建一个<a>标签用于下载
      const a = document.createElement("a");
      a.href = url;
      a.download = imageName; // 设置下载的文件名
      a.style.display = "none";
      // 触发点击事件
      document.body.appendChild(a);
      a.click();
      // 释放URL对象
      window.URL.revokeObjectURL(url);
      // 清理<a>标签
      document.body.removeChild(a);

      } else {
        console.error("下载图片失败,状态码为 " + response.status);
      }
    },
    onerror: function (e) {
      console.error("请求失败: " + e.message);
    },
  });
}

const initBindEvent = () => {
  document.addEventListener("contextmenu", function (event) {
    if (event.ctrlKey) {
      // 取消默认行为(阻止上下文菜单出现)
      event.preventDefault();
    }
  });
  document.addEventListener("mousedown", function (event) {
    if (event.ctrlKey && event.button === 2) {
      event.preventDefault();
      var targetElement = event.target;
      const getUrl = (dom) => {
        return dom.getAttribute("src") || dom.getAttribute("srcset") || "";
      };
      if (targetElement) {
        let url = getUrl(targetElement);
        let srcArr = [];
        if (url) {
          srcArr = [url];
        } else {
          let arrDom = targetElement.querySelectorAll("img");
          arrDom.forEach((item) => {
            srcArr.push(getUrl(item));
          });
        }
        srcArr.forEach((url) => {
          const fileName = getImageFileNameFromUrl(url);
          if (url.includes("data:image/")) {
            downloadImageData(url);
          } else {
            downloadImage(url, fileName);
          }
        });
      }
    }
  });
};
const initTip = () => {
  let tip = document.createElement("div");
  tip.style.cssText = `
        position: fixed;
        top: 3vh;
        left: 50%;
        transform: translate(-50%, 0);
        background-color: rgba(0, 0, 0, 0.8);
        display: flex;
        justify-content: center;
       align-items: center;
       color: #ffffff;
       z-index: 999999;
       padding: 10px 20px;
       border-radius: 10px;
       text-align: center;
       letter-spacing: 1.5px;
    `;

  tip.innerText = "Ctrl + 🖱️鼠标右键 \n (下载选中图片~)";
  tip.setAttribute("id", "flashing-div");
  setTimeout(() => {
    tip.style.top = "-150px";
    // tip.style.display = "none";
  }, 3000);

  document.body.appendChild(tip);
};

const init = () => {
  window.addEventListener("load", function () {
    if (window.self !== window.top) return;
    createEle();
    initBindEvent();
    loadStyle();
    setTimeout(() => {
      if (!localStorage.getItem("IS_IMAGE_DOWNLOAD_TIP")) {
        localStorage.setItem("IS_IMAGE_DOWNLOAD_TIP", "1");
        initTip();
      }
    }, 200);
  });
};
(function () {
  init();
})();