您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在Pinterest详情页环形展开收藏夹
// ==UserScript== // @name Pinterest环形收藏夹 // @namespace http://tampermonkey.net/ // @version 1.0 // @description 在Pinterest详情页环形展开收藏夹 // @author You // @match https://www.pinterest.com/* // @grant none // ==/UserScript== (function () { 'use strict'; // 为环形菜单添加CSS样式 const styleElement = document.createElement('style'); styleElement.textContent = ` .radial-menu-container { position: fixed; z-index: 9999; pointer-events: none; width: 0; height: 0; } .radial-menu { position: absolute; width: 500px; height: 250px; /* 半高,因为是半圆 */ border-radius: 250px 250px 0 0; /* 修改为上半部分为圆形,底部为直线 */ /* 移除背景颜色和阴影效果 */ background-color: transparent; box-shadow: none; pointer-events: auto; transform: translate(-50%, 0); /* 修改为向左平移一半,向下平移0 */ } .radial-menu-item { position: absolute; display: flex; align-items: center; justify-content: center; cursor: pointer; font-weight: bold; color: #333; transition: all 0.2s ease; text-align: center; overflow: hidden; text-overflow: ellipsis; padding: 5px; border-radius: 6px; background-color: rgba(255, 255, 255, 0.85); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } .radial-menu-item:hover { background-color: rgba(230, 0, 35, 0.1); color: #e60023; transform: scale(1.05); } .radial-menu-center { position: absolute; width: 70px; height: 70px; background-color: #e60023; border-radius: 50%; left: 50%; bottom: 20px; /* 放在半圆的底部 */ transform: translate(-50%, 0); display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; cursor: pointer; box-shadow: 0 2px 8px rgba(230, 0, 35, 0.5); z-index: 2; } .radial-menu-ring { position: absolute; border-radius: 0 0 250px 250px; /* 下半圆 */ border: 1px dashed rgba(200, 200, 200, 0.1); bottom: 0; /* 底部对齐 */ left: 50%; transform: translateX(-50%); height: 50%; /* 半高 */ } .radial-menu-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; // 修改透明度为 70% background-color: rgba(0, 0, 0, 0.7); z-index: 9998; pointer-events: auto; } .radial-menu-item-icon { width: 16px; height: 16px; margin-right: 5px; } .radial-menu-item-inner { display: flex; flex-direction: column; align-items: center; justify-content: center; max-width: 100%; } .radial-menu-item-name { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; max-width: 100%; font-size: 12px; } .radial-save-button { background-color: #efefef; border: none; border-radius: 20px; padding: 8px 12px; margin-left: 8px; color: #333; font-weight: bold; display: flex; align-items: center; cursor: pointer; transition: background-color 0.2s; } .radial-save-button:hover { background-color: #e2e2e2; } .radial-save-icon { margin-right: 4px; width: 16px; height: 16px; } `; document.head.appendChild(styleElement); // 存储用户收藏夹的数组 let userBoards = []; let menuVisible = false; let menuContainer = null; let overlayElement = null; let currentPinData = null; // 显示半圆形菜单的函数 function showRadialMenu() { // 先获取用户的收藏夹 fetchUserBoards().then(boards => { // 查找原始保存按钮 const originSaveBtn = document.querySelector('button[aria-label="保存"]'); if (!originSaveBtn) { console.log('未找到原始保存按钮'); return; } // 获取原始保存按钮的位置 const rect = originSaveBtn.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const startY = rect.top; // 创建遮罩层 overlayElement = document.createElement('div'); overlayElement.className = 'radial-menu-overlay'; overlayElement.addEventListener('click', hideRadialMenu); console.log('遮罩层元素创建:', overlayElement); // 添加日志输出 document.body.appendChild(overlayElement); console.log('遮罩层元素添加到文档:', overlayElement); // 添加日志输出 // 创建新的遮罩层(遮罩2) const overlayElement2 = document.createElement('div'); overlayElement2.className = 'radial-menu-overlay2'; // 新的类名 overlayElement2.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'; // 可以根据需要调整样式 overlayElement2.style.position = 'fixed'; overlayElement2.style.top = '0'; overlayElement2.style.left = '0'; overlayElement2.style.right = '0'; overlayElement2.style.bottom = '0'; overlayElement2.style.zIndex = '9997'; // 确保在原有遮罩层之下 overlayElement2.addEventListener('click', hideRadialMenu); document.body.appendChild(overlayElement2); // 创建菜单容器 menuContainer = document.createElement('div'); menuContainer.className = 'radial-menu-container'; menuContainer.style.left = `${centerX}px`; menuContainer.style.top = `${startY}px`; // 创建半圆形菜单 const radialMenu = document.createElement('div'); radialMenu.className = 'radial-menu'; // 创建指示环(半圆) createRing(radialMenu, 100, 100); // 第一圈,修改半径为100 createRing(radialMenu, 220, 220); // 第二圈,修改半径为220 createRing(radialMenu, 340, 340); // 第三圈,修改半径为340 // 根据收藏夹数量自动分配半圆形布局 distributeItemsInSemiCircle(radialMenu, boards); menuContainer.appendChild(radialMenu); document.body.appendChild(menuContainer); menuVisible = true; }); } // 创建指示环(半圆) function createRing(parent, width, height) { const ring = document.createElement('div'); ring.className = 'radial-menu-ring'; ring.style.width = `${width}px`; ring.style.height = `${height / 2}px`; // 半高,形成半圆 parent.appendChild(ring); } // 在半圆中分配项目 function distributeItemsInSemiCircle(radialMenu, boards) { // 第一圈最多6个 createItemsSemiCircle(radialMenu, boards, 0, Math.min(6, boards.length), 100, 50, 90); // 如果收藏夹数量大于6,第二圈最多10个 if (boards.length > 6) { createItemsSemiCircle(radialMenu, boards, 6, Math.min(16, boards.length), 220, 50, 90); } // 如果收藏夹数量大于16,剩余的放在第三圈 if (boards.length > 16) { createItemsSemiCircle(radialMenu, boards, 16, boards.length, 340, 50, 90); } } // 创建半圆形中的项目 - 调整角度计算为下半圆 function createItemsSemiCircle(radialMenu, boards, startIdx, endIdx, radius, width, height) { const totalItems = endIdx - startIdx; for (let i = startIdx; i < endIdx; i++) { const board = boards[i]; // 角度范围从0到π(上半圆) const angle = (Math.PI * (i - startIdx)) / totalItems; const posX = radius * Math.cos(angle); const posY = radius * Math.sin(angle); const item = document.createElement('div'); item.className = 'radial-menu-item'; const itemInner = document.createElement('div'); itemInner.className = 'radial-menu-item-inner'; // 移除添加图标的逻辑 // 添加收藏夹名称 const nameSpan = document.createElement('span'); nameSpan.className = 'radial-menu-item-name'; nameSpan.textContent = board.name; itemInner.appendChild(nameSpan); item.appendChild(itemInner); // 设置高度固定为12px height = 12; // 计算宽度,为标题显示完整的宽度加上5px,最小为60px const tempDiv = document.createElement('div'); tempDiv.style.position = 'absolute'; tempDiv.style.visibility = 'hidden'; tempDiv.style.whiteSpace = 'nowrap'; tempDiv.textContent = board.name; // 复制收藏夹名称的样式 const style = window.getComputedStyle(nameSpan); tempDiv.style.fontFamily = style.fontFamily; tempDiv.style.fontSize = style.fontSize; tempDiv.style.fontWeight = style.fontWeight; tempDiv.style.padding = style.padding; document.body.appendChild(tempDiv); width = Math.max(60, tempDiv.offsetWidth + 5); document.body.removeChild(tempDiv); // 位置计算,对于下半圆形布局 item.style.left = `${250 + posX - width / 2}px`; item.style.top = `${posY - height / 2}px`; item.style.width = `${width}px`; item.style.height = `${height}px`; // 计算旋转角度,使长边朝着圆心 let rotationAngle = (angle - Math.PI / 2 + Math.PI / 2) * (180 / Math.PI); // 如果在圆心左侧,额外旋转180度 if (posX < 0) { rotationAngle += 180; } item.style.transform = `rotate(${rotationAngle}deg)`; item.addEventListener('click', () => { console.log(`保存到"${board.name}"`); hideRadialMenu(); // 保存收藏夹ID和名称 const boardId = board.id; const boardName = board.name; // 使用新方法保存到指定收藏夹 saveToBoard(boardId, boardName); }); radialMenu.appendChild(item); } } // 优化后的保存到指定收藏夹的函数 function saveToBoard(boardId, boardName) { console.log(`正在尝试保存到收藏夹: ${boardName} (ID: ${boardId})`); // 查找选择收藏夹的下拉按钮 const boardSelectionButton = document.querySelector('[data-test-id="PinBetterSaveDropdown"]'); if (!boardSelectionButton) { console.log('未找到收藏夹选择按钮'); return; } // 模拟点击下拉按钮,打开收藏夹列表 triggerMouseEvent(boardSelectionButton, "mousedown"); triggerMouseEvent(boardSelectionButton, "mouseup"); triggerMouseEvent(boardSelectionButton, "click"); // 等待下拉菜单出现 setTimeout(() => { // 查找所有收藏夹项目 const boardItems = document.querySelectorAll('[data-test-id="boardWithoutSection"]'); console.log(`找到${boardItems.length}个收藏夹项目`); for (const item of boardItems) { const titleEl = item.querySelector('.X8m.zDA'); if (titleEl && titleEl.textContent === boardName) { console.log(`找到匹配的收藏夹: ${boardName}`); // 查找收藏夹后面的保存按钮 let saveBtn = item.querySelector('button[aria-label="收藏按钮"], button[aria-label="Save"], button[aria-label="保存"]'); if (!saveBtn) { // 如果没找到,尝试查找收藏夹项目内的任何按钮 saveBtn = item.querySelector('button'); } if (!saveBtn) { // 如果仍然没找到,尝试查找可能包含保存功能的div元素 const divs = item.querySelectorAll('div[role="button"]'); for (const div of divs) { if (div !== item) { // 避免选中整个收藏夹项目 saveBtn = div; break; } } } if (saveBtn) { console.log(`找到收藏夹 "${boardName}" 的按钮元素,点击它`); // 在点击前先添加日志,输出找到的元素详情,有助于调试 console.log('找到的按钮元素:', saveBtn.outerHTML); triggerMouseEvent(saveBtn, "mousedown"); triggerMouseEvent(saveBtn, "mouseup"); triggerMouseEvent(saveBtn, "click"); // 关闭下拉菜单 document.body.click(); return; } else { console.log(`在收藏夹 "${boardName}" 中未找到保存按钮`); } } } console.log(`未找到匹配的收藏夹: ${boardName}`); // 关闭下拉菜单 document.body.click(); }, 500); // 适当增加等待时间确保菜单完全打开 } // 隐藏环形菜单的函数 function hideRadialMenu() { if (menuContainer) { document.body.removeChild(menuContainer); menuContainer = null; } if (overlayElement) { document.body.removeChild(overlayElement); overlayElement = null; } // 新增:移除遮罩层2 const overlayElement2 = document.querySelector('.radial-menu-overlay2'); if (overlayElement2) { document.body.removeChild(overlayElement2); } menuVisible = false; } // 获取用户收藏夹的函数 async function fetchUserBoards() { // 清空之前的收藏夹数据 userBoards = []; // 查找选择收藏夹的下拉按钮 const boardSelectionButton = document.querySelector('[data-test-id="PinBetterSaveDropdown"]'); if (!boardSelectionButton) { console.log('未找到收藏夹选择按钮'); return userBoards; } // 模拟点击下拉按钮,打开收藏夹列表 triggerMouseEvent(boardSelectionButton, "mousedown"); triggerMouseEvent(boardSelectionButton, "mouseup"); triggerMouseEvent(boardSelectionButton, "click"); // 等待下拉菜单出现(等待500ms) await new Promise(resolve => setTimeout(resolve, 500)); // 查找所有收藏夹项目 const boardItems = document.querySelectorAll('[data-test-id="boardWithoutSection"]'); // 解析收藏夹数据 boardItems.forEach(item => { // 查找收藏夹名称 const titleEl = item.querySelector('.X8m.zDA'); if (!titleEl) return; const boardName = titleEl.textContent; // 获取收藏夹ID(如果有) let boardId = ""; // 尝试从data-test-id属性获取ID const testId = item.getAttribute('data-test-id'); if (testId && testId.includes("board-row-")) { boardId = testId.replace("board-row-", ""); } // 查找收藏夹图标(如果有) const iconEl = item.querySelector('img'); const iconUrl = iconEl ? iconEl.src : null; const icon = iconUrl ? `<img src="${iconUrl}" width="16" height="16" style="border-radius: 4px;">` : null; console.log('收藏夹', boardName, '图标 URL:', iconUrl); // 添加日志输出 userBoards.push({ id: boardId, name: boardName, icon: icon, element: item }); }); // 关闭下拉菜单 document.body.click(); console.log(`找到${userBoards.length}个收藏夹`); // 如果没有找到收藏夹,创建一些测试数据 if (userBoards.length === 0) { // 创建更多测试数据以测试多层环状布局 const testBoards = [ "个人资料", "美食", "旅行", "时尚", "艺术", "设计", "科技", "摄影", "健身", "家居", "手工", "园艺", "宠物", "电影", "音乐", "书籍", "动漫", "教育", "建筑", "美容", "汽车", "婚礼", "绘画", "户外", "游戏", "历史", "儿童", "节日", "引用", "灵感" ]; testBoards.forEach((name, index) => { userBoards.push({ id: `test-${index}`, name: name, icon: null, element: null }); }); console.log('未找到收藏夹,使用测试数据'); } return userBoards; } // 用于触发鼠标事件的辅助函数 function triggerMouseEvent(element, eventType) { const mouseEvent = document.createEvent("MouseEvents"); mouseEvent.initEvent(eventType, true, true); element.dispatchEvent(mouseEvent); } // 添加环形保存按钮的函数,现在改为添加A按钮 function addRadialSaveButton() { // 创建一个MutationObserver来监听DOM变化 const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.addedNodes.length) { // 查找原始保存按钮 const saveButtons = document.querySelectorAll('button[aria-label="保存"]:not([data-radial-menu])'); saveButtons.forEach(button => { // 标记按钮为已处理 button.setAttribute('data-radial-menu', 'true'); // 创建A按钮 const radialButton = document.createElement('button'); radialButton.className = 'radial-save-button'; radialButton.innerHTML = ` <svg class="radial-save-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <circle cx="12" cy="12" r="10" stroke="#333" stroke-width="2" fill="none"/> <path d="M6 12 L18 12 M12 6 L12 18" stroke="#333" stroke-width="2"/> </svg> A按钮 `; // 添加点击事件 radialButton.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); // 显示半圆形菜单 showRadialMenu(); return false; }); // 将A按钮添加到保存按钮旁边 const parent = button.parentNode; if (parent && !parent.querySelector('.radial-save-button')) { parent.appendChild(radialButton); } }); } } }); // 开始观察文档 observer.observe(document.body, { childList: true, subtree: true }); // 检查脚本初始加载时已存在的按钮 const existingSaveButtons = document.querySelectorAll('button[aria-label="保存"]:not([data-radial-menu])'); existingSaveButtons.forEach(button => { button.setAttribute('data-radial-menu', 'true'); // 创建A按钮 const radialButton = document.createElement('button'); radialButton.className = 'radial-save-button'; radialButton.innerHTML = ` <svg class="radial-save-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <circle cx="12" cy="12" r="10" stroke="#333" stroke-width="2" fill="none"/> <path d="M6 12 L18 12 M12 6 L12 18" stroke="#333" stroke-width="2"/> </svg> A按钮 `; // 添加点击事件 radialButton.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); // 获取按钮位置,从这里向上展开反向半圆菜单 const rect = radialButton.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const startY = rect.top; // 使用按钮顶部作为菜单底部 // 显示半圆形菜单 showRadialMenu(centerX, startY); return false; }); // 将A按钮添加到保存按钮旁边 const parent = button.parentNode; if (parent && !parent.querySelector('.radial-save-button')) { parent.appendChild(radialButton); } }); } // 初始化脚本 function init() { console.log('Pinterest反向半圆形保存菜单脚本已初始化'); // 添加环形保存按钮 addRadialSaveButton(); // 添加关闭菜单的键盘快捷键(Escape键) document.addEventListener('keydown', (event) => { if (event.key === 'Escape' && menuVisible) { hideRadialMenu(); } }); } // 页面完全加载后运行初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();