Tampermonkey脚本下载Canvas图片文件损坏 手动运行正常如何解决
问题根因
你遇到的核心差异来自运行时机:你手动在控制台运行脚本时,页面的Canvas绘制逻辑已经完全执行完毕,而Tampermonkey的document-idle、jQuery的ready事件都只代表DOM树加载完成,画廊页面的图片资源加载、Canvas异步绘制逻辑往往在这之后才执行,你提前调用toDataURL拿到的就是未完成绘制的Canvas内容,自然会出现图片损坏的情况。固定时长等待无法适配不同网络环境下的加载速度,自然不稳定。
可靠的检测方案
不要依赖固定延时,改用Canvas内容稳定性检测逻辑:定期采样Canvas的像素特征,连续多次采样结果一致时,就代表绘制已经完成,可以执行下载。
另外你现有代码里的eval解析页码逻辑风险极高,且完全没必要,可以用正则直接匹配页码,更稳定可靠。
修改后的完整脚本
// ==UserScript== // @name somesite 画廊批量下载 // @description 自动下载Canvas画廊的完整图片 // @namespace somesite // @version 2.0 // @match https://somesite.com/somegallery/* // @license tttt // @copyright tttt // @grant none // @run-at document-idle // ==/UserScript== (function() { // 等待Canvas绘制完成的函数 function waitForCanvasReady(canvas, callback) { let lastData = null; let stableCount = 0; // 每500ms采样一次Canvas的像素特征 const checkTimer = setInterval(() => { // 取Canvas左上角100x100区域的像素数据做特征,全图采样太耗性能 const ctx = canvas.getContext('2d'); const imgData = ctx.getImageData(0, 0, Math.min(100, canvas.width), Math.min(100, canvas.height)).data.toString(); if (imgData === lastData) { stableCount++; // 连续3次采样结果一致,认为绘制完成 if (stableCount >= 3) { clearInterval(checkTimer); callback(); } } else { lastData = imgData; stableCount = 0; } }, 500); } // 执行下载逻辑 function downloadImage() { const canvas = document.getElementById("the-canvas"); const pageText = document.querySelector("#main > h1").textContent; // 用正则匹配页码,比字符串切割+eval安全可靠 const pageMatch = pageText.match(/Page (\d+) of (\d+)/); if (!pageMatch) return; const currentPage = pageMatch[1], totalPage = pageMatch[2]; const image = canvas.toDataURL("image/png"); const link = document.createElement("a"); link.download = `Page ${currentPage} of ${totalPage}.png`; link.href = image; document.body.appendChild(link); link.click(); document.body.removeChild(link); // 不是最后一页就翻页 if (parseInt(currentPage) !== parseInt(totalPage)) { setTimeout(() => { document.getElementsByClassName("btn btn-primary float-right")[1].click(); }, Math.floor(Math.random() * 2000) + 1000); } } // 先检测Canvas元素是否存在,存在就等绘制完成再下载 const checkCanvasTimer = setInterval(() => { const canvas = document.getElementById("the-canvas"); if (canvas) { clearInterval(checkCanvasTimer); waitForCanvasReady(canvas, downloadImage); } }, 300); })();
额外优化说明
- 去掉了对页面jQuery的依赖,全部用原生JS实现,避免页面未加载jQuery导致的逻辑异常
- 增加了Canvas元素存在性检测,避免页面DOM还没渲染完就执行逻辑报错
- 采样仅取Canvas左上角小区域做特征对比,不会影响页面性能
- 下载完成后自动移除创建的a标签,避免页面残留冗余元素
内容的提问来源于stack exchange,提问作者Marugo
相关产品推荐
相关产品推荐

