ocirema commited on
Commit
99fd31f
·
verified ·
1 Parent(s): d2d593b

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +880 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Imagem
3
- emoji: 💻
4
- colorFrom: blue
5
- colorTo: purple
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: imagem
3
+ emoji: 🐳
4
+ colorFrom: gray
5
+ colorTo: pink
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,880 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="pt-PT">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Editor de Imagens Completo</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ .image-container {
11
+ max-width: 100%;
12
+ max-height: 80vh; /* Aumentado de 70vh para 80vh */
13
+ overflow: auto;
14
+ border: 2px dashed #ccc;
15
+ position: relative;
16
+ }
17
+ #canvas {
18
+ max-width: 100%;
19
+ max-height: 80vh; /* Aumentado para corresponder ao container */
20
+ display: block;
21
+ }
22
+ .slider-container {
23
+ width: 100%;
24
+ }
25
+ .slider {
26
+ -webkit-appearance: none;
27
+ width: 100%;
28
+ height: 8px;
29
+ border-radius: 5px;
30
+ background: #d3d3d3;
31
+ outline: none;
32
+ }
33
+ .slider::-webkit-slider-thumb {
34
+ -webkit-appearance: none;
35
+ appearance: none;
36
+ width: 18px;
37
+ height: 18px;
38
+ border-radius: 50%;
39
+ background: #4f46e5;
40
+ cursor: pointer;
41
+ }
42
+ .slider::-moz-range-thumb {
43
+ width: 18px;
44
+ height: 18px;
45
+ border-radius: 50%;
46
+ background: #4f46e5;
47
+ cursor: pointer;
48
+ }
49
+ .filter-option {
50
+ transition: all 0.2s;
51
+ }
52
+ .filter-option:hover {
53
+ transform: scale(1.05);
54
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
55
+ }
56
+ .filter-option.active {
57
+ border: 2px solid #4f46e5;
58
+ }
59
+ .tooltip {
60
+ position: relative;
61
+ display: inline-block;
62
+ }
63
+ .tooltip .tooltiptext {
64
+ visibility: hidden;
65
+ width: 120px;
66
+ background-color: #333;
67
+ color: #fff;
68
+ text-align: center;
69
+ border-radius: 6px;
70
+ padding: 5px;
71
+ position: absolute;
72
+ z-index: 1;
73
+ bottom: 125%;
74
+ left: 50%;
75
+ margin-left: -60px;
76
+ opacity: 0;
77
+ transition: opacity 0.3s;
78
+ font-size: 12px;
79
+ }
80
+ .tooltip:hover .tooltiptext {
81
+ visibility: visible;
82
+ opacity: 1;
83
+ }
84
+ .tab-content {
85
+ display: none;
86
+ }
87
+ .tab-content.active {
88
+ display: block;
89
+ }
90
+ </style>
91
+ </head>
92
+ <body class="bg-gray-100 font-sans">
93
+ <div class="container mx-auto px-4 py-8">
94
+ <h1 class="text-3xl font-bold text-center text-indigo-700 mb-8">Editor de Imagens Completo</h1>
95
+
96
+ <div class="bg-white rounded-lg shadow-lg overflow-hidden">
97
+ <!-- Header -->
98
+ <div class="bg-indigo-600 text-white px-6 py-4 flex justify-between items-center">
99
+ <h2 class="text-xl font-semibold">Editar Imagem</h2>
100
+ <div class="flex space-x-2">
101
+ <button id="resetBtn" class="bg-indigo-700 hover:bg-indigo-800 px-4 py-2 rounded-md text-sm">
102
+ <i class="fas fa-undo-alt mr-1"></i> Reiniciar
103
+ </button>
104
+ <button id="downloadBtn" class="bg-green-600 hover:bg-green-700 px-4 py-2 rounded-md text-sm">
105
+ <i class="fas fa-download mr-1"></i> Descarregar
106
+ </button>
107
+ </div>
108
+ </div>
109
+
110
+ <div class="flex flex-col md:flex-row">
111
+ <!-- Sidebar -->
112
+ <div class="w-full md:w-1/4 bg-gray-50 p-4 border-r">
113
+ <div class="mb-6">
114
+ <h3 class="font-medium text-gray-700 mb-2">Carregar Imagem</h3>
115
+ <div class="flex flex-col space-y-2">
116
+ <input type="file" id="fileInput" accept="image/*" class="hidden">
117
+ <label for="fileInput" class="bg-indigo-100 text-indigo-700 hover:bg-indigo-200 px-4 py-2 rounded-md text-center cursor-pointer">
118
+ <i class="fas fa-folder-open mr-2"></i> Escolher Imagem
119
+ </label>
120
+ </div>
121
+ </div>
122
+
123
+ <div class="tabs mb-6">
124
+ <div class="flex border-b">
125
+ <button class="tab-btn active px-4 py-2 text-indigo-600 border-b-2 border-indigo-600" data-tab="filters">Filtros</button>
126
+ <button class="tab-btn px-4 py-2 text-gray-600" data-tab="adjust">Ajustes</button>
127
+ <button class="tab-btn px-4 py-2 text-gray-600" data-tab="tools">Ferramentas</button>
128
+ </div>
129
+
130
+ <div id="filters" class="tab-content active mt-4">
131
+ <div class="grid grid-cols-3 gap-2">
132
+ <div class="filter-option p-2 bg-white rounded cursor-pointer" data-filter="none">
133
+ <div class="w-full h-16 bg-gray-200 rounded mb-1"></div>
134
+ <p class="text-xs text-center">Original</p>
135
+ </div>
136
+ <div class="filter-option p-2 bg-white rounded cursor-pointer" data-filter="grayscale">
137
+ <div class="w-full h-16 bg-gray-400 rounded mb-1"></div>
138
+ <p class="text-xs text-center">Preto e Branco</p>
139
+ </div>
140
+ <div class="filter-option p-2 bg-white rounded cursor-pointer" data-filter="sepia">
141
+ <div class="w-full h-16 bg-yellow-300 rounded mb-1"></div>
142
+ <p class="text-xs text-center">Sépia</p>
143
+ </div>
144
+ <div class="filter-option p-2 bg-white rounded cursor-pointer" data-filter="invert">
145
+ <div class="w-full h-16 bg-indigo-200 rounded mb-1"></div>
146
+ <p class="text-xs text-center">Inverter</p>
147
+ </div>
148
+ <div class="filter-option p-2 bg-white rounded cursor-pointer" data-filter="blur">
149
+ <div class="w-full h-16 bg-blue-100 rounded mb-1"></div>
150
+ <p class="text-xs text-center">Desfocado</p>
151
+ </div>
152
+ <div class="filter-option p-2 bg-white rounded cursor-pointer" data-filter="hue-rotate">
153
+ <div class="w-full h-16 bg-purple-200 rounded mb-1"></div>
154
+ <p class="text-xs text-center">Matiz</p>
155
+ </div>
156
+ </div>
157
+ </div>
158
+
159
+ <div id="adjust" class="tab-content mt-4">
160
+ <div class="space-y-4">
161
+ <div>
162
+ <label class="block text-sm font-medium text-gray-700 mb-1">Brilho</label>
163
+ <div class="slider-container">
164
+ <input type="range" min="-100" max="100" value="0" class="slider" id="brightnessSlider">
165
+ </div>
166
+ <div class="flex justify-between text-xs text-gray-500 mt-1">
167
+ <span>-100</span>
168
+ <span>0</span>
169
+ <span>100</span>
170
+ </div>
171
+ </div>
172
+
173
+ <div>
174
+ <label class="block text-sm font-medium text-gray-700 mb-1">Contraste</label>
175
+ <div class="slider-container">
176
+ <input type="range" min="-100" max="100" value="0" class="slider" id="contrastSlider">
177
+ </div>
178
+ <div class="flex justify-between text-xs text-gray-500 mt-1">
179
+ <span>-100</span>
180
+ <span>0</span>
181
+ <span>100</span>
182
+ </div>
183
+ </div>
184
+
185
+ <div>
186
+ <label class="block text-sm font-medium text-gray-700 mb-1">Saturação</label>
187
+ <div class="slider-container">
188
+ <input type="range" min="0" max="200" value="100" class="slider" id="saturationSlider">
189
+ </div>
190
+ <div class="flex justify-between text-xs text-gray-500 mt-1">
191
+ <span>0</span>
192
+ <span>100</span>
193
+ <span>200</span>
194
+ </div>
195
+ </div>
196
+
197
+ <div>
198
+ <label class="block text-sm font-medium text-gray-700 mb-1">Temperatura</label>
199
+ <div class="slider-container">
200
+ <input type="range" min="-100" max="100" value="0" class="slider" id="temperatureSlider">
201
+ </div>
202
+ <div class="flex justify-between text-xs text-gray-500 mt-1">
203
+ <span>Frio</span>
204
+ <span>Neutro</span>
205
+ <span>Quente</span>
206
+ </div>
207
+ </div>
208
+ </div>
209
+ </div>
210
+
211
+ <div id="tools" class="tab-content mt-4">
212
+ <div class="grid grid-cols-2 gap-3">
213
+ <button id="cropBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-md text-sm">
214
+ <i class="fas fa-crop mr-1"></i> Cortar
215
+ </button>
216
+ <button id="rotateBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-md text-sm">
217
+ <i class="fas fa-undo mr-1"></i> Rodar
218
+ </button>
219
+ <button id="flipHBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-md text-sm">
220
+ <i class="fas fa-arrows-alt-h mr-1"></i> Inverter H
221
+ </button>
222
+ <button id="flipVBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-md text-sm">
223
+ <i class="fas fa-arrows-alt-v mr-1"></i> Inverter V
224
+ </button>
225
+ <button id="textBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-md text-sm">
226
+ <i class="fas fa-font mr-1"></i> Texto
227
+ </button>
228
+ <button id="drawBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-md text-sm">
229
+ <i class="fas fa-pencil-alt mr-1"></i> Desenhar
230
+ </button>
231
+ </div>
232
+
233
+ <div id="textOptions" class="mt-4 hidden">
234
+ <input type="text" id="textInput" placeholder="Introduza o texto" class="w-full px-3 py-2 border rounded mb-2">
235
+ <div class="flex space-x-2 mb-2">
236
+ <input type="color" id="textColor" value="#000000" class="w-8 h-8">
237
+ <select id="textFont" class="flex-1 px-2 py-1 border rounded">
238
+ <option value="Arial">Arial</option>
239
+ <option value="Times New Roman">Times New Roman</option>
240
+ <option value="Courier New">Courier New</option>
241
+ <option value="Georgia">Georgia</option>
242
+ <option value="Verdana">Verdana</option>
243
+ </select>
244
+ </div>
245
+ <div class="slider-container mb-2">
246
+ <label class="block text-xs text-gray-700 mb-1">Tamanho</label>
247
+ <input type="range" min="10" max="72" value="24" class="slider" id="textSizeSlider">
248
+ </div>
249
+ <button id="addTextBtn" class="w-full bg-indigo-600 text-white px-3 py-2 rounded-md text-sm">
250
+ Adicionar Texto
251
+ </button>
252
+ </div>
253
+
254
+ <div id="drawOptions" class="mt-4 hidden">
255
+ <div class="flex space-x-2 mb-2">
256
+ <input type="color" id="drawColor" value="#000000" class="w-8 h-8">
257
+ <div class="slider-container flex-1">
258
+ <label class="block text-xs text-gray-700 mb-1">Espessura</label>
259
+ <input type="range" min="1" max="20" value="5" class="slider" id="drawSizeSlider">
260
+ </div>
261
+ </div>
262
+ <div class="grid grid-cols-3 gap-2">
263
+ <button id="drawStartBtn" class="bg-indigo-600 text-white px-3 py-2 rounded-md text-sm">
264
+ Começar
265
+ </button>
266
+ <button id="drawClearBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-md text-sm">
267
+ Limpar
268
+ </button>
269
+ <button id="drawSaveBtn" class="bg-green-600 text-white px-3 py-2 rounded-md text-sm">
270
+ Guardar
271
+ </button>
272
+ </div>
273
+ </div>
274
+ </div>
275
+ </div>
276
+ </div>
277
+
278
+ <!-- Main Content - Área de imagem aumentada -->
279
+ <div class="w-full md:w-3/4 p-6">
280
+ <div class="image-container mx-auto bg-gray-100 flex items-center justify-center" id="imageContainer">
281
+ <canvas id="canvas"></canvas>
282
+ <p class="text-gray-500 text-center p-8" id="placeholderText">Carregue uma imagem para começar a editar</p>
283
+ </div>
284
+
285
+ <div class="mt-4 flex justify-center space-x-4" id="cropControls" style="display: none;">
286
+ <button id="cropConfirmBtn" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-md">
287
+ <i class="fas fa-check mr-1"></i> Confirmar
288
+ </button>
289
+ <button id="cropCancelBtn" class="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-md">
290
+ <i class="fas fa-times mr-1"></i> Cancelar
291
+ </button>
292
+ </div>
293
+ </div>
294
+ </div>
295
+ </div>
296
+ </div>
297
+
298
+ <script>
299
+ document.addEventListener('DOMContentLoaded', function() {
300
+ // Elementos DOM
301
+ const fileInput = document.getElementById('fileInput');
302
+ const canvas = document.getElementById('canvas');
303
+ const ctx = canvas.getContext('2d');
304
+ const imageContainer = document.getElementById('imageContainer');
305
+ const placeholderText = document.getElementById('placeholderText');
306
+ const resetBtn = document.getElementById('resetBtn');
307
+ const downloadBtn = document.getElementById('downloadBtn');
308
+
309
+ // Elementos de filtros
310
+ const filterOptions = document.querySelectorAll('.filter-option');
311
+
312
+ // Elementos de ajustes
313
+ const brightnessSlider = document.getElementById('brightnessSlider');
314
+ const contrastSlider = document.getElementById('contrastSlider');
315
+ const saturationSlider = document.getElementById('saturationSlider');
316
+ const temperatureSlider = document.getElementById('temperatureSlider');
317
+
318
+ // Elementos de ferramentas
319
+ const cropBtn = document.getElementById('cropBtn');
320
+ const rotateBtn = document.getElementById('rotateBtn');
321
+ const flipHBtn = document.getElementById('flipHBtn');
322
+ const flipVBtn = document.getElementById('flipVBtn');
323
+ const textBtn = document.getElementById('textBtn');
324
+ const drawBtn = document.getElementById('drawBtn');
325
+
326
+ // Elementos de texto
327
+ const textOptions = document.getElementById('textOptions');
328
+ const textInput = document.getElementById('textInput');
329
+ const textColor = document.getElementById('textColor');
330
+ const textFont = document.getElementById('textFont');
331
+ const textSizeSlider = document.getElementById('textSizeSlider');
332
+ const addTextBtn = document.getElementById('addTextBtn');
333
+
334
+ // Elementos de desenho
335
+ const drawOptions = document.getElementById('drawOptions');
336
+ const drawColor = document.getElementById('drawColor');
337
+ const drawSizeSlider = document.getElementById('drawSizeSlider');
338
+ const drawStartBtn = document.getElementById('drawStartBtn');
339
+ const drawClearBtn = document.getElementById('drawClearBtn');
340
+ const drawSaveBtn = document.getElementById('drawSaveBtn');
341
+
342
+ // Elementos de crop
343
+ const cropControls = document.getElementById('cropControls');
344
+ const cropConfirmBtn = document.getElementById('cropConfirmBtn');
345
+ const cropCancelBtn = document.getElementById('cropCancelBtn');
346
+
347
+ // Tabs
348
+ const tabBtns = document.querySelectorAll('.tab-btn');
349
+ const tabContents = document.querySelectorAll('.tab-content');
350
+
351
+ // Variáveis de estado
352
+ let originalImage = null;
353
+ let currentImage = null;
354
+ let isDrawing = false;
355
+ let isCropping = false;
356
+ let cropStartX, cropStartY, cropEndX, cropEndY;
357
+ let rotationAngle = 0;
358
+ let flipH = false;
359
+ let flipV = false;
360
+ let filters = {
361
+ brightness: 0,
362
+ contrast: 0,
363
+ saturation: 100,
364
+ temperature: 0,
365
+ filter: 'none'
366
+ };
367
+
368
+ // Event Listeners
369
+ fileInput.addEventListener('change', handleImageUpload);
370
+ resetBtn.addEventListener('click', resetImage);
371
+ downloadBtn.addEventListener('click', downloadImage);
372
+
373
+ // Filtros
374
+ filterOptions.forEach(option => {
375
+ option.addEventListener('click', () => {
376
+ document.querySelector('.filter-option.active')?.classList.remove('active');
377
+ option.classList.add('active');
378
+ filters.filter = option.dataset.filter;
379
+ applyFilters();
380
+ });
381
+ });
382
+
383
+ // Ajustes
384
+ brightnessSlider.addEventListener('input', () => {
385
+ filters.brightness = parseInt(brightnessSlider.value);
386
+ applyFilters();
387
+ });
388
+
389
+ contrastSlider.addEventListener('input', () => {
390
+ filters.contrast = parseInt(contrastSlider.value);
391
+ applyFilters();
392
+ });
393
+
394
+ saturationSlider.addEventListener('input', () => {
395
+ filters.saturation = parseInt(saturationSlider.value);
396
+ applyFilters();
397
+ });
398
+
399
+ temperatureSlider.addEventListener('input', () => {
400
+ filters.temperature = parseInt(temperatureSlider.value);
401
+ applyFilters();
402
+ });
403
+
404
+ // Ferramentas
405
+ cropBtn.addEventListener('click', startCropping);
406
+ rotateBtn.addEventListener('click', rotateImage);
407
+ flipHBtn.addEventListener('click', flipHorizontal);
408
+ flipVBtn.addEventListener('click', flipVertical);
409
+ textBtn.addEventListener('click', toggleTextOptions);
410
+ drawBtn.addEventListener('click', toggleDrawOptions);
411
+
412
+ // Texto
413
+ addTextBtn.addEventListener('click', addTextToImage);
414
+
415
+ // Desenho
416
+ drawStartBtn.addEventListener('click', startDrawing);
417
+ drawClearBtn.addEventListener('click', clearDrawing);
418
+ drawSaveBtn.addEventListener('click', saveDrawing);
419
+
420
+ // Crop
421
+ cropConfirmBtn.addEventListener('click', confirmCrop);
422
+ cropCancelBtn.addEventListener('click', cancelCrop);
423
+
424
+ // Tabs
425
+ tabBtns.forEach(btn => {
426
+ btn.addEventListener('click', () => {
427
+ const tabId = btn.dataset.tab;
428
+
429
+ // Remover active de todos os botões e conteúdos
430
+ tabBtns.forEach(b => b.classList.remove('active', 'text-indigo-600', 'border-indigo-600'));
431
+ tabContents.forEach(c => c.classList.remove('active'));
432
+
433
+ // Adicionar active ao botão e conteúdo selecionado
434
+ btn.classList.add('active', 'text-indigo-600', 'border-indigo-600');
435
+ document.getElementById(tabId).classList.add('active');
436
+ });
437
+ });
438
+
439
+ // Canvas events for drawing
440
+ canvas.addEventListener('mousedown', startDraw);
441
+ canvas.addEventListener('mousemove', draw);
442
+ canvas.addEventListener('mouseup', endDraw);
443
+ canvas.addEventListener('mouseout', endDraw);
444
+
445
+ // Canvas events for touch devices
446
+ canvas.addEventListener('touchstart', handleTouchStart);
447
+ canvas.addEventListener('touchmove', handleTouchMove);
448
+ canvas.addEventListener('touchend', handleTouchEnd);
449
+
450
+ // Canvas events for cropping
451
+ canvas.addEventListener('mousedown', startCrop);
452
+ canvas.addEventListener('mousemove', moveCrop);
453
+ canvas.addEventListener('mouseup', endCrop);
454
+
455
+ // Funções
456
+ function handleImageUpload(e) {
457
+ const file = e.target.files[0];
458
+ if (!file) return;
459
+
460
+ const reader = new FileReader();
461
+ reader.onload = function(event) {
462
+ const img = new Image();
463
+ img.onload = function() {
464
+ originalImage = img;
465
+ currentImage = img;
466
+ setupCanvas(img);
467
+ placeholderText.style.display = 'none';
468
+ };
469
+ img.src = event.target.result;
470
+ };
471
+ reader.readAsDataURL(file);
472
+ }
473
+
474
+ function setupCanvas(img) {
475
+ // Ajustar tamanho do canvas para a imagem
476
+ const maxWidth = imageContainer.clientWidth - 40;
477
+ const maxHeight = imageContainer.clientHeight - 40;
478
+
479
+ let width = img.width;
480
+ let height = img.height;
481
+
482
+ if (width > maxWidth) {
483
+ const ratio = maxWidth / width;
484
+ width = maxWidth;
485
+ height = height * ratio;
486
+ }
487
+
488
+ if (height > maxHeight) {
489
+ const ratio = maxHeight / height;
490
+ height = maxHeight;
491
+ width = width * ratio;
492
+ }
493
+
494
+ canvas.width = width;
495
+ canvas.height = height;
496
+
497
+ // Redefinir todos os filtros e transformações
498
+ rotationAngle = 0;
499
+ flipH = false;
500
+ flipV = false;
501
+ filters = {
502
+ brightness: 0,
503
+ contrast: 0,
504
+ saturation: 100,
505
+ temperature: 0,
506
+ filter: 'none'
507
+ };
508
+
509
+ // Redefinir controles deslizantes
510
+ brightnessSlider.value = 0;
511
+ contrastSlider.value = 0;
512
+ saturationSlider.value = 100;
513
+ temperatureSlider.value = 0;
514
+
515
+ // Aplicar filtro "none" (remover qualquer filtro ativo)
516
+ document.querySelector('.filter-option.active')?.classList.remove('active');
517
+ document.querySelector('.filter-option[data-filter="none"]').classList.add('active');
518
+ filters.filter = 'none';
519
+
520
+ drawImage();
521
+ }
522
+
523
+ function drawImage() {
524
+ if (!currentImage) return;
525
+
526
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
527
+
528
+ // Aplicar flip horizontal e vertical
529
+ if (flipH || flipV) {
530
+ ctx.save();
531
+ if (flipH && flipV) {
532
+ ctx.scale(-1, -1);
533
+ ctx.drawImage(currentImage, -canvas.width, -canvas.height, canvas.width, canvas.height);
534
+ } else if (flipH) {
535
+ ctx.scale(-1, 1);
536
+ ctx.drawImage(currentImage, -canvas.width, 0, canvas.width, canvas.height);
537
+ } else if (flipV) {
538
+ ctx.scale(1, -1);
539
+ ctx.drawImage(currentImage, 0, -canvas.height, canvas.width, canvas.height);
540
+ }
541
+ ctx.restore();
542
+ }
543
+ // Aplicar rotação
544
+ else if (rotationAngle !== 0) {
545
+ ctx.save();
546
+ ctx.translate(canvas.width / 2, canvas.height / 2);
547
+ ctx.rotate(rotationAngle * Math.PI / 180);
548
+ ctx.drawImage(currentImage, -canvas.width / 2, -canvas.height / 2, canvas.width, canvas.height);
549
+ ctx.restore();
550
+ }
551
+ // Desenhar normalmente
552
+ else {
553
+ ctx.drawImage(currentImage, 0, 0, canvas.width, canvas.height);
554
+ }
555
+
556
+ applyFilters();
557
+ }
558
+
559
+ function applyFilters() {
560
+ if (!currentImage) return;
561
+
562
+ let filterString = '';
563
+
564
+ // Brightness (brilho)
565
+ if (filters.brightness !== 0) {
566
+ filterString += `brightness(${100 + filters.brightness}%) `;
567
+ }
568
+
569
+ // Contrast (contraste)
570
+ if (filters.contrast !== 0) {
571
+ filterString += `contrast(${100 + filters.contrast}%) `;
572
+ }
573
+
574
+ // Saturation (saturação)
575
+ if (filters.saturation !== 100) {
576
+ filterString += `saturate(${filters.saturation}%) `;
577
+ }
578
+
579
+ // Temperature (temperatura)
580
+ if (filters.temperature !== 0) {
581
+ const temp = filters.temperature;
582
+ if (temp > 0) {
583
+ // Quente (mais vermelho)
584
+ filterString += `sepia(${temp}%) hue-rotate(-${temp/2}deg) saturate(${100 + temp}%) `;
585
+ } else {
586
+ // Frio (mais azul)
587
+ filterString += `sepia(${Math.abs(temp)}%) hue-rotate(${Math.abs(temp)}deg) saturate(${100 + Math.abs(temp)}%) `;
588
+ }
589
+ }
590
+
591
+ // Filtros específicos
592
+ if (filters.filter !== 'none') {
593
+ switch(filters.filter) {
594
+ case 'grayscale':
595
+ filterString += 'grayscale(100%) ';
596
+ break;
597
+ case 'sepia':
598
+ filterString += 'sepia(100%) ';
599
+ break;
600
+ case 'invert':
601
+ filterString += 'invert(100%) ';
602
+ break;
603
+ case 'blur':
604
+ filterString += 'blur(2px) ';
605
+ break;
606
+ case 'hue-rotate':
607
+ filterString += 'hue-rotate(90deg) ';
608
+ break;
609
+ }
610
+ }
611
+
612
+ canvas.style.filter = filterString.trim();
613
+ }
614
+
615
+ function resetImage() {
616
+ if (!originalImage) return;
617
+
618
+ currentImage = originalImage;
619
+ setupCanvas(originalImage);
620
+ drawImage();
621
+
622
+ // Esconder opções de texto e desenho se estiverem visíveis
623
+ textOptions.classList.add('hidden');
624
+ drawOptions.classList.add('hidden');
625
+ isDrawing = false;
626
+ isCropping = false;
627
+ cropControls.style.display = 'none';
628
+ }
629
+
630
+ function downloadImage() {
631
+ if (!currentImage) {
632
+ alert('Por favor, carregue uma imagem primeiro.');
633
+ return;
634
+ }
635
+
636
+ const link = document.createElement('a');
637
+ link.download = 'imagem-editada.png';
638
+ link.href = canvas.toDataURL('image/png');
639
+ link.click();
640
+ }
641
+
642
+ function rotateImage() {
643
+ rotationAngle += 90;
644
+ if (rotationAngle >= 360) rotationAngle = 0;
645
+
646
+ // Trocar largura e altura para rotações de 90 graus
647
+ if (rotationAngle % 180 === 90) {
648
+ const temp = canvas.width;
649
+ canvas.width = canvas.height;
650
+ canvas.height = temp;
651
+ }
652
+
653
+ drawImage();
654
+ }
655
+
656
+ function flipHorizontal() {
657
+ flipH = !flipH;
658
+ drawImage();
659
+ }
660
+
661
+ function flipVertical() {
662
+ flipV = !flipV;
663
+ drawImage();
664
+ }
665
+
666
+ function toggleTextOptions() {
667
+ if (textOptions.classList.contains('hidden')) {
668
+ textOptions.classList.remove('hidden');
669
+ drawOptions.classList.add('hidden');
670
+ isDrawing = false;
671
+ } else {
672
+ textOptions.classList.add('hidden');
673
+ }
674
+ }
675
+
676
+ function toggleDrawOptions() {
677
+ if (drawOptions.classList.contains('hidden')) {
678
+ drawOptions.classList.remove('hidden');
679
+ textOptions.classList.add('hidden');
680
+ } else {
681
+ drawOptions.classList.add('hidden');
682
+ isDrawing = false;
683
+ }
684
+ }
685
+
686
+ function addTextToImage() {
687
+ if (!textInput.value.trim()) {
688
+ alert('Por favor, introduza um texto.');
689
+ return;
690
+ }
691
+
692
+ ctx.font = `${textSizeSlider.value}px ${textFont.value}`;
693
+ ctx.fillStyle = textColor.value;
694
+ ctx.textAlign = 'center';
695
+ ctx.textBaseline = 'middle';
696
+
697
+ const x = canvas.width / 2;
698
+ const y = canvas.height / 2;
699
+
700
+ ctx.fillText(textInput.value, x, y);
701
+
702
+ // Atualizar currentImage para incluir o texto
703
+ const img = new Image();
704
+ img.src = canvas.toDataURL();
705
+ img.onload = function() {
706
+ currentImage = img;
707
+ };
708
+ }
709
+
710
+ function startDrawing() {
711
+ isDrawing = true;
712
+ canvas.style.cursor = 'crosshair';
713
+ }
714
+
715
+ function clearDrawing() {
716
+ drawImage(); // Redesenha a imagem original, limpando o desenho
717
+ }
718
+
719
+ function saveDrawing() {
720
+ isDrawing = false;
721
+ canvas.style.cursor = 'default';
722
+
723
+ // Atualizar currentImage para incluir o desenho
724
+ const img = new Image();
725
+ img.src = canvas.toDataURL();
726
+ img.onload = function() {
727
+ currentImage = img;
728
+ };
729
+ }
730
+
731
+ function startDraw(e) {
732
+ if (!isDrawing) return;
733
+
734
+ isDrawing = true;
735
+ ctx.beginPath();
736
+ ctx.moveTo(e.offsetX, e.offsetY);
737
+ ctx.strokeStyle = drawColor.value;
738
+ ctx.lineWidth = drawSizeSlider.value;
739
+ ctx.lineCap = 'round';
740
+ ctx.lineJoin = 'round';
741
+ }
742
+
743
+ function draw(e) {
744
+ if (!isDrawing) return;
745
+
746
+ ctx.lineTo(e.offsetX, e.offsetY);
747
+ ctx.stroke();
748
+ }
749
+
750
+ function endDraw() {
751
+ if (!isDrawing) return;
752
+
753
+ isDrawing = false;
754
+ }
755
+
756
+ // Funções para touch events
757
+ function handleTouchStart(e) {
758
+ if (!isDrawing) return;
759
+ e.preventDefault();
760
+
761
+ const touch = e.touches[0];
762
+ const mouseEvent = new MouseEvent('mousedown', {
763
+ clientX: touch.clientX,
764
+ clientY: touch.clientY
765
+ });
766
+ canvas.dispatchEvent(mouseEvent);
767
+ }
768
+
769
+ function handleTouchMove(e) {
770
+ if (!isDrawing) return;
771
+ e.preventDefault();
772
+
773
+ const touch = e.touches[0];
774
+ const mouseEvent = new MouseEvent('mousemove', {
775
+ clientX: touch.clientX,
776
+ clientY: touch.clientY
777
+ });
778
+ canvas.dispatchEvent(mouseEvent);
779
+ }
780
+
781
+ function handleTouchEnd(e) {
782
+ if (!isDrawing) return;
783
+ e.preventDefault();
784
+
785
+ const mouseEvent = new MouseEvent('mouseup', {});
786
+ canvas.dispatchEvent(mouseEvent);
787
+ }
788
+
789
+ // Funções para crop
790
+ function startCropping() {
791
+ isCropping = true;
792
+ cropControls.style.display = 'flex';
793
+ canvas.style.cursor = 'crosshair';
794
+ }
795
+
796
+ function startCrop(e) {
797
+ if (!isCropping) return;
798
+
799
+ const rect = canvas.getBoundingClientRect();
800
+ cropStartX = e.clientX - rect.left;
801
+ cropStartY = e.clientY - rect.top;
802
+ }
803
+
804
+ function moveCrop(e) {
805
+ if (!isCropping) return;
806
+
807
+ const rect = canvas.getBoundingClientRect();
808
+ cropEndX = e.clientX - rect.left;
809
+ cropEndY = e.clientY - rect.top;
810
+
811
+ // Desenhar retângulo de seleção
812
+ drawImage(); // Redesenha a imagem original
813
+ ctx.strokeStyle = '#4f46e5';
814
+ ctx.lineWidth = 2;
815
+ ctx.setLineDash([5, 5]);
816
+ ctx.strokeRect(
817
+ cropStartX,
818
+ cropStartY,
819
+ cropEndX - cropStartX,
820
+ cropEndY - cropStartY
821
+ );
822
+ ctx.setLineDash([]);
823
+ }
824
+
825
+ function endCrop(e) {
826
+ if (!isCropping) return;
827
+
828
+ const rect = canvas.getBoundingClientRect();
829
+ cropEndX = e.clientX - rect.left;
830
+ cropEndY = e.clientY - rect.top;
831
+ }
832
+
833
+ function confirmCrop() {
834
+ if (!isCropping) return;
835
+
836
+ // Garantir que as coordenadas estão corretas (pode arrastar em qualquer direção)
837
+ const x = Math.min(cropStartX, cropEndX);
838
+ const y = Math.min(cropStartY, cropEndY);
839
+ const width = Math.abs(cropEndX - cropStartX);
840
+ const height = Math.abs(cropEndY - cropStartY);
841
+
842
+ // Criar um canvas temporário para a imagem cortada
843
+ const tempCanvas = document.createElement('canvas');
844
+ const tempCtx = tempCanvas.getContext('2d');
845
+ tempCanvas.width = width;
846
+ tempCanvas.height = height;
847
+
848
+ // Desenhar a parte cortada no canvas temporário
849
+ tempCtx.drawImage(
850
+ canvas,
851
+ x, y, width, height,
852
+ 0, 0, width, height
853
+ );
854
+
855
+ // Atualizar o canvas principal
856
+ canvas.width = width;
857
+ canvas.height = height;
858
+ ctx.drawImage(tempCanvas, 0, 0);
859
+
860
+ // Atualizar currentImage
861
+ const img = new Image();
862
+ img.src = canvas.toDataURL();
863
+ img.onload = function() {
864
+ currentImage = img;
865
+ };
866
+
867
+ // Resetar estado de crop
868
+ cancelCrop();
869
+ }
870
+
871
+ function cancelCrop() {
872
+ isCropping = false;
873
+ cropControls.style.display = 'none';
874
+ canvas.style.cursor = 'default';
875
+ drawImage(); // Redesenha a imagem sem o retângulo de seleção
876
+ }
877
+ });
878
+ </script>
879
+ <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=ocirema/imagem" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
880
+ </html>