// ==UserScript== // @name 网页字数统计器 1.0 Pro // @namespace http://tampermonkey.net/ // @version 1.0.0 // @description 字数统计 | 字符统计支持去空格/不去空格 | 可拖动设置面板 // @author 拾壹有点呆,本工具由Chat-GPT,DeepSeek,Gemini,Dobao完成 // @match *://*/* // @grant none // ==/UserScript== (function () { 'use strict'; /* ========== 全局变量 ========== */ let currentMode = '字符'; // 统计模式:字符 / 中文 / 英文单词 let charCountMode = 'trim'; // 字符统计子选项:trim(去空格) / keep(不去空格) let isCounterHidden = false; let dragStarted = false; let dragPerformed = false; let dragOffsetX = 0, dragOffsetY = 0; let mouseDownX = 0, mouseDownY = 0; let suppressOpen = false; let counterBox = null; let settingsPanel = null; /* ========== 创建 UI ========== */ const style = document.createElement('style'); style.innerHTML = ` #word-counter-pro { position: fixed; bottom: 20px; right: 20px; padding: 10px 14px; background: rgba(0, 123, 255, 0.9); color: #fff; border-radius: 10px; font-size: 13px; font-family: sans-serif; z-index: 999999999; box-shadow: 0 4px 12px rgba(0,0,0,0.3); cursor: move; user-select: none; transition: background 0.2s, font-size 0.2s, padding 0.2s; } .wc-settings-panel { position: fixed; background: #fff; color: #333; border-radius: 12px; box-shadow: 0 8px 20px rgba(0,0,0,0.2); padding: 0; font-size: 14px; font-family: system-ui, 'Segoe UI', sans-serif; z-index: 1000000000; width: 280px; backdrop-filter: blur(2px); border: 1px solid rgba(0,0,0,0.1); cursor: default; overflow: hidden; } .wc-settings-header { background: #f8f9fa; padding: 12px 16px; cursor: move; border-bottom: 1px solid #e9ecef; display: flex; justify-content: space-between; align-items: center; user-select: none; } .wc-settings-header h4 { margin: 0; font-size: 16px; color: #007bff; } .wc-settings-header .close-icon { cursor: pointer; font-size: 18px; line-height: 1; padding: 0 4px; color: #888; } .wc-settings-header .close-icon:hover { color: #333; } .wc-settings-content { padding: 14px 18px; } .setting-row { margin-bottom: 12px; display: flex; justify-content: space-between; align-items: center; gap: 12px; } .setting-row label { font-weight: 500; width: 70px; } .setting-row select, .setting-row input[type="color"], .setting-row input[type="range"] { flex: 1; padding: 5px; border-radius: 6px; border: 1px solid #ccc; } .color-row { display: flex; gap: 8px; align-items: center; flex: 1; } .color-row input { flex: 1; } .radio-group { display: flex; gap: 12px; align-items: center; } .radio-group label { width: auto; font-weight: normal; display: flex; align-items: center; gap: 4px; cursor: pointer; } button { width: 100%; padding: 6px 0; margin-top: 8px; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; transition: 0.2s; } .btn-hide-counter { background: #dc3545; color: white; } .btn-hide-counter:hover { background: #c82333; } .value-display { min-width: 40px; text-align: right; font-size: 12px; color: #666; } .sub-setting { margin-left: 10px; padding-left: 10px; border-left: 2px solid #ddd; } `; document.head.appendChild(style); counterBox = document.createElement('div'); counterBox.id = 'word-counter-pro'; counterBox.innerText = '统计中...'; document.body.appendChild(counterBox); settingsPanel = document.createElement('div'); settingsPanel.className = 'wc-settings-panel'; settingsPanel.style.display = 'none'; document.body.appendChild(settingsPanel); /* ========== 辅助函数 ========== */ function getText() { const clone = document.body.cloneNode(true); clone.querySelectorAll('script, style, nav, footer, noscript, meta, link').forEach(el => el.remove()); return clone.textContent || ''; } function countText(text) { if (currentMode === '字符') { if (charCountMode === 'trim') { return text.replace(/\s+/g, '').length; } else { return text.length; // 不去空格,包括空格、换行等 } } if (currentMode === '中文') { return (text.match(/[\u4e00-\u9fa5]/g) || []).length; } if (currentMode === '英文单词') { return (text.match(/\b[a-zA-Z]+\b/g) || []).length; } return 0; } function updateCount() { if (isCounterHidden) return; const text = getText(); const count = countText(text); counterBox.innerText = `${currentMode}: ${count}`; } let updateTimer = null; function scheduleUpdate() { if (updateTimer) return; updateTimer = setTimeout(() => { updateCount(); updateTimer = null; }, 500); } function applyCustomStyle(colorHex, alpha, fontSize, padding) { let r = 0, g = 123, b = 255; if (colorHex && colorHex.startsWith('#')) { r = parseInt(colorHex.slice(1,3), 16); g = parseInt(colorHex.slice(3,5), 16); b = parseInt(colorHex.slice(5,7), 16); } const bgColor = `rgba(${r}, ${g}, ${b}, ${alpha})`; counterBox.style.backgroundColor = bgColor; counterBox.style.fontSize = fontSize + 'px'; counterBox.style.padding = padding + 'px ' + (padding + 4) + 'px'; } function syncControlsToPanel() { const bgColor = counterBox.style.backgroundColor; let alpha = 0.9, r = 0, g = 123, b = 255; const match = bgColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/); if (match) { r = parseInt(match[1]); g = parseInt(match[2]); b = parseInt(match[3]); alpha = match[4] ? parseFloat(match[4]) : 1; } const hex = '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); const colorInput = document.getElementById('wc-bg-color'); if (colorInput) colorInput.value = hex; const alphaSlider = document.getElementById('wc-alpha'); if (alphaSlider) { alphaSlider.value = alpha; const alphaVal = document.getElementById('wc-alpha-val'); if (alphaVal) alphaVal.innerText = alpha.toFixed(2); } let fontSize = parseInt(counterBox.style.fontSize); if (isNaN(fontSize)) fontSize = 13; const fontSizeSlider = document.getElementById('wc-font-size'); if (fontSizeSlider) { fontSizeSlider.value = fontSize; const fontSizeVal = document.getElementById('wc-font-size-val'); if (fontSizeVal) fontSizeVal.innerText = fontSize + 'px'; } let paddingVal = parseInt(counterBox.style.padding); if (isNaN(paddingVal)) paddingVal = 10; const paddingSlider = document.getElementById('wc-padding'); if (paddingSlider) { paddingSlider.value = paddingVal; const paddingValSpan = document.getElementById('wc-padding-val'); if (paddingValSpan) paddingValSpan.innerText = paddingVal + 'px'; } } // 设置面板拖动 let panelDragActive = false; let panelStartX = 0, panelStartY = 0; let panelInitialLeft = 0, panelInitialTop = 0; function initPanelDrag(headerElement) { if (!headerElement) return; headerElement.addEventListener('mousedown', (e) => { if (e.button !== 0) return; e.preventDefault(); panelDragActive = true; panelStartX = e.clientX; panelStartY = e.clientY; const rect = settingsPanel.getBoundingClientRect(); panelInitialLeft = rect.left; panelInitialTop = rect.top; settingsPanel.style.cursor = 'grabbing'; }); } function handlePanelDragMove(e) { if (!panelDragActive) return; let dx = e.clientX - panelStartX; let dy = e.clientY - panelStartY; let newLeft = panelInitialLeft + dx; let newTop = panelInitialTop + dy; newLeft = Math.min(Math.max(newLeft, 0), window.innerWidth - settingsPanel.offsetWidth); newTop = Math.min(Math.max(newTop, 0), window.innerHeight - settingsPanel.offsetHeight); settingsPanel.style.left = newLeft + 'px'; settingsPanel.style.top = newTop + 'px'; settingsPanel.style.right = 'auto'; settingsPanel.style.bottom = 'auto'; } function stopPanelDrag() { panelDragActive = false; settingsPanel.style.cursor = ''; } function buildSettingsPanel() { settingsPanel.innerHTML = `