akra35567 commited on
Commit
2c77255
Β·
verified Β·
1 Parent(s): cf5e572

Upload app.js

Browse files
Files changed (1) hide show
  1. js/app.js +494 -0
js/app.js ADDED
@@ -0,0 +1,494 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * ═══════════════════════════════════════════════════════════
3
+ * SOFTEDGE CORPORATION - APP.JS
4
+ * Sistema completo de interaΓ§Γ΅es e animaΓ§Γ΅es
5
+ * VersΓ£o: 2.0 - Ultra Moderno (2025)
6
+ * ═══════════════════════════════════════════════════════════
7
+ */
8
+
9
+ // ═══════════════════════════════════════════════════════════
10
+ // 🎯 INICIALIZAÇÃO PRINCIPAL
11
+ // ═══════════════════════════════════════════════════════════
12
+ class SoftEdgeApp {
13
+ constructor() {
14
+ this.init();
15
+ }
16
+
17
+ init() {
18
+ // Esperar DOM estar pronto
19
+ if (document.readyState === 'loading') {
20
+ document.addEventListener('DOMContentLoaded', () => this.start());
21
+ } else {
22
+ this.start();
23
+ }
24
+ }
25
+
26
+ start() {
27
+ console.log('%cπŸš€ SoftEdge Corporation', 'font-size: 20px; color: #06b6d4; font-weight: bold;');
28
+ console.log('%cWebsite carregado com sucesso!', 'color: #10b981;');
29
+
30
+ // Inicializar todos os mΓ³dulos
31
+ this.initLucideIcons();
32
+ this.initSmoothScroll();
33
+ this.initScrollAnimations();
34
+ this.initParallaxEffects();
35
+ this.initTypewriterEffect();
36
+ this.initCursorEffect();
37
+ this.initLazyLoading();
38
+ this.initDarkMode();
39
+ this.initPerformanceMonitor();
40
+ this.initProgressBar();
41
+ this.initBackToTop();
42
+ this.initFormEnhancements();
43
+ this.initNavigationEffects();
44
+ this.initPreloader();
45
+ this.initKeyboardShortcuts();
46
+ }
47
+
48
+ // ═══════════════════════════════════════════════════════════
49
+ // 🎨 ÍCONES LUCIDE
50
+ // ═══════════════════════════════════════════════════════════
51
+ initLucideIcons() {
52
+ if (typeof lucide !== 'undefined') {
53
+ lucide.createIcons();
54
+ }
55
+ }
56
+
57
+ // ═══════════════════════════════════════════════════════════
58
+ // 🎒 SMOOTH SCROLL (Nativo, sem bibliotecas)
59
+ // ═══════════════════════════════════════════════════════════
60
+ initSmoothScroll() {
61
+ document.querySelectorAll('a[href^="#"]').forEach(anchor => {
62
+ anchor.addEventListener('click', (e) => {
63
+ const href = anchor.getAttribute('href');
64
+ if (href === '#') return;
65
+
66
+ e.preventDefault();
67
+ const target = document.querySelector(href);
68
+
69
+ if (target) {
70
+ const headerHeight = document.querySelector('header')?.offsetHeight || 0;
71
+ const targetPosition = target.getBoundingClientRect().top + window.pageYOffset - headerHeight;
72
+
73
+ window.scrollTo({
74
+ top: targetPosition,
75
+ behavior: 'smooth'
76
+ });
77
+ }
78
+ });
79
+ });
80
+ }
81
+
82
+ // ═══════════════════════════════════════════════════════════
83
+ // ✨ ANIMAÇÕES NO SCROLL (Intersection Observer)
84
+ // ═══════════════════════════════════════════════════════════
85
+ initScrollAnimations() {
86
+ const observerOptions = {
87
+ threshold: 0.1,
88
+ rootMargin: '0px 0px -50px 0px'
89
+ };
90
+
91
+ const observer = new IntersectionObserver((entries) => {
92
+ entries.forEach((entry, index) => {
93
+ if (entry.isIntersecting) {
94
+ setTimeout(() => {
95
+ entry.target.classList.add('animate-in');
96
+ }, index * 100); // Efeito cascata
97
+ observer.unobserve(entry.target);
98
+ }
99
+ });
100
+ }, observerOptions);
101
+
102
+ // Observar elementos com classes especΓ­ficas
103
+ const animateElements = document.querySelectorAll(`
104
+ section,
105
+ .card,
106
+ .group,
107
+ .project-card,
108
+ .service-card,
109
+ h1, h2, h3,
110
+ p:not(footer p)
111
+ `);
112
+
113
+ animateElements.forEach((el) => {
114
+ el.classList.add('will-animate');
115
+ observer.observe(el);
116
+ });
117
+ }
118
+
119
+ // ═══════════════════════════════════════════════════════════
120
+ // 🌊 EFEITO PARALLAX
121
+ // ═══════════════════════���═══════════════════════════════════
122
+ initParallaxEffects() {
123
+ let ticking = false;
124
+
125
+ const parallaxElements = document.querySelectorAll('[data-parallax]');
126
+
127
+ if (parallaxElements.length === 0) return;
128
+
129
+ window.addEventListener('scroll', () => {
130
+ if (!ticking) {
131
+ window.requestAnimationFrame(() => {
132
+ const scrolled = window.pageYOffset;
133
+
134
+ parallaxElements.forEach((el) => {
135
+ const speed = parseFloat(el.dataset.parallax) || 0.5;
136
+ const yPos = -(scrolled * speed);
137
+ el.style.transform = `translate3d(0, ${yPos}px, 0)`;
138
+ });
139
+
140
+ ticking = false;
141
+ });
142
+ ticking = true;
143
+ }
144
+ });
145
+ }
146
+
147
+ // ═══════════════════════════════════════════════════════════
148
+ // ⌨️ EFEITO TYPEWRITER (MÑquina de escrever)
149
+ // ═══════════════════════════════════════════════════════════
150
+ initTypewriterEffect() {
151
+ const typewriterElements = document.querySelectorAll('[data-typewriter]');
152
+
153
+ typewriterElements.forEach((element) => {
154
+ const text = element.textContent;
155
+ const speed = parseInt(element.dataset.typewriterSpeed) || 50;
156
+ element.textContent = '';
157
+ element.style.opacity = '1';
158
+
159
+ let index = 0;
160
+ const type = () => {
161
+ if (index < text.length) {
162
+ element.textContent += text.charAt(index);
163
+ index++;
164
+ setTimeout(type, speed);
165
+ }
166
+ };
167
+
168
+ // Iniciar quando o elemento estiver visΓ­vel
169
+ const observer = new IntersectionObserver((entries) => {
170
+ if (entries[0].isIntersecting) {
171
+ type();
172
+ observer.disconnect();
173
+ }
174
+ });
175
+ observer.observe(element);
176
+ });
177
+ }
178
+
179
+ // ═══════════════════════════════════════════════════════════
180
+ // πŸ–±οΈ CURSOR CUSTOMIZADO
181
+ // ═══════════════════════════════════════════════════════════
182
+ initCursorEffect() {
183
+ // Apenas em desktop
184
+ if (window.matchMedia('(pointer: coarse)').matches) return;
185
+
186
+ const cursor = document.createElement('div');
187
+ cursor.className = 'custom-cursor';
188
+ document.body.appendChild(cursor);
189
+
190
+ const cursorFollower = document.createElement('div');
191
+ cursorFollower.className = 'custom-cursor-follower';
192
+ document.body.appendChild(cursorFollower);
193
+
194
+ let mouseX = 0, mouseY = 0;
195
+ let cursorX = 0, cursorY = 0;
196
+ let followerX = 0, followerY = 0;
197
+
198
+ document.addEventListener('mousemove', (e) => {
199
+ mouseX = e.clientX;
200
+ mouseY = e.clientY;
201
+ });
202
+
203
+ const animateCursor = () => {
204
+ // Cursor principal (rΓ‘pido)
205
+ cursorX += (mouseX - cursorX) * 0.3;
206
+ cursorY += (mouseY - cursorY) * 0.3;
207
+ cursor.style.transform = `translate(${cursorX}px, ${cursorY}px)`;
208
+
209
+ // Cursor follower (mais lento)
210
+ followerX += (mouseX - followerX) * 0.1;
211
+ followerY += (mouseY - followerY) * 0.1;
212
+ cursorFollower.style.transform = `translate(${followerX}px, ${followerY}px)`;
213
+
214
+ requestAnimationFrame(animateCursor);
215
+ };
216
+ animateCursor();
217
+
218
+ // Efeitos em elementos clicΓ‘veis
219
+ document.querySelectorAll('a, button, [role="button"]').forEach((el) => {
220
+ el.addEventListener('mouseenter', () => {
221
+ cursor.classList.add('cursor-hover');
222
+ cursorFollower.classList.add('cursor-hover');
223
+ });
224
+ el.addEventListener('mouseleave', () => {
225
+ cursor.classList.remove('cursor-hover');
226
+ cursorFollower.classList.remove('cursor-hover');
227
+ });
228
+ });
229
+ }
230
+
231
+ // ═══════════════════════════════════════════════════════════
232
+ // πŸ–ΌοΈ LAZY LOADING DE IMAGENS
233
+ // ═══════════════════════════════════════════════════════════
234
+ initLazyLoading() {
235
+ const images = document.querySelectorAll('img[data-src]');
236
+
237
+ const imageObserver = new IntersectionObserver((entries) => {
238
+ entries.forEach((entry) => {
239
+ if (entry.isIntersecting) {
240
+ const img = entry.target;
241
+ img.src = img.dataset.src;
242
+ img.removeAttribute('data-src');
243
+ img.classList.add('loaded');
244
+ imageObserver.unobserve(img);
245
+ }
246
+ });
247
+ });
248
+
249
+ images.forEach((img) => imageObserver.observe(img));
250
+ }
251
+
252
+ // ════════════���══════════════════════════════════════════════
253
+ // πŸŒ™ DARK MODE AVANΓ‡ADO
254
+ // ═══════════════════════════════════════════════════════════
255
+ initDarkMode() {
256
+ const darkModeToggle = document.createElement('button');
257
+ darkModeToggle.innerHTML = '<i data-lucide="moon" class="w-5 h-5"></i>';
258
+ darkModeToggle.className = 'fixed bottom-6 right-6 z-50 w-12 h-12 bg-slate-900/80 backdrop-blur-xl border border-white/10 text-white rounded-full shadow-lg hover:scale-110 transition-all duration-300 flex items-center justify-center';
259
+ darkModeToggle.setAttribute('aria-label', 'Toggle Dark Mode');
260
+ darkModeToggle.setAttribute('title', 'Alternar modo escuro');
261
+ document.body.appendChild(darkModeToggle);
262
+
263
+ // Verificar preferΓͺncia salva
264
+ const currentTheme = localStorage.getItem('theme');
265
+ if (currentTheme === 'light' ||
266
+ (!currentTheme && window.matchMedia('(prefers-color-scheme: light)').matches)) {
267
+ document.documentElement.classList.add('light-mode');
268
+ darkModeToggle.innerHTML = '<i data-lucide="sun" class="w-5 h-5"></i>';
269
+ }
270
+
271
+ darkModeToggle.addEventListener('click', () => {
272
+ document.documentElement.classList.toggle('light-mode');
273
+ const isLight = document.documentElement.classList.contains('light-mode');
274
+
275
+ localStorage.setItem('theme', isLight ? 'light' : 'dark');
276
+ darkModeToggle.innerHTML = isLight
277
+ ? '<i data-lucide="sun" class="w-5 h-5"></i>'
278
+ : '<i data-lucide="moon" class="w-5 h-5"></i>';
279
+
280
+ lucide.createIcons();
281
+
282
+ // AnimaΓ§Γ£o de transiΓ§Γ£o
283
+ document.body.style.transition = 'background-color 0.3s ease';
284
+ });
285
+
286
+ lucide.createIcons();
287
+ }
288
+
289
+ // ═══════════════════════════════════════════════════════════
290
+ // πŸ“Š MONITOR DE PERFORMANCE
291
+ // ═══════════════════════════════════════════════════════════
292
+ initPerformanceMonitor() {
293
+ if ('performance' in window) {
294
+ window.addEventListener('load', () => {
295
+ setTimeout(() => {
296
+ const perfData = performance.getEntriesByType('navigation')[0];
297
+ const loadTime = perfData.loadEventEnd - perfData.fetchStart;
298
+
299
+ console.log(`%c⚑ Performance`, 'color: #f59e0b; font-weight: bold;');
300
+ console.log(`Tempo de carregamento: ${(loadTime / 1000).toFixed(2)}s`);
301
+
302
+ // Mostrar notificaΓ§Γ£o se o site carregar rΓ‘pido
303
+ if (loadTime < 1500) {
304
+ this.showNotification('⚑ Site carregado em menos de 1.5s!', 'success');
305
+ }
306
+ }, 0);
307
+ });
308
+ }
309
+ }
310
+
311
+ // ═══════════════════════════════════════════════════════════
312
+ // πŸ“ˆ BARRA DE PROGRESSO DE LEITURA
313
+ // ═══════════════════════════════════════════════════════════
314
+ initProgressBar() {
315
+ const progressBar = document.createElement('div');
316
+ progressBar.className = 'reading-progress-bar';
317
+ document.body.appendChild(progressBar);
318
+
319
+ window.addEventListener('scroll', () => {
320
+ const windowHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
321
+ const scrolled = (window.scrollY / windowHeight) * 100;
322
+ progressBar.style.width = `${scrolled}%`;
323
+ });
324
+ }
325
+
326
+ // ═══════════════════════════════════════════════════════════
327
+ // ⬆️ BOTΓƒO VOLTAR AO TOPO
328
+ // ═══════════════════════════════════════════════════════════
329
+ initBackToTop() {
330
+ const backToTop = document.createElement('button');
331
+ backToTop.innerHTML = '<i data-lucide="arrow-up" class="w-5 h-5"></i>';
332
+ backToTop.className = 'back-to-top fixed bottom-6 left-6 z-50 w-12 h-12 bg-cyan-500 text-white rounded-full shadow-lg opacity-0 invisible transition-all duration-300 flex items-center justify-center hover:scale-110';
333
+ backToTop.setAttribute('aria-label', 'Voltar ao topo');
334
+ document.body.appendChild(backToTop);
335
+
336
+ window.addEventListener('scroll', () => {
337
+ if (window.scrollY > 500) {
338
+ backToTop.classList.remove('opacity-0', 'invisible');
339
+ } else {
340
+ backToTop.classList.add('opacity-0', 'invisible');
341
+ }
342
+ });
343
+
344
+ backToTop.addEventListener('click', () => {
345
+ window.scrollTo({
346
+ top: 0,
347
+ behavior: 'smooth'
348
+ });
349
+ });
350
+
351
+ lucide.createIcons();
352
+ }
353
+
354
+ // ═══════════════════════════════════════════════════════════
355
+ // πŸ“ MELHORIAS EM FORMULÁRIOS
356
+ // ═══════════════════════════════════════════════════════════
357
+ initFormEnhancements() {
358
+ // Auto-resize para textareas
359
+ document.querySelectorAll('textarea').forEach((textarea) => {
360
+ textarea.addEventListener('input', function() {
361
+ this.style.height = 'auto';
362
+ this.style.height = this.scrollHeight + 'px';
363
+ });
364
+ });
365
+
366
+ // ValidaΓ§Γ£o em tempo real
367
+ document.querySelectorAll('input[type="email"]').forEach((input) => {
368
+ input.addEventListener('blur', function() {
369
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
370
+ if (this.value && !emailRegex.test(this.value)) {
371
+ this.classList.add('border-red-500');
372
+ this.classList.remove('border-cyan-500');
373
+ } else if (this.value) {
374
+ this.classList.add('border-cyan-500');
375
+ this.classList.remove('border-red-500');
376
+ }
377
+ });
378
+ });
379
+
380
+ // Contador de caracteres para textareas
381
+ document.querySelectorAll('textarea[maxlength]').forEach((textarea) => {
382
+ const maxLength = textarea.getAttribute('maxlength');
383
+ const counter = document.createElement('div');
384
+ counter.className = 'text-sm text-gray-400 mt-2 text-right';
385
+ textarea.parentNode.appendChild(counter);
386
+
387
+ const updateCounter = () => {
388
+ const remaining = maxLength - textarea.value.length;
389
+ counter.textContent = `${remaining} caracteres restantes`;
390
+ if (remaining < 50) {
391
+ counter.classList.add('text-orange-400');
392
+ } else {
393
+ counter.classList.remove('text-orange-400');
394
+ }
395
+ };
396
+
397
+ textarea.addEventListener('input', updateCounter);
398
+ updateCounter();
399
+ });
400
+ }
401
+
402
+ // ═══════════════════════════════════════════════════════════
403
+ // 🧭 EFEITOS DE NAVEGAÇÃO
404
+ // ═══════════════════════════════════════════════════════════
405
+ initNavigationEffects() {
406
+ const header = document.querySelector('header');
407
+ if (!header) return;
408
+
409
+ let lastScroll = 0;
410
+
411
+ window.addEventListener('scroll', () => {
412
+ const currentScroll = window.pageYOffset;
413
+
414
+ // Adicionar sombra ao rolar
415
+ if (currentScroll > 50) {
416
+ header.classList.add('shadow-lg');
417
+ } else {
418
+ header.classList.remove('shadow-lg');
419
+ }
420
+
421
+ // Esconder header ao rolar para baixo (opcional)
422
+ // if (currentScroll > lastScroll && currentScroll > 500) {
423
+ // header.style.transform = 'translateY(-100%)';
424
+ // } else {
425
+ // header.style.transform = 'translateY(0)';
426
+ // }
427
+
428
+ lastScroll = currentScroll;
429
+ });
430
+ }
431
+
432
+ // ═══════════════════════════════════════════════════════════
433
+ // ⏳ PRELOADER
434
+ // ═══════════════════════════════════════════════════════════
435
+ initPreloader() {
436
+ const preloader = document.querySelector('.preloader');
437
+ if (!preloader) return;
438
+
439
+ window.addEventListener('load', () => {
440
+ setTimeout(() => {
441
+ preloader.classList.add('fade-out');
442
+ setTimeout(() => {
443
+ preloader.remove();
444
+ }, 500);
445
+ }, 300);
446
+ });
447
+ }
448
+
449
+ // ═══════════════════════════════════════════════════════════
450
+ // ⌨️ ATALHOS DE TECLADO
451
+ // ═══════════════════════════════════════════════════════════
452
+ initKeyboardShortcuts() {
453
+ document.addEventListener('keydown', (e) => {
454
+ // Ctrl/Cmd + K = Abrir menu de busca (se existir)
455
+ if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
456
+ e.preventDefault();
457
+ console.log('Busca ativada (Ctrl/Cmd + K)');
458
+ }
459
+
460
+ // Escape = Fechar modais
461
+ if (e.key === 'Escape') {
462
+ document.querySelectorAll('.modal.active, .dropdown.open').forEach((el) => {
463
+ el.classList.remove('active', 'open');
464
+ });
465
+ }
466
+ });
467
+ }
468
+
469
+ // ═══════════════════════════════════════════════════════════
470
+ // πŸ”” SISTEMA DE NOTIFICAÇÕES
471
+ // ═══════════════════════════════════════════════════════════
472
+ showNotification(message, type = 'info') {
473
+ const notification = document.createElement('div');
474
+ notification.className = `notification notification-${type}`;
475
+ notification.textContent = message;
476
+ document.body.appendChild(notification);
477
+
478
+ setTimeout(() => notification.classList.add('show'), 10);
479
+ setTimeout(() => {
480
+ notification.classList.remove('show');
481
+ setTimeout(() => notification.remove(), 300);
482
+ }, 3000);
483
+ }
484
+ }
485
+
486
+ // ═══════════════════════════════════════════════════════════
487
+ // πŸš€ INICIALIZAR APP
488
+ // ═══════════════════════════════════════════════════════════
489
+ const app = new SoftEdgeApp();
490
+
491
+ // ═══════════════════════════════════════════════════════════
492
+ // 🎨 EXPORTAR PARA USO GLOBAL (se necessÑrio)
493
+ // ═══════════════════════════════════════════════════════════
494
+ window.SoftEdgeApp = app;