Download index.html from raineye/ds: direct link, hf CLI and curl.
- Browser
- Download file 35.2 kB
-
https://huggingface.co/spaces/raineye/ds/resolve/main/index.html
- Command line
-
hf download hf://spaces/raineye/ds/index.html
-
curl -L -o index.html https://huggingface.co/spaces/raineye/ds/resolve/main/index.html
35.2 kB
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>图片优化大师 | 压缩图片 & 去除水印</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .dropzone { | |
| border: 2px dashed #cbd5e0; | |
| transition: all 0.3s ease; | |
| } | |
| .dropzone.active { | |
| border-color: #4f46e5; | |
| background-color: #f0f4ff; | |
| } | |
| .progress-bar { | |
| transition: width 0.3s ease; | |
| } | |
| .watermark-preview { | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .watermark-box { | |
| position: absolute; | |
| border: 2px dashed red; | |
| background-color: rgba(255, 0, 0, 0.2); | |
| } | |
| .settings-panel { | |
| transition: all 0.3s ease; | |
| overflow: hidden; | |
| } | |
| .settings-panel.closed { | |
| max-height: 0; | |
| } | |
| .settings-panel.open { | |
| max-height: 500px; | |
| } | |
| .tooltip { | |
| position: relative; | |
| } | |
| .tooltip-text { | |
| visibility: hidden; | |
| width: 200px; | |
| background-color: #333; | |
| color: #fff; | |
| text-align: center; | |
| border-radius: 6px; | |
| padding: 5px; | |
| position: absolute; | |
| z-index: 1; | |
| bottom: 125%; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| opacity: 0; | |
| transition: opacity 0.3s; | |
| } | |
| .tooltip:hover .tooltip-text { | |
| visibility: visible; | |
| opacity: 1; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <!-- 顶部标题和按钮 --> | |
| <header class="flex justify-between items-center mb-8"> | |
| <div> | |
| <h1 class="text-3xl font-bold text-indigo-600">图片优化大师</h1> | |
| <p class="text-gray-600">轻松压缩图片并去除水印</p> | |
| </div> | |
| <button id="premium-btn" class="bg-gradient-to-r from-purple-500 to-indigo-600 text-white px-4 py-2 rounded-lg shadow-md hover:shadow-lg transition-all flex items-center"> | |
| <i class="fas fa-crown mr-2"></i> | |
| 升级专业版 | |
| </button> | |
| </header> | |
| <div class="grid grid-cols-1 lg:grid-cols-2 gap-8"> | |
| <!-- 上传区域 --> | |
| <div class="bg-white rounded-xl shadow-md p-6"> | |
| <div id="dropzone" class="dropzone rounded-lg p-8 text-center cursor-pointer mb-6"> | |
| <div class="flex flex-col items-center justify-center"> | |
| <i class="fas fa-cloud-upload-alt text-4xl text-indigo-500 mb-4"></i> | |
| <h3 class="text-xl font-semibold mb-2">拖放图片到此处</h3> | |
| <p class="text-gray-500 mb-4">或点击选择文件</p> | |
| <input type="file" id="fileInput" class="hidden" accept="image/*" multiple> | |
| <button id="browseBtn" class="bg-indigo-500 text-white px-6 py-2 rounded-lg hover:bg-indigo-600 transition"> | |
| 选择图片 | |
| </button> | |
| </div> | |
| </div> | |
| <div id="fileInfo" class="hidden mb-6"> | |
| <div class="flex justify-between items-center mb-2"> | |
| <span class="font-medium">已选文件:</span> | |
| <span id="fileCount" class="text-gray-600">0 个文件</span> | |
| </div> | |
| <div id="fileList" class="border rounded-lg p-3 max-h-40 overflow-y-auto"></div> | |
| </div> | |
| <!-- 高级设置面板 - 默认展开 --> | |
| <div class="mb-6"> | |
| <button id="settingsToggle" class="flex justify-between items-center w-full bg-gray-100 hover:bg-gray-200 px-4 py-3 rounded-lg transition"> | |
| <span class="font-medium">高级设置</span> | |
| <i class="fas fa-chevron-up transition-transform"></i> | |
| </button> | |
| <div id="settingsPanel" class="settings-panel open bg-gray-50 rounded-lg mt-2 px-4 py-3"> | |
| <div class="mb-4"> | |
| <label class="block text-gray-700 mb-2">压缩级别</label> | |
| <div class="flex items-center"> | |
| <input type="range" id="compressionLevel" min="0" max="100" value="70" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> | |
| <span id="compressionValue" class="ml-3 text-gray-700 w-12 text-center">70%</span> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="block text-gray-700 mb-2">输出格式</label> | |
| <select id="outputFormat" class="w-full border rounded-lg px-3 py-2"> | |
| <option value="original">保持原格式</option> | |
| <option value="jpeg">JPEG</option> | |
| <option value="png">PNG</option> | |
| <option value="webp">WebP</option> | |
| </select> | |
| </div> | |
| <div class="flex items-center mb-4"> | |
| <input type="checkbox" id="removeMetadata" class="mr-2" checked> | |
| <label for="removeMetadata" class="text-gray-700">移除元数据</label> | |
| </div> | |
| <div class="flex items-center"> | |
| <input type="checkbox" id="removeWatermark" class="mr-2" checked> | |
| <label for="removeWatermark" class="text-gray-700">去除水印</label> | |
| </div> | |
| </div> | |
| </div> | |
| <button id="processBtn" class="w-full bg-green-500 hover:bg-green-600 text-white py-3 rounded-lg font-medium transition flex items-center justify-center hidden"> | |
| <i class="fas fa-magic mr-2"></i> | |
| 处理图片 | |
| </button> | |
| <div id="progressContainer" class="hidden mt-6"> | |
| <div class="flex justify-between mb-1"> | |
| <span>处理中...</span> | |
| <span id="progressPercent">0%</span> | |
| </div> | |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> | |
| <div id="progressBar" class="progress-bar bg-indigo-600 h-2.5 rounded-full" style="width: 0%"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 水印去除工具 --> | |
| <div class="bg-white rounded-xl shadow-md p-6"> | |
| <h2 class="text-xl font-semibold mb-4">水印去除工具</h2> | |
| <div id="watermarkPreviewContainer" class="hidden"> | |
| <div class="watermark-preview mb-4 border rounded-lg overflow-hidden relative" style="height: 300px;"> | |
| <img id="watermarkPreview" src="#" alt="预览" class="w-full h-full object-contain"> | |
| <div id="watermarkBox" class="watermark-box hidden"></div> | |
| </div> | |
| <div class="flex justify-between mb-4"> | |
| <div> | |
| <label class="block text-gray-700 mb-2">选择水印区域</label> | |
| <div class="flex space-x-2"> | |
| <button id="selectBtn" class="bg-indigo-500 text-white px-3 py-1 rounded hover:bg-indigo-600"> | |
| <i class="fas fa-vector-square mr-1"></i> 选择 | |
| </button> | |
| <button id="clearSelectionBtn" class="bg-gray-200 text-gray-700 px-3 py-1 rounded hover:bg-gray-300"> | |
| <i class="fas fa-eraser mr-1"></i> 清除 | |
| </button> | |
| </div> | |
| </div> | |
| <div class="tooltip"> | |
| <button id="autoDetectBtn" class="bg-purple-500 text-white px-3 py-1 rounded hover:bg-purple-600"> | |
| <i class="fas fa-robot mr-1"></i> 自动检测 | |
| </button> | |
| <span class="tooltip-text">我们的AI将尝试自动检测常见水印模式</span> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="block text-gray-700 mb-2">去除强度</label> | |
| <div class="flex items-center"> | |
| <input type="range" id="removalStrength" min="1" max="10" value="5" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> | |
| <span id="removalStrengthValue" class="ml-3 text-gray-700 w-12 text-center">5</span> | |
| </div> | |
| </div> | |
| <button id="removeWatermarkBtn" class="w-full bg-red-500 hover:bg-red-600 text-white py-3 rounded-lg font-medium transition flex items-center justify-center"> | |
| <i class="fas fa-trash-alt mr-2"></i> | |
| 去除水印 | |
| </button> | |
| </div> | |
| <div id="noImageSelected" class="text-center py-12 text-gray-500"> | |
| <i class="fas fa-image fa-3x mb-4"></i> | |
| <p>未选择用于去除水印的图片。</p> | |
| <p>请先上传图片以使用此工具。</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 结果展示区域 --> | |
| <div id="resultsSection" class="hidden mt-8 bg-white rounded-xl shadow-md p-6"> | |
| <h2 class="text-xl font-semibold mb-4">处理结果</h2> | |
| <div class="flex justify-between items-center mb-4"> | |
| <div> | |
| <span class="text-gray-600">原始大小:</span> | |
| <span id="originalSize" class="font-medium ml-2">-</span> | |
| </div> | |
| <div> | |
| <span class="text-gray-600">压缩后大小:</span> | |
| <span id="compressedSize" class="font-medium ml-2">-</span> | |
| </div> | |
| <div> | |
| <span class="text-gray-600">节省空间:</span> | |
| <span id="savings" class="font-medium ml-2">-</span> | |
| </div> | |
| <button id="downloadAllBtn" class="bg-indigo-500 text-white px-4 py-2 rounded-lg hover:bg-indigo-600 transition flex items-center"> | |
| <i class="fas fa-download mr-2"></i> | |
| 全部下载 | |
| </button> | |
| </div> | |
| <div id="resultsGrid" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4"></div> | |
| </div> | |
| </div> | |
| <script> | |
| // DOM元素 | |
| const dropzone = document.getElementById('dropzone'); | |
| const fileInput = document.getElementById('fileInput'); | |
| const browseBtn = document.getElementById('browseBtn'); | |
| const fileInfo = document.getElementById('fileInfo'); | |
| const fileList = document.getElementById('fileList'); | |
| const fileCount = document.getElementById('fileCount'); | |
| const processBtn = document.getElementById('processBtn'); | |
| const progressContainer = document.getElementById('progressContainer'); | |
| const progressBar = document.getElementById('progressBar'); | |
| const progressPercent = document.getElementById('progressPercent'); | |
| const compressionLevel = document.getElementById('compressionLevel'); | |
| const compressionValue = document.getElementById('compressionValue'); | |
| const outputFormat = document.getElementById('outputFormat'); | |
| const removeMetadata = document.getElementById('removeMetadata'); | |
| const removeWatermark = document.getElementById('removeWatermark'); | |
| const settingsToggle = document.getElementById('settingsToggle'); | |
| const settingsPanel = document.getElementById('settingsPanel'); | |
| const watermarkPreviewContainer = document.getElementById('watermarkPreviewContainer'); | |
| const watermarkPreview = document.getElementById('watermarkPreview'); | |
| const noImageSelected = document.getElementById('noImageSelected'); | |
| const selectBtn = document.getElementById('selectBtn'); | |
| const clearSelectionBtn = document.getElementById('clearSelectionBtn'); | |
| const autoDetectBtn = document.getElementById('autoDetectBtn'); | |
| const removalStrength = document.getElementById('removalStrength'); | |
| const removalStrengthValue = document.getElementById('removalStrengthValue'); | |
| const removeWatermarkBtn = document.getElementById('removeWatermarkBtn'); | |
| const watermarkBox = document.getElementById('watermarkBox'); | |
| const resultsSection = document.getElementById('resultsSection'); | |
| const resultsGrid = document.getElementById('resultsGrid'); | |
| const originalSize = document.getElementById('originalSize'); | |
| const compressedSize = document.getElementById('compressedSize'); | |
| const savings = document.getElementById('savings'); | |
| const downloadAllBtn = document.getElementById('downloadAllBtn'); | |
| const premiumBtn = document.getElementById('premium-btn'); | |
| // 变量 | |
| let selectedFiles = []; | |
| let isSelectingWatermark = false; | |
| let selectionStart = { x: 0, y: 0 }; | |
| let currentImageForWatermark = null; | |
| let processedFiles = []; | |
| // 事件监听 | |
| browseBtn.addEventListener('click', () => fileInput.click()); | |
| fileInput.addEventListener('change', handleFileSelect); | |
| dropzone.addEventListener('dragover', handleDragOver); | |
| dropzone.addEventListener('dragleave', handleDragLeave); | |
| dropzone.addEventListener('drop', handleDrop); | |
| processBtn.addEventListener('click', processImages); | |
| compressionLevel.addEventListener('input', updateCompressionValue); | |
| settingsToggle.addEventListener('click', toggleSettingsPanel); | |
| removalStrength.addEventListener('input', updateRemovalStrengthValue); | |
| selectBtn.addEventListener('click', startWatermarkSelection); | |
| clearSelectionBtn.addEventListener('click', clearWatermarkSelection); | |
| autoDetectBtn.addEventListener('click', autoDetectWatermark); | |
| removeWatermarkBtn.addEventListener('click', processWatermarkRemoval); | |
| downloadAllBtn.addEventListener('click', downloadAllProcessedFiles); | |
| premiumBtn.addEventListener('click', showPremiumModal); | |
| // 水印选择事件 | |
| watermarkPreview.addEventListener('mousedown', startSelection); | |
| document.addEventListener('mousemove', updateSelection); | |
| document.addEventListener('mouseup', endSelection); | |
| // 函数 | |
| function handleFileSelect(e) { | |
| const files = e.target.files || e.dataTransfer.files; | |
| addFilesToSelection(files); | |
| } | |
| function handleDragOver(e) { | |
| e.preventDefault(); | |
| dropzone.classList.add('active'); | |
| } | |
| function handleDragLeave() { | |
| dropzone.classList.remove('active'); | |
| } | |
| function handleDrop(e) { | |
| e.preventDefault(); | |
| dropzone.classList.remove('active'); | |
| const files = e.dataTransfer.files; | |
| addFilesToSelection(files); | |
| } | |
| function addFilesToSelection(files) { | |
| for (let i = 0; i < files.length; i++) { | |
| if (files[i].type.match('image.*')) { | |
| selectedFiles.push(files[i]); | |
| } | |
| } | |
| updateFileList(); | |
| if (selectedFiles.length > 0) { | |
| fileInfo.classList.remove('hidden'); | |
| processBtn.classList.remove('hidden'); | |
| fileCount.textContent = `${selectedFiles.length} 个文件`; | |
| // 设置第一张图片用于水印去除预览 | |
| if (!currentImageForWatermark) { | |
| setImageForWatermarkRemoval(selectedFiles[0]); | |
| } | |
| } | |
| } | |
| function updateFileList() { | |
| fileList.innerHTML = ''; | |
| selectedFiles.forEach((file, index) => { | |
| const div = document.createElement('div'); | |
| div.className = 'flex justify-between items-center py-2 border-b last:border-b-0'; | |
| const fileName = document.createElement('span'); | |
| fileName.className = 'truncate'; | |
| fileName.textContent = file.name; | |
| const fileSize = document.createElement('span'); | |
| fileSize.className = 'text-gray-500 text-sm'; | |
| fileSize.textContent = formatFileSize(file.size); | |
| const removeBtn = document.createElement('button'); | |
| removeBtn.className = 'text-red-500 hover:text-red-700 ml-2'; | |
| removeBtn.innerHTML = '<i class="fas fa-times"></i>'; | |
| removeBtn.addEventListener('click', () => removeFile(index)); | |
| div.appendChild(fileName); | |
| div.appendChild(fileSize); | |
| div.appendChild(removeBtn); | |
| fileList.appendChild(div); | |
| }); | |
| } | |
| function removeFile(index) { | |
| selectedFiles.splice(index, 1); | |
| updateFileList(); | |
| fileCount.textContent = `${selectedFiles.length} 个文件`; | |
| if (selectedFiles.length === 0) { | |
| fileInfo.classList.add('hidden'); | |
| processBtn.classList.add('hidden'); | |
| watermarkPreviewContainer.classList.add('hidden'); | |
| noImageSelected.classList.remove('hidden'); | |
| currentImageForWatermark = null; | |
| } else if (index === 0) { | |
| setImageForWatermarkRemoval(selectedFiles[0]); | |
| } | |
| } | |
| function formatFileSize(bytes) { | |
| if (bytes === 0) return '0 字节'; | |
| const k = 1024; | |
| const sizes = ['字节', 'KB', 'MB', 'GB']; | |
| const i = Math.floor(Math.log(bytes) / Math.log(k)); | |
| return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; | |
| } | |
| function updateCompressionValue() { | |
| compressionValue.textContent = `${compressionLevel.value}%`; | |
| } | |
| function updateRemovalStrengthValue() { | |
| removalStrengthValue.textContent = removalStrength.value; | |
| } | |
| function toggleSettingsPanel() { | |
| settingsPanel.classList.toggle('open'); | |
| const icon = settingsToggle.querySelector('i'); | |
| icon.classList.toggle('fa-chevron-down'); | |
| icon.classList.toggle('fa-chevron-up'); | |
| } | |
| function processImages() { | |
| if (selectedFiles.length === 0) return; | |
| progressContainer.classList.remove('hidden'); | |
| processBtn.classList.add('hidden'); | |
| let processedCount = 0; | |
| processedFiles = []; | |
| // 模拟处理过程(实际应用中会使用真实的图片处理库) | |
| selectedFiles.forEach((file, index) => { | |
| setTimeout(() => { | |
| // 模拟处理时间 | |
| simulateProgress(index, () => { | |
| processedCount++; | |
| // 计算大小(模拟) | |
| const originalSize = file.size; | |
| const compressionRatio = parseInt(compressionLevel.value) / 100; | |
| const compressedSize = Math.max(1000, originalSize * compressionRatio); // 确保最小大小 | |
| // 创建结果对象 | |
| const result = { | |
| originalFile: file, | |
| compressedFile: new File([file], `压缩_${file.name}`, { type: file.type }), | |
| originalSize, | |
| compressedSize, | |
| downloadUrl: URL.createObjectURL(file) // 修正这里: 使用冒号而不是等号 | |
| }; | |
| processedFiles.push(result); | |
| // 更新进度 | |
| const progress = Math.round((processedCount / selectedFiles.length) * 100); | |
| progressBar.style.width = `${progress}%`; | |
| progressPercent.textContent = `${progress}%`; | |
| // 所有文件处理完成 | |
| if (processedCount === selectedFiles.length) { | |
| setTimeout(showResults, 500); | |
| } | |
| }); | |
| }, index * 500); | |
| }); | |
| } | |
| function simulateProgress(index, callback) { | |
| let progress = 0; | |
| const interval = setInterval(() => { | |
| progress += 5; | |
| const overallProgress = Math.min(100, ((index * 100) + progress) / selectedFiles.length); | |
| progressBar.style.width = `${overallProgress}%`; | |
| progressPercent.textContent = `${Math.round(overallProgress)}%`; | |
| if (progress >= 100) { | |
| clearInterval(interval); | |
| callback(); | |
| } | |
| }, 100); | |
| } | |
| function showResults() { | |
| progressContainer.classList.add('hidden'); | |
| resultsSection.classList.remove('hidden'); | |
| // 计算总量 | |
| const totalOriginal = processedFiles.reduce((sum, file) => sum + file.originalSize, 0); | |
| const totalCompressed = processedFiles.reduce((sum, file) => sum + file.compressedSize, 0); | |
| const savingsPercentage = ((totalOriginal - totalCompressed) / totalOriginal * 100).toFixed(1); | |
| originalSize.textContent = formatFileSize(totalOriginal); | |
| compressedSize.textContent = formatFileSize(totalCompressed); | |
| savings.textContent = `${savingsPercentage}% (${formatFileSize(totalOriginal - totalCompressed)})`; | |
| // 显示结果 | |
| resultsGrid.innerHTML = ''; | |
| processedFiles.forEach((file, index) => { | |
| const reader = new FileReader(); | |
| reader.onload = (e) => { | |
| const resultCard = document.createElement('div'); | |
| resultCard.className = 'bg-gray-50 rounded-lg overflow-hidden border'; | |
| const imgContainer = document.createElement('div'); | |
| imgContainer.className = 'relative pt-[100%]'; | |
| const img = document.createElement('img'); | |
| img.src = e.target.result; | |
| img.className = 'absolute top-0 left-0 w-full h-full object-cover'; | |
| const infoContainer = document.createElement('div'); | |
| infoContainer.className = 'p-3'; | |
| const fileName = document.createElement('div'); | |
| fileName.className = 'font-medium truncate mb-1'; | |
| fileName.textContent = file.originalFile.name; | |
| const sizeInfo = document.createElement('div'); | |
| sizeInfo.className = 'flex justify-between text-sm text-gray-600 mb-2'; | |
| const originalSizeSpan = document.createElement('span'); | |
| originalSizeSpan.textContent = formatFileSize(file.originalSize); | |
| const compressedSizeSpan = document.createElement('span'); | |
| compressedSizeSpan.className = 'text-green-600 font-medium'; | |
| compressedSizeSpan.textContent = formatFileSize(file.compressedSize); | |
| sizeInfo.appendChild(originalSizeSpan); | |
| sizeInfo.appendChild(compressedSizeSpan); | |
| const downloadBtn = document.createElement('button'); | |
| downloadBtn.className = 'w-full bg-indigo-500 hover:bg-indigo-600 text-white py-2 rounded text-sm transition flex items-center justify-center'; | |
| downloadBtn.innerHTML = '<i class="fas fa-download mr-2"></i> 下载'; | |
| downloadBtn.addEventListener('click', () => downloadFile(file)); | |
| infoContainer.appendChild(fileName); | |
| infoContainer.appendChild(sizeInfo); | |
| infoContainer.appendChild(downloadBtn); | |
| imgContainer.appendChild(img); | |
| resultCard.appendChild(imgContainer); | |
| resultCard.appendChild(infoContainer); | |
| resultsGrid.appendChild(resultCard); | |
| }; | |
| reader.readAsDataURL(file.originalFile); | |
| }); | |
| } | |
| function downloadFile(file) { | |
| const a = document.createElement('a'); | |
| a.href = file.downloadUrl; | |
| a.download = file.compressedFile.name; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| } | |
| function downloadAllProcessedFiles() { | |
| processedFiles.forEach((file, index) => { | |
| setTimeout(() => { | |
| downloadFile(file); | |
| }, index * 500); | |
| }); | |
| } | |
| function setImageForWatermarkRemoval(file) { | |
| const reader = new FileReader(); | |
| reader.onload = (e) => { | |
| watermarkPreview.src = e.target.result; | |
| watermarkPreviewContainer.classList.remove('hidden'); | |
| noImageSelected.classList.add('hidden'); | |
| currentImageForWatermark = file; | |
| clearWatermarkSelection(); | |
| }; | |
| reader.readAsDataURL(file); | |
| } | |
| function startWatermarkSelection() { | |
| isSelectingWatermark = true; | |
| watermarkBox.classList.remove('hidden'); | |
| selectBtn.classList.add('bg-indigo-600'); | |
| } | |
| function clearWatermarkSelection() { | |
| watermarkBox.classList.add('hidden'); | |
| watermarkBox.style.width = '0'; | |
| watermarkBox.style.height = '0'; | |
| selectBtn.classList.remove('bg-indigo-600'); | |
| isSelectingWatermark = false; | |
| } | |
| function startSelection(e) { | |
| if (!isSelectingWatermark) return; | |
| e.preventDefault(); | |
| const rect = watermarkPreview.getBoundingClientRect(); | |
| selectionStart = { | |
| x: e.clientX - rect.left, | |
| y: e.clientY - rect.top | |
| }; | |
| watermarkBox.style.left = `${selectionStart.x}px`; | |
| watermarkBox.style.top = `${selectionStart.y}px`; | |
| watermarkBox.style.width = '0'; | |
| watermarkBox.style.height = '0'; | |
| } | |
| function updateSelection(e) { | |
| if (!isSelectingWatermark || !selectionStart) return; | |
| const rect = watermarkPreview.getBoundingClientRect(); | |
| const currentX = e.clientX - rect.left; | |
| const currentY = e.clientY - rect.top; | |
| const width = currentX - selectionStart.x; | |
| const height = currentY - selectionStart.y; | |
| watermarkBox.style.width = `${Math.abs(width)}px`; | |
| watermarkBox.style.height = `${Math.abs(height)}px`; | |
| if (width < 0) { | |
| watermarkBox.style.left = `${currentX}px`; | |
| } | |
| if (height < 0) { | |
| watermarkBox.style.top = `${currentY}px`; | |
| } | |
| } | |
| function endSelection() { | |
| if (!isSelectingWatermark) return; | |
| isSelectingWatermark = false; | |
| selectBtn.classList.remove('bg-indigo-600'); | |
| } | |
| function autoDetectWatermark() { | |
| // 模拟AI检测 | |
| watermarkBox.classList.remove('hidden'); | |
| // 随机位置用于演示(实际应用中会使用真实的检测) | |
| const rect = watermarkPreview.getBoundingClientRect(); | |
| const width = Math.min(100, rect.width * 0.3); | |
| const height = Math.min(50, rect.height * 0.1); | |
| const left = Math.random() * (rect.width - width); | |
| const top = rect.height - height - 10; // 通常在底部 | |
| watermarkBox.style.left = `${left}px`; | |
| watermarkBox.style.top = `${top}px`; | |
| watermarkBox.style.width = `${width}px`; | |
| watermarkBox.style.height = `${height}px`; | |
| // 显示成功消息 | |
| const originalText = autoDetectBtn.innerHTML; | |
| autoDetectBtn.innerHTML = '<i class="fas fa-check mr-1"></i> 已检测!'; | |
| autoDetectBtn.classList.remove('bg-purple-500'); | |
| autoDetectBtn.classList.add('bg-green-500'); | |
| setTimeout(() => { | |
| autoDetectBtn.innerHTML = originalText; | |
| autoDetectBtn.classList.add('bg-purple-500'); | |
| autoDetectBtn.classList.remove('bg-green-500'); | |
| }, 2000); | |
| } | |
| function processWatermarkRemoval() { | |
| if (!watermarkBox || watermarkBox.classList.contains('hidden')) { | |
| alert('请先选择水印区域'); | |
| return; | |
| } | |
| // 模拟处理过程 | |
| removeWatermarkBtn.disabled = true; | |
| removeWatermarkBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> 处理中...'; | |
| setTimeout(() => { | |
| // 实际应用中会真正处理图片 | |
| // 演示中只显示"成功"消息 | |
| removeWatermarkBtn.innerHTML = '<i class="fas fa-check mr-2"></i> 水印已去除!'; | |
| removeWatermarkBtn.classList.remove('bg-red-500'); | |
| removeWatermarkBtn.classList.add('bg-green-500'); | |
| setTimeout(() => { | |
| removeWatermarkBtn.innerHTML = '<i class="fas fa-trash-alt mr-2"></i> 去除水印'; | |
| removeWatermarkBtn.classList.add('bg-red-500'); | |
| removeWatermarkBtn.classList.remove('bg-green-500'); | |
| removeWatermarkBtn.disabled = false; | |
| }, 2000); | |
| }, 2000); | |
| } | |
| function showPremiumModal() { | |
| // 创建模态框 | |
| const modal = document.createElement('div'); | |
| modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50'; | |
| modal.innerHTML = ` | |
| <div class="bg-white rounded-xl p-6 max-w-md w-full mx-4"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h3 class="text-xl font-bold text-indigo-600">升级专业版</h3> | |
| <button id="closeModal" class="text-gray-500 hover:text-gray-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="mb-6"> | |
| <div class="bg-indigo-50 rounded-lg p-4 mb-4"> | |
| <div class="flex items-center mb-2"> | |
| <i class="fas fa-check-circle text-green-500 mr-2"></i> | |
| <span>自动去除所有水印</span> | |
| </div> | |
| <div class="flex items-center mb-2"> | |
| <i class="fas fa-check-circle text-green-500 mr-2"></i> | |
| <span>批量处理无限图片</span> | |
| </div> | |
| <div class="flex items-center"> | |
| <i class="fas fa-check-circle text-green-500 mr-2"></i> | |
| <span>优先技术支持</span> | |
| </div> | |
| </div> | |
| <div class="text-center py-4"> | |
| <p class="text-3xl font-bold text-gray-800 mb-2">¥9.99<span class="text-lg text-gray-600">/月</span></p> | |
| <p class="text-gray-600">或 ¥99.99/年 (节省20%)</p> | |
| </div> | |
| </div> | |
| <button id="upgradeBtn" class="w-full bg-gradient-to-r from-purple-500 to-indigo-600 text-white py-3 rounded-lg font-medium hover:opacity-90 transition flex items-center justify-center"> | |
| <i class="fas fa-crown mr-2"></i> | |
| 立即升级 | |
| </button> | |
| </div> | |
| `; | |
| document.body.appendChild(modal); | |
| // 添加事件监听 | |
| modal.querySelector('#closeModal').addEventListener('click', () => { | |
| modal.remove(); | |
| }); | |
| modal.querySelector('#upgradeBtn').addEventListener('click', () => { | |
| alert('感谢选择专业版! 正在跳转支付...'); | |
| modal.remove(); | |
| }); | |
| // 点击外部关闭模态框 | |
| modal.addEventListener('click', (e) => { | |
| if (e.target === modal) { | |
| modal.remove(); | |
| } | |
| }); | |
| } | |
| // 初始化 | |
| updateCompressionValue(); | |
| updateRemovalStrengthValue(); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=raineye/ds" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |