chore: improve performance

main
Jad 2025-12-20 13:37:52 +08:00
parent 86a7033f3c
commit 23837b25ac
7 changed files with 108 additions and 25 deletions

View File

@ -27,8 +27,7 @@ async function build() {
bundle: true, bundle: true,
format: 'esm', format: 'esm',
outfile: 'dist/app.js', outfile: 'dist/app.js',
loader: { '.png': 'file' }, loader: { '.png': 'dataurl' },
assetNames: 'assets/[name]',
publicPath: '/', publicPath: '/',
minify: process.env.NODE_ENV === 'production' minify: process.env.NODE_ENV === 'production'
}); });

View File

@ -7,6 +7,9 @@
"build": "NODE_ENV=production node build.js", "build": "NODE_ENV=production node build.js",
"serve": "npx serve dist" "serve": "npx serve dist"
}, },
"dependencies": {
"jszip": "^3.10.1"
},
"devDependencies": { "devDependencies": {
"esbuild": "^0.24.0" "esbuild": "^0.24.0"
}, },

View File

@ -7,6 +7,10 @@ settings:
importers: importers:
.: .:
dependencies:
jszip:
specifier: ^3.10.1
version: 3.10.1
devDependencies: devDependencies:
esbuild: esbuild:
specifier: ^0.24.0 specifier: ^0.24.0
@ -164,11 +168,50 @@ packages:
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
esbuild@0.24.2: esbuild@0.24.2:
resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==}
engines: {node: '>=18'} engines: {node: '>=18'}
hasBin: true hasBin: true
immediate@3.0.6:
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
jszip@3.10.1:
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
lie@3.3.0:
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
pako@1.0.11:
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
readable-stream@2.3.8:
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
setimmediate@1.0.5:
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
string_decoder@1.1.1:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
snapshots: snapshots:
'@esbuild/aix-ppc64@0.24.2': '@esbuild/aix-ppc64@0.24.2':
@ -246,6 +289,8 @@ snapshots:
'@esbuild/win32-x64@0.24.2': '@esbuild/win32-x64@0.24.2':
optional: true optional: true
core-util-is@1.0.3: {}
esbuild@0.24.2: esbuild@0.24.2:
optionalDependencies: optionalDependencies:
'@esbuild/aix-ppc64': 0.24.2 '@esbuild/aix-ppc64': 0.24.2
@ -273,3 +318,44 @@ snapshots:
'@esbuild/win32-arm64': 0.24.2 '@esbuild/win32-arm64': 0.24.2
'@esbuild/win32-ia32': 0.24.2 '@esbuild/win32-ia32': 0.24.2
'@esbuild/win32-x64': 0.24.2 '@esbuild/win32-x64': 0.24.2
immediate@3.0.6: {}
inherits@2.0.4: {}
isarray@1.0.0: {}
jszip@3.10.1:
dependencies:
lie: 3.3.0
pako: 1.0.11
readable-stream: 2.3.8
setimmediate: 1.0.5
lie@3.3.0:
dependencies:
immediate: 3.0.6
pako@1.0.11: {}
process-nextick-args@2.0.1: {}
readable-stream@2.3.8:
dependencies:
core-util-is: 1.0.3
inherits: 2.0.4
isarray: 1.0.0
process-nextick-args: 2.0.1
safe-buffer: 5.1.2
string_decoder: 1.1.1
util-deprecate: 1.0.2
safe-buffer@5.1.2: {}
setimmediate@1.0.5: {}
string_decoder@1.1.1:
dependencies:
safe-buffer: 5.1.2
util-deprecate@1.0.2: {}

View File

@ -11,13 +11,12 @@
<script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/jszip@3.10.1/dist/jszip.min.js"></script>
<script> <script>
tailwind.config = { tailwind.config = {
theme: { theme: {
extend: { extend: {
colors: { colors: {
primary: '#10B981', // 对应参考图的绿色 primary: '#10B981',
'primary-hover': '#059669', 'primary-hover': '#059669',
dark: '#1F2937', dark: '#1F2937',
}, },
@ -32,12 +31,11 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style> <style>
body { font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; } body { font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; }
/* 自定义滚动条 */
::-webkit-scrollbar { width: 10px; } ::-webkit-scrollbar { width: 10px; }
::-webkit-scrollbar-track { background: #f1f1f1; } ::-webkit-scrollbar-track { background: #f1f1f1; }
::-webkit-scrollbar-thumb { background: #10B981; border-radius: 4px; } ::-webkit-scrollbar-thumb { background: #10B981; border-radius: 4px; }
/* 步骤条的箭头效果 */
.step-arrow::after { .step-arrow::after {
content: ''; content: '';
position: absolute; position: absolute;
@ -46,7 +44,7 @@
transform: translateY(-50%); transform: translateY(-50%);
border-top: 15px solid transparent; border-top: 15px solid transparent;
border-bottom: 15px solid transparent; border-bottom: 15px solid transparent;
border-left: 15px solid #F3F4F6; /* gray-100 */ border-left: 15px solid #F3F4F6;
z-index: 10; z-index: 10;
} }
@media (min-width: 768px) { @media (min-width: 768px) {
@ -197,7 +195,7 @@
<section class="bg-gray-50 py-16 border-t border-gray-100"> <section class="bg-gray-50 py-16 border-t border-gray-100">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center md:mb-12"> <div class="text-center mb-6 md:mb-12">
<h3 class="text-2xl font-bold text-gray-900" data-i18n="feature.title">功能特点</h3> <h3 class="text-2xl font-bold text-gray-900" data-i18n="feature.title">功能特点</h3>
</div> </div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-8"> <div class="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-8">
@ -241,8 +239,9 @@
<div> <div>
<h6 class="font-bold mb-2 md:mb-4 text-emerald-100" data-i18n="footer.links">链接</h6> <h6 class="font-bold mb-2 md:mb-4 text-emerald-100" data-i18n="footer.links">链接</h6>
<ul class="space-y-1 md:space-y-2 text-sm text-emerald-50"> <ul class="space-y-1 md:space-y-2 text-sm text-emerald-50">
<li><a href="./terms.html" class="hover:text-white" data-i18n="footer.terms">使用条款</a></li> <li><a href="/terms.html" class="hover:text-white" data-i18n="footer.terms">使用条款</a></li>
<li><a href="https://github.com/journey-ad/gemini-watermark-web" target="_blank" class="hover:text-white">Github</a></li> <li><a href="https://github.com/journey-ad/gemini-watermark-web" target="_blank" class="hover:text-white">Github</a></li>
<li><a href="/userscript/gemini-watermark-remover.user.js" target="_blank" class="hover:text-white" data-i18n="nav.userscript">油猴脚本</a></li>
</ul> </ul>
</div> </div>
<div> <div>
@ -265,7 +264,6 @@
</div> </div>
</div> </div>
<script src="/i18n.js"></script> <script src="app.js"></script>
<script src="/app.js"></script>
</body> </body>
</html> </html>

View File

@ -1,16 +1,13 @@
/**
* 主应用程序 - UI 交互逻辑
*/
import { WatermarkEngine } from './core/watermarkEngine.js'; import { WatermarkEngine } from './core/watermarkEngine.js';
import i18n from './i18n.js'; import i18n from './i18n.js';
import JSZip from 'jszip';
// 全局状态 // global state
let engine = null; let engine = null;
let imageQueue = []; let imageQueue = [];
let processedCount = 0; let processedCount = 0;
// DOM 元素 // dom elements references
const uploadArea = document.getElementById('uploadArea'); const uploadArea = document.getElementById('uploadArea');
const fileInput = document.getElementById('fileInput'); const fileInput = document.getElementById('fileInput');
const singlePreview = document.getElementById('singlePreview'); const singlePreview = document.getElementById('singlePreview');
@ -29,7 +26,7 @@ const resetBtn = document.getElementById('resetBtn');
const statusMessage = document.getElementById('statusMessage'); const statusMessage = document.getElementById('statusMessage');
/** /**
* 初始化应用 * initialize the application
*/ */
async function init() { async function init() {
try { try {
@ -48,7 +45,7 @@ async function init() {
} }
/** /**
* 设置语言切换 * setup language switch
*/ */
function setupLanguageSwitch() { function setupLanguageSwitch() {
const btn = document.getElementById('langSwitch'); const btn = document.getElementById('langSwitch');
@ -62,7 +59,7 @@ function setupLanguageSwitch() {
} }
/** /**
* 设置事件监听器 * setup event listeners
*/ */
function setupEventListeners() { function setupEventListeners() {
uploadArea.addEventListener('click', () => fileInput.click()); uploadArea.addEventListener('click', () => fileInput.click());
@ -143,7 +140,7 @@ async function processSingle(item) {
originalCanvas.height = img.height; originalCanvas.height = img.height;
originalCanvas.getContext('2d').drawImage(img, 0, 0); originalCanvas.getContext('2d').drawImage(img, 0, 0);
// 显示图片信息 // update original image info
const watermarkInfo = engine.getWatermarkInfo(img.width, img.height); const watermarkInfo = engine.getWatermarkInfo(img.width, img.height);
originalInfo.innerHTML = ` originalInfo.innerHTML = `
<strong>${i18n.t('info.size')}</strong>${img.width} × ${img.height} px<br> <strong>${i18n.t('info.size')}</strong>${img.width} × ${img.height} px<br>

View File

@ -1,7 +1,7 @@
{ {
"title": "Gemini Watermark Remover - Lossless Watermark Removal Tool", "title": "Gemini Watermark Remover - Lossless Watermark Removal Tool",
"header.title": "Gemini Watermark Remover", "header.title": "Gemini Watermark Remover",
"nav.userscript": "Userscript for Gemini", "nav.userscript": "Install Userscript for Gemini",
"nav.principle": "How It Works?", "nav.principle": "How It Works?",
"main.title": "Gemini AI Watermark Removal", "main.title": "Gemini AI Watermark Removal",
"main.subtitle": "Based on reverse alpha blending algorithm, pure browser-side processing, Free, Fast, and Lossless", "main.subtitle": "Based on reverse alpha blending algorithm, pure browser-side processing, Free, Fast, and Lossless",

View File

@ -1,7 +1,7 @@
{ {
"title": "Gemini Watermark Remover - Gemini 无损去水印工具", "title": "Gemini Watermark Remover - Gemini 无损去水印工具",
"header.title": "Gemini Watermark Remover", "header.title": "Gemini Watermark Remover",
"nav.userscript": "油猴脚本", "nav.userscript": "安装油猴脚本",
"nav.principle": "去水印原理", "nav.principle": "去水印原理",
"main.title": "Gemini AI 图像去水印", "main.title": "Gemini AI 图像去水印",
"main.subtitle": "基于反向 Alpha 混合算法,纯浏览器本地处理,免费、极速、无损", "main.subtitle": "基于反向 Alpha 混合算法,纯浏览器本地处理,免费、极速、无损",
@ -21,7 +21,7 @@
"feature.speed.title": "极速处理", "feature.speed.title": "极速处理",
"feature.speed.desc": "基于现代浏览器技术加速处理,毫秒级响应,无需等待排队", "feature.speed.desc": "基于现代浏览器技术加速处理,毫秒级响应,无需等待排队",
"feature.privacy.title": "隐私安全", "feature.privacy.title": "隐私安全",
"feature.privacy.desc": "纯前端运行,图片数据不离机,绝不上传服务器", "feature.privacy.desc": "浏览器前端本地运行,图片数据不会离开您的设备,绝不会上传服务器",
"feature.free.title": "完全免费", "feature.free.title": "完全免费",
"feature.free.desc": "无次数限制,无隐藏付费,即开即用", "feature.free.desc": "无次数限制,无隐藏付费,即开即用",
"footer.desc": "Gemini 无损去水印工具,本工具仅供学习交流使用", "footer.desc": "Gemini 无损去水印工具,本工具仅供学习交流使用",