// ==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保存,避免频繁保存
});
})();