Created
September 18, 2025 19:40
-
-
Save karminski/0a9a231275cbf14808ff168266cc9553 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name iPhone 17 Pro Custom Color Palette | |
| // @namespace http://tampermonkey.net/ | |
| // @version 0.3 | |
| // @description iPhone 3D model with full color palette | |
| // @author karminski-牙医 | |
| // @match https://www.apple.com.cn/iphone-17-pro/ | |
| // @grant none | |
| // ==/UserScript== | |
| (function () { | |
| 'use strict'; | |
| console.log('🎯 iPhone Color Palette Script Loaded'); | |
| // 调试工具 | |
| const debug = { | |
| log: (type, ...args) => { | |
| console.log(`🔍 [${type}]`, ...args); | |
| } | |
| }; | |
| // 当前选中的颜色 | |
| let currentCustomColor = null; | |
| // 1. 网络请求拦截(保持原有功能) | |
| function interceptAllRequests() { | |
| const originalFetch = window.fetch; | |
| window.fetch = function (...args) { | |
| const url = typeof args[0] === 'string' ? args[0] : args[0].url; | |
| if (url && (url.includes('.avif') || url.includes('.jpg') || url.includes('.png'))) { | |
| debug.log('Texture', '🎨 Found asset:', url); | |
| } | |
| return originalFetch.apply(this, args); | |
| }; | |
| } | |
| // 2. 监听颜色选择变化(保持原有功能) | |
| function monitorColorChanges() { | |
| document.addEventListener('change', function (e) { | |
| if (e.target && e.target.name === 'colornav-variant-default') { | |
| const selectedColor = e.target.value; | |
| debug.log('Color', '🎨 Color changed to:', selectedColor); | |
| setTimeout(() => applyCustomColorLogic(selectedColor), 100); | |
| } | |
| }); | |
| } | |
| // 3. 颜色转换工具 | |
| const ColorUtils = { | |
| // HEX转HSL | |
| hexToHsl(hex) { | |
| const r = parseInt(hex.slice(1, 3), 16) / 255; | |
| const g = parseInt(hex.slice(3, 5), 16) / 255; | |
| const b = parseInt(hex.slice(5, 7), 16) / 255; | |
| const max = Math.max(r, g, b); | |
| const min = Math.min(r, g, b); | |
| let h, s, l = (max + min) / 2; | |
| if (max === min) { | |
| h = s = 0; | |
| } else { | |
| const d = max - min; | |
| s = l > 0.5 ? d / (2 - max - min) : d / (max + min); | |
| switch (max) { | |
| case r: h = (g - b) / d + (g < b ? 6 : 0); break; | |
| case g: h = (b - r) / d + 2; break; | |
| case b: h = (r - g) / d + 4; break; | |
| } | |
| h /= 6; | |
| } | |
| return [h * 360, s * 100, l * 100]; | |
| }, | |
| // 根据颜色生成CSS滤镜 | |
| colorToFilter(hex) { | |
| const [h, s, l] = this.hexToHsl(hex); | |
| // 基于HSL值计算滤镜参数 | |
| const hueRotate = Math.round(h); | |
| const saturate = Math.round((s / 50) * 100) / 100; | |
| const brightness = Math.round((l / 50) * 100) / 100; | |
| const contrast = saturate > 1 ? saturate * 0.8 : 1; | |
| return `hue-rotate(${hueRotate}deg) saturate(${saturate}) brightness(${brightness}) contrast(${contrast})`; | |
| }, | |
| // 预设颜色配置 | |
| presets: { | |
| '曼巴': '#2a4ba7', | |
| '桃花运': '#c1a9b7', | |
| '死亡芭比': '#c1498f', | |
| 'SU7': '#5cc7a3', | |
| '辐射': '#a4ce64', | |
| '老冰种': '#9fb3bc', | |
| '月壤': '#6b6b6b', | |
| '黄金精神': '#fdbb49', | |
| '茄子': '#865997', | |
| } | |
| }; | |
| // 4. 应用自定义颜色 | |
| function applyCustomColorLogic(color, isHex = false) { | |
| debug.log('Custom', '🎯 Applying color:', color); | |
| const viewer = document.querySelector('.product-viewer-enhanced'); | |
| const canvas = document.querySelector('.product-viewer-enhanced-container'); | |
| if (viewer || canvas) { | |
| let filterValue; | |
| if (isHex) { | |
| // 自定义十六进制颜色 | |
| filterValue = ColorUtils.colorToFilter(color); | |
| currentCustomColor = color; | |
| } else { | |
| // 预设颜色或原始颜色 | |
| if (ColorUtils.presets[color]) { | |
| filterValue = ColorUtils.colorToFilter(ColorUtils.presets[color]); | |
| currentCustomColor = ColorUtils.presets[color]; | |
| } else { | |
| // 原始苹果颜色的滤镜 | |
| const originalFilters = { | |
| 'Orange': 'none', | |
| 'Blue': 'hue-rotate(200deg) saturate(1.2)', | |
| 'Silver': 'saturate(0.2) brightness(1.2)', | |
| }; | |
| filterValue = originalFilters[color] || 'none'; | |
| currentCustomColor = null; | |
| } | |
| } | |
| // 应用滤镜到3D模型容器 | |
| if (canvas) { | |
| canvas.style.filter = filterValue; | |
| debug.log('Filter', '🎨 Applied filter:', filterValue); | |
| } | |
| // 更新调色盘的当前颜色显示 | |
| updateColorDisplay(currentCustomColor || (ColorUtils.presets[color] || '#ff8c00')); | |
| } | |
| } | |
| // 5. 更新颜色显示 | |
| function updateColorDisplay(color) { | |
| const currentColorDiv = document.getElementById('current-color'); | |
| const colorInput = document.getElementById('color-picker'); | |
| if (currentColorDiv) { | |
| currentColorDiv.style.backgroundColor = color; | |
| } | |
| if (colorInput) { | |
| colorInput.value = color; | |
| } | |
| } | |
| // 6. 创建完整的调色盘界面 | |
| function createColorPalette() { | |
| const paletteHTML = ` | |
| <div id="color-palette-panel" style=" | |
| position: fixed; | |
| top: 20px; | |
| right: 20px; | |
| z-index: 99999; | |
| background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 50%, #dee2e6 100%); | |
| color: #343a40; | |
| padding: 24px; | |
| border-radius: 16px; | |
| font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', sans-serif; | |
| box-shadow: 0 20px 40px rgba(0,0,0,0.15), 0 0 0 1px rgba(255,255,255,0.8); | |
| backdrop-filter: blur(20px); | |
| border: 1px solid rgba(255,255,255,0.3); | |
| min-width: 300px; | |
| max-height: 85vh; | |
| overflow-y: auto; | |
| "> | |
| <!-- 标题栏 --> | |
| <div style=" | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 20px; | |
| padding-bottom: 16px; | |
| border-bottom: 1px solid rgba(52, 58, 64, 0.1); | |
| "> | |
| <h3 style=" | |
| margin: 0; | |
| color: #495057; | |
| font-size: 18px; | |
| font-weight: 600; | |
| letter-spacing: -0.02em; | |
| ">🎨 库克, 我们需要这个</h3> | |
| <button onclick="togglePalette()" style=" | |
| background: rgba(108, 117, 125, 0.1); | |
| border: 1px solid rgba(108, 117, 125, 0.2); | |
| color: #6c757d; | |
| width: 28px; | |
| height: 28px; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| font-size: 14px; | |
| font-weight: bold; | |
| transition: all 0.2s ease; | |
| " | |
| onmouseover="this.style.background='rgba(108, 117, 125, 0.15)'; this.style.transform='scale(1.05)'" | |
| onmouseout="this.style.background='rgba(108, 117, 125, 0.1)'; this.style.transform='scale(1)'">×</button> | |
| </div> | |
| <!-- 当前颜色显示 --> | |
| <div style="margin-bottom: 20px;"> | |
| <div style=" | |
| font-size: 14px; | |
| font-weight: 500; | |
| margin-bottom: 12px; | |
| color: #495057; | |
| ">当前颜色</div> | |
| <div style=" | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 16px; | |
| padding: 16px; | |
| background: rgba(248, 249, 250, 0.6); | |
| border-radius: 12px; | |
| border: 1px solid rgba(108, 117, 125, 0.1); | |
| "> | |
| <div id="current-color" style=" | |
| width: 48px; | |
| height: 48px; | |
| border-radius: 12px; | |
| border: 3px solid rgba(108, 117, 125, 0.2); | |
| background: #ff8c00; | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.1); | |
| "></div> | |
| <input type="color" id="color-picker" value="#ff8c00" style=" | |
| width: 48px; | |
| height: 48px; | |
| border: 3px solid rgba(108, 117, 125, 0.2); | |
| border-radius: 12px; | |
| cursor: pointer; | |
| background: none; | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.1); | |
| "> | |
| </div> | |
| </div> | |
| <!-- 预设颜色 --> | |
| <div style="margin-bottom: 20px;"> | |
| <div style=" | |
| font-size: 14px; | |
| font-weight: 500; | |
| margin-bottom: 12px; | |
| color: #495057; | |
| ">预设颜色</div> | |
| <div id="preset-colors" style=" | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 16px; | |
| padding: 16px; | |
| background: rgba(248, 249, 250, 0.6); | |
| border-radius: 12px; | |
| border: 1px solid rgba(108, 117, 125, 0.1); | |
| max-height: 300px; | |
| overflow-y: auto; | |
| scrollbar-width: none; | |
| -ms-overflow-style: none; | |
| "> | |
| ${Object.entries(ColorUtils.presets).map(([name, color]) => ` | |
| <div style=" | |
| text-align: center; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| "> | |
| <button onclick="applyPresetColor('${name}')" style=" | |
| background: ${color}; | |
| border: 3px solid rgba(108, 117, 125, 0.2); | |
| width: 56px; | |
| height: 56px; | |
| border-radius: 12px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| display: block; | |
| margin-bottom: 8px; | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.1); | |
| " | |
| onmouseover="this.style.transform='scale(1.1)'; this.style.boxShadow='0 6px 20px rgba(0,0,0,0.2)'" | |
| onmouseout="this.style.transform='scale(1)'; this.style.boxShadow='0 4px 12px rgba(0,0,0,0.1)'"> | |
| </button> | |
| <div style=" | |
| font-size: 11px; | |
| color: #6c757d; | |
| font-weight: 500; | |
| text-align: center; | |
| line-height: 1.3; | |
| max-width: 60px; | |
| word-wrap: break-word; | |
| ">${name}</div> | |
| </div> | |
| `).join('')} | |
| </div> | |
| </div> | |
| <!-- 控制按钮 --> | |
| <div style=" | |
| display: flex; | |
| gap: 12px; | |
| margin-bottom: 20px; | |
| "> | |
| <button onclick="resetToOriginal()" style=" | |
| flex: 1; | |
| padding: 12px 16px; | |
| background: linear-gradient(135deg, #f8f9fa, #e9ecef); | |
| border: 1px solid rgba(108, 117, 125, 0.2); | |
| color: #495057; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| font-size: 13px; | |
| font-weight: 500; | |
| transition: all 0.2s ease; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.05); | |
| " | |
| onmouseover="this.style.background='linear-gradient(135deg, #e9ecef, #dee2e6)'; this.style.transform='translateY(-1px)'; this.style.boxShadow='0 4px 8px rgba(0,0,0,0.1)'" | |
| onmouseout="this.style.background='linear-gradient(135deg, #f8f9fa, #e9ecef)'; this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(0,0,0,0.05)'">重置</button> | |
| <button onclick="randomColor()" style=" | |
| flex: 1; | |
| padding: 12px 16px; | |
| background: linear-gradient(135deg, #f8f9fa, #e9ecef); | |
| border: 1px solid rgba(108, 117, 125, 0.2); | |
| color: #495057; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| font-size: 13px; | |
| font-weight: 500; | |
| transition: all 0.2s ease; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.05); | |
| " | |
| onmouseover="this.style.background='linear-gradient(135deg, #e9ecef, #dee2e6)'; this.style.transform='translateY(-1px)'; this.style.boxShadow='0 4px 8px rgba(0,0,0,0.1)'" | |
| onmouseout="this.style.background='linear-gradient(135deg, #f8f9fa, #e9ecef)'; this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(0,0,0,0.05)'">随机</button> | |
| </div> | |
| <!-- 调试信息 --> | |
| <div style=" | |
| background: rgba(248, 249, 250, 0.8); | |
| border: 1px solid rgba(108, 117, 125, 0.1); | |
| padding: 12px; | |
| border-radius: 8px; | |
| font-size: 11px; | |
| max-height: 80px; | |
| overflow-y: auto; | |
| " id="debug-mini"> | |
| <div style="color: #28a745; font-weight: 500;">✅ 调色盘已激活</div> | |
| </div> | |
| </div> | |
| `; | |
| document.body.insertAdjacentHTML('beforeend', paletteHTML); | |
| // 添加样式增强 | |
| const style = document.createElement('style'); | |
| style.textContent = ` | |
| #preset-colors button:hover .color-tooltip { | |
| opacity: 1 !important; | |
| } | |
| #preset-colors::-webkit-scrollbar { | |
| width: 4px; | |
| } | |
| #preset-colors::-webkit-scrollbar-track { | |
| background: rgba(248, 249, 250, 0.5); | |
| border-radius: 2px; | |
| } | |
| #preset-colors::-webkit-scrollbar-thumb { | |
| background: rgba(108, 117, 125, 0.3); | |
| border-radius: 2px; | |
| } | |
| #color-palette-panel::-webkit-scrollbar { | |
| width: 6px; | |
| } | |
| #color-palette-panel::-webkit-scrollbar-track { | |
| background: rgba(248, 249, 250, 0.5); | |
| border-radius: 3px; | |
| } | |
| #color-palette-panel::-webkit-scrollbar-thumb { | |
| background: rgba(108, 117, 125, 0.3); | |
| border-radius: 3px; | |
| } | |
| #color-palette-panel::-webkit-scrollbar-thumb:hover { | |
| background: rgba(108, 117, 125, 0.5); | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| } | |
| // 7. 全局函数 | |
| window.applyPresetColor = function (colorName) { | |
| debug.log('Preset', '🎨 Applying preset:', colorName); | |
| applyCustomColorLogic(colorName); | |
| updateDebugMini(`应用预设颜色: ${colorName}`); | |
| }; | |
| window.togglePalette = function () { | |
| const panel = document.getElementById('color-palette-panel'); | |
| if (panel) { | |
| panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; | |
| } | |
| }; | |
| window.resetToOriginal = function () { | |
| debug.log('Reset', '🔄 Resetting to original'); | |
| const canvas = document.querySelector('.product-viewer-enhanced-container'); | |
| if (canvas) { | |
| canvas.style.filter = 'none'; | |
| } | |
| updateColorDisplay('#ff8c00'); | |
| updateDebugMini('已重置为原始颜色'); | |
| }; | |
| window.randomColor = function () { | |
| const randomHex = '#' + Math.floor(Math.random() * 16777215).toString(16); | |
| debug.log('Random', '🎲 Random color:', randomHex); | |
| applyCustomColorLogic(randomHex, true); | |
| updateDebugMini(`随机颜色: ${randomHex}`); | |
| }; | |
| function updateDebugMini(message) { | |
| const debugDiv = document.getElementById('debug-mini'); | |
| if (debugDiv) { | |
| const time = new Date().toLocaleTimeString(); | |
| debugDiv.innerHTML += `<div style="color: #60a5fa;">[${time}] ${message}</div>`; | |
| debugDiv.scrollTop = debugDiv.scrollHeight; | |
| } | |
| } | |
| // 8. 设置颜色选择器事件 | |
| function setupColorPicker() { | |
| setTimeout(() => { | |
| const colorInput = document.getElementById('color-picker'); | |
| if (colorInput) { | |
| colorInput.addEventListener('input', function (e) { | |
| const color = e.target.value; | |
| debug.log('ColorPicker', '🎨 Custom color selected:', color); | |
| applyCustomColorLogic(color, true); | |
| updateDebugMini(`自定义颜色: ${color}`); | |
| }); | |
| } | |
| }, 1000); | |
| } | |
| // 9. 启动函数 | |
| function init() { | |
| debug.log('Init', '🚀 Starting iPhone Color Palette'); | |
| interceptAllRequests(); | |
| monitorColorChanges(); | |
| // 创建调色盘界面 | |
| if (document.readyState === 'loading') { | |
| document.addEventListener('DOMContentLoaded', () => { | |
| createColorPalette(); | |
| setupColorPicker(); | |
| }); | |
| } else { | |
| createColorPalette(); | |
| setupColorPicker(); | |
| } | |
| debug.log('Init', '✅ Color palette ready'); | |
| } | |
| // 启动 | |
| init(); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment