Greasy Fork

Greasy Fork is available in English.

GoodNote - 网页笔记助手

在任何网页添加笔记功能

当前为 2025-02-07 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         GoodNote - 网页笔记助手
// @namespace    http://tampermonkey.net/
// @version      0.2.8
// @description  在任何网页添加笔记功能
// @author       kasusa
// @license MIT
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // 创建样式
    const style = document.createElement('style');
    style.textContent = `
        .note-icon {
            position: fixed;
            top: 20px;
            right: 20px;
            width: 40px;
            height: 40px;
            background-color: #4CAF50;
            border-radius: 50%;
            cursor: move;
            z-index: 9999;
            display: flex;
            align-items: center;
            justify-content: center;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
            transition: all 0.3s ease;
            user-select: none;
            will-change: transform;
            transform: translate3d(0, 0, 0);
            transform: scale(0.8);
            opacity:50%;
        }

        .note-icon:hover {
            transform: scale(1);
            opacity:1;
        }
        .note-icon:active {
            transform: scale(0.9);
            opacity:1;
        }

        .note-icon svg {
            width: 24px;
            height: 24px;
            fill: white;
        }

        .note-container {
            position: fixed;
            background: ;
            backdrop-filter: blur(10px);
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
            z-index: 9998;
            padding: 10px;
            transition: all 0.3s ease;
            opacity: 0;
            transform-origin: center;

        }

        .note-container.active {
            opacity: 1;
            transform: scale(1);
        }

        .note-textarea {
            width: 100%;
            min-height: 250px;
            border: 1px solid #ddd;
            border-radius: 4px;
            padding: 12px;
            font-size: 14px;
            resize: all;
            font-family: Arial, sans-serif;
            line-height: 1.5;
    min-width: 350px;
        }

        .note-textarea:focus {
            outline: none;
            border-color: #4CAF50;
        }
    `;
    document.head.appendChild(style);

    // 创建笔记图标
    const noteIcon = document.createElement('div');
    noteIcon.className = 'note-icon';
    noteIcon.innerHTML = `
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
            <path d="M14,10H19.5L14,4.5V10M5,3H15L21,9V19A2,2 0 0,1 19,21H5C3.89,21 3,20.1 3,19V5C3,3.89 3.89,3 5,3M5,12V14H19V12H5M5,16V18H14V16H5Z"/>
        </svg>
    `;

    // 创建笔记容器
    const noteContainer = document.createElement('div');
    noteContainer.className = 'note-container';

    // 创建文本框
    const textarea = document.createElement('textarea');
    textarea.className = 'note-textarea';
    textarea.placeholder = '在这里输入你的笔记...';

    noteContainer.appendChild(textarea);

    // 添加到页面
    document.body.appendChild(noteIcon);
    document.body.appendChild(noteContainer);

    // 获取当前域名作为存储键
    const storageKey = `goodnote_${window.location.hostname}`;
    const positionKey = `goodnote_position_${window.location.hostname}`;

    // 从localStorage加载笔记
    const savedNote = localStorage.getItem(storageKey);
    if (savedNote) {
        textarea.value = savedNote;
    }

    // 实现拖拽功能
    let isDragging = false;
    let currentX;
    let currentY;
    let initialX;
    let initialY;
    let xOffset = 0;
    let yOffset = 0;

    let rafId = null;

    noteIcon.addEventListener('mousedown', dragStart);
    document.addEventListener('mousemove', drag);
    document.addEventListener('mouseup', dragEnd);

    function dragStart(e) {
        if (e.target === noteIcon || noteIcon.contains(e.target)) {
            isDragging = true;
            const rect = noteIcon.getBoundingClientRect();
            initialX = e.clientX - rect.left;
            initialY = e.clientY - rect.top;

            noteIcon.style.transition = 'none';
        }
    }

    function drag(e) {
        if (isDragging) {
            e.preventDefault();

            if (rafId) {
                cancelAnimationFrame(rafId);
            }

            rafId = requestAnimationFrame(() => {
                const newX = e.clientX - initialX;
                const newY = e.clientY - initialY;

                currentX = Math.min(Math.max(0, newX), window.innerWidth - noteIcon.offsetWidth);
                currentY = Math.min(Math.max(0, newY), window.innerHeight - noteIcon.offsetHeight);

                setTranslate(currentX, currentY);
            });
        }
    }

    function setTranslate(xPos, yPos) {
        noteIcon.style.left = `${xPos}px`;
        noteIcon.style.top = `${yPos}px`;
        noteIcon.style.right = 'auto';
        noteIcon.style.bottom = 'auto';
    }

    function dragEnd(e) {
        if (isDragging) {
            isDragging = false;
            noteIcon.style.transition = 'all 0.1s ease';

            localStorage.setItem(positionKey, JSON.stringify({
                top: noteIcon.style.top,
                left: noteIcon.style.left
            }));

            if (rafId) {
                cancelAnimationFrame(rafId);
            }
        }
    }

    // 加载保存的位置
    const savedPosition = localStorage.getItem(positionKey);
    if (savedPosition) {
        try {
            const { top, left } = JSON.parse(savedPosition);
            setTranslate(parseInt(left), parseInt(top));
        } catch (e) {
            console.error('Failed to load saved position');
        }
    }

    // 修改笔记显示逻辑
    noteContainer.style.position = 'fixed';
    let isVisible = false;

    noteIcon.addEventListener('click', (e) => {
        if (!isDragging) {
            isVisible = !isVisible;

            if (isVisible) {
                const iconRect = noteIcon.getBoundingClientRect();
                const windowWidth = window.innerWidth;
                const windowHeight = window.innerHeight;
                const noteHeight = 300; // 预估笔记窗口高度

                let left = iconRect.right - 10;
                let top = iconRect.top;

                // 检查水平方向是否超出
                if (left + 400 > windowWidth) {
                    left = iconRect.left - 360;
                }

                // 检查垂直方向是否超出
                if (top + noteHeight > windowHeight) {
                    top = windowHeight - noteHeight - 10; // 留出10px边距
                }

                noteContainer.style.top = `${top}px`;
                noteContainer.style.left = `${left}px`;
                noteContainer.style.display = 'block';

                requestAnimationFrame(() => {
                    noteContainer.classList.add('active');
                });
            } else {
                noteContainer.classList.remove('active');
                setTimeout(() => {
                    noteContainer.style.display = 'none';
                }, 300);
            }
        }
    });

    // 修改点击其他地方关闭笔记的逻辑
    document.addEventListener('click', (e) => {
        if (!noteContainer.contains(e.target) && !noteIcon.contains(e.target) && isVisible) {
            isVisible = false;
            noteContainer.classList.remove('active');
            setTimeout(() => {
                noteContainer.style.display = 'none';
            }, 300);
        }
    });

    // 自动保存功能
    let saveTimeout;
    textarea.addEventListener('input', () => {
        clearTimeout(saveTimeout);
        saveTimeout = setTimeout(() => {
            localStorage.setItem(storageKey, textarea.value);
        }, 500); // 延迟500ms保存,避免频繁保存
    });
})();