// ==UserScript== // @name Reading Ruler 阅读标尺 // @namespace http://tampermonkey.net/ // @version 0.1 // @description A reading ruler tool to help focus while reading // @author lumos momo // @match *://*/* // @grant GM_setValue // @grant GM_getValue // @license GPL-3.0-or-later // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 默认设置 const defaultSettings = { height: 30, color: '#ffeb3b', opacity: 0.3, isEnabled: false, isInverted: false, position: { x: 20, y: '50%' } }; // 获取保存的设置 let settings = { ...defaultSettings, ...GM_getValue('rulerSettings', {}) }; // 创建样式 const style = document.createElement('style'); style.textContent = ` .reading-ruler { position: fixed; left: 0; width: 100%; height: ${settings.height}px; pointer-events: none; z-index: 9999; transition: top 0.1s ease; display: none; } .reading-ruler.normal { background-color: ${settings.color}; opacity: ${settings.opacity}; } .reading-ruler.inverted { background-color: transparent; box-shadow: 0 0 0 100vh ${settings.color}; position: fixed; left: 0; right: 0; width: 100%; } .ruler-control { position: fixed; left: ${settings.position.x}px; top: ${settings.position.y}; transform: translateY(-50%); z-index: 10000; cursor: move; user-select: none; } .ruler-toggle { width: 48px; height: 48px; border-radius: 50%; background: white; border: none; box-shadow: 0 2px 5px rgba(0,0,0,0.2); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background-color 0.3s; font-size: 20px; font-weight: bold; color: #666; } .ruler-toggle:hover { background-color: #f5f5f5; } .ruler-toggle.active { background-color: #e3f2fd; color: #2196f3; } .ruler-settings { position: absolute; top: 0; left: 100%; margin-left: 10px; background: white; border-radius: 4px; padding: 15px; box-shadow: 0 2px 5px rgba(0,0,0,0.2); display: none; width: 200px; } .ruler-settings.visible { display: block; } .ruler-settings label { display: block; margin: 10px 0; font-size: 14px; } .ruler-settings input { width: 100%; margin-top: 5px; } .ruler-settings .mode-switch { display: flex; align-items: center; margin: 10px 0; padding: 8px 0; border-top: 1px solid #eee; } .ruler-settings .mode-switch span { flex-grow: 1; font-size: 14px; } .mode-switch-toggle { position: relative; display: inline-block; width: 40px; height: 20px; } .mode-switch-toggle input { opacity: 0; width: 0; height: 0; } .mode-switch-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 20px; } .mode-switch-slider:before { position: absolute; content: ""; height: 16px; width: 16px; left: 2px; bottom: 2px; background-color: white; transition: .4s; border-radius: 50%; } .mode-switch-toggle input:checked + .mode-switch-slider { background-color: #2196F3; } .mode-switch-toggle input:checked + .mode-switch-slider:before { transform: translateX(20px); } `; document.head.appendChild(style); // 创建标尺元素 const ruler = document.createElement('div'); ruler.className = 'reading-ruler'; document.body.appendChild(ruler); // 创建控制面板 const control = document.createElement('div'); control.className = 'ruler-control'; control.innerHTML = `
反色模式
`; document.body.appendChild(control); // 获取所有需要的元素 const toggleButton = document.getElementById('toggleRuler'); const modeSwitch = document.getElementById('toggleMode'); const settingsPanel = control.querySelector('.ruler-settings'); // 拖动功能实现 let isDragging = false; let currentX; let currentY; let initialX; let initialY; let xOffset = settings.position.x; let yOffset = parseInt(settings.position.y) || window.innerHeight / 2; function dragStart(e) { if (e.type === "mousedown") { initialX = e.clientX - xOffset; initialY = e.clientY - yOffset; } if (e.target.closest('.ruler-toggle')) { isDragging = true; } } function dragEnd(e) { initialX = currentX; initialY = currentY; isDragging = false; settings.position = { x: xOffset, y: yOffset + 'px' }; saveSettings(); } function drag(e) { if (isDragging) { e.preventDefault(); currentX = e.clientX - initialX; currentY = e.clientY - initialY; xOffset = currentX; yOffset = currentY; control.style.left = currentX + 'px'; control.style.top = currentY + 'px'; control.style.transform = 'none'; } } // 更新设置显示 function updateSettingsDisplay() { document.getElementById('heightValue').textContent = settings.height; document.getElementById('opacityValue').textContent = Math.round(settings.opacity * 100); ruler.style.height = `${settings.height}px`; updateRulerMode(); } // 更新标尺模式 function updateRulerMode() { ruler.className = 'reading-ruler ' + (settings.isInverted ? 'inverted' : 'normal'); if (!settings.isInverted) { ruler.style.backgroundColor = settings.color; ruler.style.opacity = settings.opacity; ruler.style.boxShadow = ''; } else { ruler.style.backgroundColor = 'transparent'; ruler.style.boxShadow = `0 0 0 100vh ${settings.color}`; ruler.style.opacity = settings.opacity; } } // 保存设置 function saveSettings() { GM_setValue('rulerSettings', settings); } // 更新显示模式 function updateDisplayMode() { ruler.style.display = settings.isEnabled ? 'block' : 'none'; updateRulerMode(); } // 事件监听器 toggleButton.addEventListener('click', () => { settings.isEnabled = !settings.isEnabled; toggleButton.classList.toggle('active', settings.isEnabled); updateDisplayMode(); saveSettings(); }); // 模式切换 modeSwitch.addEventListener('change', (e) => { settings.isInverted = e.target.checked; updateDisplayMode(); saveSettings(); }); // 右键菜单 toggleButton.addEventListener('contextmenu', (e) => { e.preventDefault(); settingsPanel.classList.toggle('visible'); }); // 点击其他地方关闭设置面板 document.addEventListener('click', (e) => { if (!e.target.closest('.ruler-settings') && !e.target.closest('.ruler-toggle')) { settingsPanel.classList.remove('visible'); } }); // 拖动事件监听 control.addEventListener("mousedown", dragStart); document.addEventListener("mousemove", drag); document.addEventListener("mouseup", dragEnd); // 设置面板事件监听 document.getElementById('rulerHeight').addEventListener('input', (e) => { settings.height = parseInt(e.target.value); updateSettingsDisplay(); saveSettings(); }); document.getElementById('rulerColor').addEventListener('input', (e) => { settings.color = e.target.value; updateSettingsDisplay(); saveSettings(); }); document.getElementById('rulerOpacity').addEventListener('input', (e) => { settings.opacity = parseInt(e.target.value) / 100; updateSettingsDisplay(); saveSettings(); }); // 鼠标移动时更新位置 document.addEventListener('mousemove', (e) => { if (settings.isEnabled) { const y = e.clientY - (settings.height / 2); ruler.style.top = `${y}px`; } }); // 初始化显示状态 if (settings.isEnabled) { toggleButton.classList.add('active'); updateDisplayMode(); } })();