文本复制按钮简洁版

选中文本后,出现一个简洁的复制按钮,鼠标离开一定范围按钮自动消失(为了防止截图的时候截取到)

// ==UserScript==
// @name         文本复制按钮简洁版
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  选中文本后,出现一个简洁的复制按钮,鼠标离开一定范围按钮自动消失(为了防止截图的时候截取到)
// @author       damu
// @match        *://*/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    const style = document.createElement('style');
    style.textContent = `
        .copy-btn {
            position: fixed;
            background: rgba(0,0,0,0.7);
            color: white;
            border: none;
            border-radius: 3px;
            padding: 2px 6px;
            font-size: 12px;
            cursor: pointer;
            z-index: 9999;
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.3s ease;
            transform: translate(0, 0); /* 确保定位精确 */
        }
        .copy-btn.visible {
            opacity: 1;
            pointer-events: auto;
        }
        .copy-btn:hover {
            background: rgba(0,0,0,0.9);
        }
    `;
    document.head.appendChild(style);

    const copyBtn = document.createElement('button');
    copyBtn.className = 'copy-btn';
    copyBtn.textContent = '复制';
    document.body.appendChild(copyBtn);

    let currentSelection = { text: '', rect: null };
    let ignoreNextMouseUp = false;
    let fixedPosition = { x: 0, y: 0 };

    const hideCopyBtn = () => {
        copyBtn.classList.remove('visible');
    };

    const showCopyBtn = (x, y) => {
        fixedPosition = {
            x: Math.round(x) + 22,
            y: Math.round(y) + 6
        };
        copyBtn.textContent = '复制';
        copyBtn.style.left = `${fixedPosition.x}px`;
        copyBtn.style.top = `${fixedPosition.y}px`;
        copyBtn.style.transform = 'none'; // 清除任何变换
        copyBtn.classList.add('visible');
    };

    const isMouseNearSelection = (mouseX, mouseY) => {
        if (!currentSelection.rect || !currentSelection.text) return false;
        const rangeInPixels = 2 * 37.8;
        const { left, top, right, bottom } = currentSelection.rect;
        return mouseX >= left - rangeInPixels &&
            mouseX <= right + rangeInPixels &&
            mouseY >= top - rangeInPixels &&
            mouseY <= bottom + rangeInPixels;
    };

    copyBtn.addEventListener('click', (e) => {
        e.stopPropagation();
        navigator.clipboard.writeText(currentSelection.text).then(() => {
            // 严格使用固定位置
            copyBtn.textContent = '已复制';
            copyBtn.style.left = `${fixedPosition.x}px`;
            copyBtn.style.top = `${fixedPosition.y}px`;

            setTimeout(() => {
                copyBtn.classList.remove('visible');
            }, 1000);
        }).catch(console.error);
        ignoreNextMouseUp = true;
        setTimeout(() => { ignoreNextMouseUp = false; }, 100);
    });

    document.addEventListener('mouseup', (e) => {
        if (ignoreNextMouseUp) return;
        const selection = window.getSelection();
        const selectedText = selection.toString().trim();

        if (selectedText.length > 0) {
            const range = selection.getRangeAt(0);
            const rect = range.getBoundingClientRect();
            const buttonX = rect.right;
            const buttonY = rect.top;
            currentSelection = {
                text: selectedText,
                rect: rect
            };
            showCopyBtn(buttonX, buttonY);
        } else {
            hideCopyBtn();
        }
    });

    document.addEventListener('mousemove', (e) => {
        if (!currentSelection.text) return;
        if (!isMouseNearSelection(e.clientX, e.clientY)) {
            hideCopyBtn();
        }
    });

    document.addEventListener('mousedown', (e) => {
        if (e.target !== copyBtn) {
            hideCopyBtn();
        }
    });
})();