// ==UserScript== // @name SZ去黑屏 // @namespace sz-smooth-frame // @version 1.2.1 // @description 粟镇标注平台切帧优化:预取相邻帧图进浏览器缓存 + 隐藏loading转圈 + 隐藏load_box黑色遮罩,消除切帧时的转圈、闪烁和黑屏;预取请求打标记兼容SZ.txt校验脚本 // @match http://118.196.97.105:8080/w/task.html* // @run-at document-start // @grant none // ==/UserScript== (function () { 'use strict'; if (window.__szSmoothInjected) return; window.__szSmoothInjected = true; // ===== 可调参数 ===== const RADIUS = 3; // 向前预取帧数 const BACK = 2; // 向后(回看)预取帧数 const GAP = 250; // 预取请求间隔(ms),防风控 const DEBUG = false; // true 时打印调试日志 // ==================== let accessKey = null; // 登录凭证(从真实请求学习) let taskKey = null; // 任务key(从请求体学习) const prefetched = new Set(); // 已处理过的 task_id const queue = []; let busy = false; const log = (m) => { if (DEBUG) console.log('[SZ优化]', m); }; const warn = (m) => console.warn('[SZ优化]', m); // ---------- hook XHR:学凭证 + 真实响应后触发预取 ---------- const _setRequestHeader = XMLHttpRequest.prototype.setRequestHeader; XMLHttpRequest.prototype.setRequestHeader = function (k, v) { if (String(k).toLowerCase() === 'access-key') accessKey = v; return _setRequestHeader.apply(this, arguments); }; const _open = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function (method, url) { this.__szUrl = String(url || ''); return _open.apply(this, arguments); }; const _send = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function (body) { if ((this.__szUrl || '').indexOf('get-mark-data') !== -1) { try { const b = JSON.parse(body); if (b.task_key) taskKey = b.task_key; } catch (e) {} // 真实响应:预加载当前帧图 + 滑动预取相邻帧 this.addEventListener('load', () => { try { const d = JSON.parse(this.responseText); const md = d && d.data && d.data.markData; if (md && md.imgUrl) preloadImage(md.imgUrl); if (d && d.data && d.data.task_id) queuePrefetch(d.data.task_id); } catch (e) {} }); } return _send.apply(this, arguments); }; // ---------- 帧图预加载(进浏览器HTTP缓存,切帧时秒出) ---------- function preloadImage(url) { try { const im = new Image(); im.src = url; } catch (e) {} } // ---------- 串行预取队列(一次一个,间隔GAP,防风控) ---------- function enqueue(tid) { if (!tid || prefetched.has(tid) || !taskKey) return; prefetched.add(tid); queue.push(tid); pump(); } function pump() { if (busy || !queue.length) return; busy = true; const tid = queue.shift(); const xhr = new XMLHttpRequest(); xhr._szPrefetch = true; // 标记预取请求:SZ.txt等校验脚本据此跳过,避免面板内容跳动 xhr.open('POST', '/api/task/get-mark-data'); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); xhr.setRequestHeader('Accept', 'application/json, text/javascript, */*; q=0.01'); if (accessKey) xhr.setRequestHeader('Access-Key', accessKey); xhr.onload = () => { try { const d = JSON.parse(xhr.responseText); const md = d && d.data && d.data.markData; if (md && md.imgUrl) { preloadImage(md.imgUrl); log('预取成功 task=' + tid); } } catch (e) {} setTimeout(() => { busy = false; pump(); }, GAP); }; xhr.onerror = () => { warn('预取网络错误 task=' + tid); setTimeout(() => { busy = false; pump(); }, GAP); }; xhr.send(JSON.stringify({ time: Date.now(), task_id: tid, task_key: taskKey, is_preview: 0 })); } function queuePrefetch(tid) { for (let d = 1; d <= RADIUS; d++) enqueue(tid + d); for (let d = 1; d <= BACK; d++) enqueue(tid - d); } // ---------- 隐藏 loading 转圈 + 黑色遮罩(load_box / view-mode-loading) ---------- function hideLoading() { document.querySelectorAll('img').forEach((im) => { if ((im.src || '').indexOf('loading.gif') !== -1) im.style.display = 'none'; }); } // "暗一下"来源:平台切帧时把 .load_box 设 display:block(全屏 rgba(0,0,0,0.5) 黑罩+转圈) // 以及 .view-mode-loading/.el-loading-mask 半透明遮罩 // 用 CSS !important 强制隐藏(保留占位不跳布局,pointer-events 穿透不挡点击) const maskStyle = document.createElement('style'); maskStyle.textContent = '.load_box{display:none !important;}' + '.view-mode-loading{opacity:0 !important;pointer-events:none !important;}' + '.el-loading-mask{opacity:0 !important;pointer-events:none !important;}'; if (document.head) document.head.appendChild(maskStyle); else document.addEventListener('DOMContentLoaded', () => document.head.appendChild(maskStyle)); // 兜底:平台若用内联 style 覆盖,观察器强制清掉 function hideMask() { document.querySelectorAll('.load_box').forEach((el) => { el.style.display = 'none'; }); document.querySelectorAll('.view-mode-loading,.el-loading-mask').forEach((el) => { el.style.opacity = '0'; el.style.pointerEvents = 'none'; }); } function initHide() { hideLoading(); hideMask(); new MutationObserver(() => { hideLoading(); hideMask(); }) .observe(document.documentElement, { subtree: true, childList: true, attributes: true, attributeFilter: ['class', 'style'] }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initHide); } else { initHide(); } log('已注入'); })();