Enhance homepage animations and fix visual glitches
Refactor CSS and JavaScript to implement advanced GSAP animations, resolve image overlay issues, and ensure sponsor visibility. Replit-Commit-Author: Agent Replit-Commit-Session-Id: f13f685e-aa76-4721-bd29-73edf2f9795d Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: c95103aa-b9ba-4a37-8cab-7bb946a4dee9 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/705de26f-a3c1-41e6-845d-88f96627134c/f13f685e-aa76-4721-bd29-73edf2f9795d/2yHlFNO Replit-Helium-Checkpoint-Created: true
This commit is contained in:
402
js/enhance.js
402
js/enhance.js
@@ -1,357 +1,81 @@
|
||||
/* =====================================================
|
||||
TSCB — Cross-page Utilities
|
||||
===================================================== */
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
/* ============================
|
||||
Scroll Progress Bar
|
||||
============================ */
|
||||
var progressBar = document.createElement('div');
|
||||
progressBar.id = 'tscb-progress';
|
||||
document.body.prepend(progressBar);
|
||||
/* ---- Scroll Progress Bar ---- */
|
||||
(function () {
|
||||
var bar = document.createElement('div');
|
||||
bar.id = 'tscb-progress';
|
||||
document.body.appendChild(bar);
|
||||
|
||||
window.addEventListener('scroll', function () {
|
||||
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
|
||||
var scrollHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
|
||||
var progress = (scrollTop / scrollHeight) * 100;
|
||||
progressBar.style.width = progress + '%';
|
||||
}, { passive: true });
|
||||
function onScroll() {
|
||||
var scrollTop = window.scrollY || document.documentElement.scrollTop;
|
||||
var docHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
|
||||
var pct = docHeight > 0 ? (scrollTop / docHeight) * 100 : 0;
|
||||
bar.style.width = pct + '%';
|
||||
}
|
||||
|
||||
/* ============================
|
||||
Hero Decorative Orbs
|
||||
============================ */
|
||||
var hero = document.querySelector('.hero');
|
||||
if (hero) {
|
||||
['hero-orb-1', 'hero-orb-2', 'hero-orb-3'].forEach(function (cls) {
|
||||
window.addEventListener('scroll', onScroll, { passive: true });
|
||||
})();
|
||||
|
||||
/* ---- Hero Decorative Orbs ---- */
|
||||
(function () {
|
||||
var hero = document.querySelector('.hero');
|
||||
if (!hero) return;
|
||||
[1, 2, 3].forEach(function (n) {
|
||||
var orb = document.createElement('div');
|
||||
orb.className = cls;
|
||||
hero.prepend(orb);
|
||||
orb.className = 'hero-orb-' + n;
|
||||
hero.appendChild(orb);
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
/* ============================
|
||||
Intersection Observer: Stagger counters
|
||||
============================ */
|
||||
function observeElements(selector, className, threshold) {
|
||||
var elements = document.querySelectorAll(selector);
|
||||
if (!elements.length) return;
|
||||
|
||||
var observer = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add(className);
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
/* ---- Magnetic Buttons ---- */
|
||||
(function () {
|
||||
var btns = document.querySelectorAll('.btn-default, .readmore-btn, .circular-arrow');
|
||||
btns.forEach(function (btn) {
|
||||
btn.addEventListener('mousemove', function (e) {
|
||||
var rect = btn.getBoundingClientRect();
|
||||
var cx = rect.left + rect.width / 2;
|
||||
var cy = rect.top + rect.height / 2;
|
||||
var dx = (e.clientX - cx) * 0.25;
|
||||
var dy = (e.clientY - cy) * 0.25;
|
||||
btn.style.transform = 'translate(' + dx + 'px, ' + dy + 'px)';
|
||||
});
|
||||
}, { threshold: threshold || 0.2, rootMargin: '0px 0px -60px 0px' });
|
||||
|
||||
elements.forEach(function (el) { observer.observe(el); });
|
||||
}
|
||||
|
||||
observeElements('.counter-item', 'is-visible', 0.2);
|
||||
observeElements('.sponsor-logo-item', 'is-visible', 0.15);
|
||||
observeElements('.about-list-item', 'is-visible', 0.15);
|
||||
|
||||
/* ============================
|
||||
GSAP Enhanced Animations (if GSAP available)
|
||||
============================ */
|
||||
window.addEventListener('load', function () {
|
||||
if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') return;
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
/* --- Hero content stagger --- */
|
||||
var heroContent = document.querySelector('.hero-content .section-title');
|
||||
if (heroContent) {
|
||||
var heroKids = heroContent.querySelectorAll('h3, p');
|
||||
gsap.from(heroKids, {
|
||||
y: 40,
|
||||
opacity: 0,
|
||||
duration: 1,
|
||||
stagger: 0.2,
|
||||
ease: 'power3.out',
|
||||
delay: 0.3
|
||||
});
|
||||
}
|
||||
|
||||
/* --- Counter numbers enhanced count-up feel --- */
|
||||
var counterBox = document.querySelector('.counter-box');
|
||||
if (counterBox) {
|
||||
gsap.from(counterBox, {
|
||||
scrollTrigger: {
|
||||
trigger: counterBox,
|
||||
start: 'top 80%',
|
||||
},
|
||||
scaleX: 0.92,
|
||||
scaleY: 0.95,
|
||||
opacity: 0,
|
||||
duration: 0.9,
|
||||
ease: 'power3.out'
|
||||
});
|
||||
}
|
||||
|
||||
/* --- Service items stagger entrance --- */
|
||||
var serviceItems = document.querySelectorAll('.service-item');
|
||||
if (serviceItems.length) {
|
||||
gsap.from(serviceItems, {
|
||||
scrollTrigger: {
|
||||
trigger: serviceItems[0].closest('.our-services') || serviceItems[0],
|
||||
start: 'top 75%',
|
||||
},
|
||||
y: 60,
|
||||
opacity: 0,
|
||||
duration: 0.8,
|
||||
stagger: 0.15,
|
||||
ease: 'back.out(1.4)'
|
||||
});
|
||||
}
|
||||
|
||||
/* --- About list items stagger --- */
|
||||
var aboutItems = document.querySelectorAll('.about-list-item');
|
||||
if (aboutItems.length) {
|
||||
gsap.from(aboutItems, {
|
||||
scrollTrigger: {
|
||||
trigger: aboutItems[0].parentElement,
|
||||
start: 'top 80%',
|
||||
},
|
||||
x: -30,
|
||||
opacity: 0,
|
||||
duration: 0.7,
|
||||
stagger: 0.12,
|
||||
ease: 'power2.out'
|
||||
});
|
||||
}
|
||||
|
||||
/* --- Sponsor logos stagger entrance --- */
|
||||
var sponsorItems = document.querySelectorAll('.sponsor-logo-item');
|
||||
if (sponsorItems.length) {
|
||||
gsap.from(sponsorItems, {
|
||||
scrollTrigger: {
|
||||
trigger: document.querySelector('.sponsors-logo-grid'),
|
||||
start: 'top 80%',
|
||||
},
|
||||
y: 40,
|
||||
scale: 0.88,
|
||||
opacity: 0,
|
||||
duration: 0.7,
|
||||
stagger: 0.15,
|
||||
ease: 'back.out(1.7)'
|
||||
});
|
||||
}
|
||||
|
||||
/* --- CTA section reveal --- */
|
||||
var ctaBox = document.querySelector('.cta-box');
|
||||
if (ctaBox) {
|
||||
gsap.from(ctaBox, {
|
||||
scrollTrigger: {
|
||||
trigger: ctaBox,
|
||||
start: 'top 85%',
|
||||
},
|
||||
y: 30,
|
||||
opacity: 0,
|
||||
duration: 0.9,
|
||||
ease: 'power2.out'
|
||||
});
|
||||
}
|
||||
|
||||
/* --- Section titles enhanced parallax --- */
|
||||
var sectionTitles = document.querySelectorAll('.section-row .section-title');
|
||||
sectionTitles.forEach(function (title) {
|
||||
gsap.from(title, {
|
||||
scrollTrigger: {
|
||||
trigger: title,
|
||||
start: 'top 85%',
|
||||
},
|
||||
y: 25,
|
||||
opacity: 0,
|
||||
duration: 0.8,
|
||||
ease: 'power2.out'
|
||||
btn.addEventListener('mouseleave', function () {
|
||||
btn.style.transform = '';
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
/* --- Team member items stagger --- */
|
||||
var teamItems = document.querySelectorAll('.team-member-item');
|
||||
if (teamItems.length) {
|
||||
gsap.from(teamItems, {
|
||||
scrollTrigger: {
|
||||
trigger: teamItems[0].closest('.our-teams') || teamItems[0],
|
||||
start: 'top 75%',
|
||||
},
|
||||
y: 50,
|
||||
opacity: 0,
|
||||
duration: 0.8,
|
||||
stagger: 0.14,
|
||||
ease: 'back.out(1.4)'
|
||||
});
|
||||
}
|
||||
|
||||
/* --- Mission image floating --- */
|
||||
var missionImg = document.querySelector('.mission-img figure');
|
||||
if (missionImg) {
|
||||
gsap.to(missionImg, {
|
||||
y: -18,
|
||||
duration: 3.5,
|
||||
ease: 'sine.inOut',
|
||||
yoyo: true,
|
||||
repeat: -1
|
||||
});
|
||||
}
|
||||
|
||||
/* --- Mission life circle pulse --- */
|
||||
var lifeCircle = document.querySelector('.mission-life-circle img');
|
||||
if (lifeCircle) {
|
||||
gsap.to(lifeCircle, {
|
||||
boxShadow: '0 0 0 12px rgba(217, 40, 0, 0.12)',
|
||||
duration: 2,
|
||||
ease: 'sine.inOut',
|
||||
yoyo: true,
|
||||
repeat: -1
|
||||
});
|
||||
}
|
||||
|
||||
/* --- Orb parallax on scroll --- */
|
||||
var orbs = document.querySelectorAll('.hero-orb-1, .hero-orb-2, .hero-orb-3');
|
||||
if (orbs.length && document.querySelector('.hero')) {
|
||||
orbs.forEach(function (orb, i) {
|
||||
var speed = (i + 1) * 0.08;
|
||||
gsap.to(orb, {
|
||||
scrollTrigger: {
|
||||
trigger: '.hero',
|
||||
start: 'top top',
|
||||
end: 'bottom top',
|
||||
scrub: 1
|
||||
},
|
||||
y: (i % 2 === 0 ? -1 : 1) * 120 * speed * 10,
|
||||
x: (i % 2 === 0 ? 1 : -1) * 40 * speed * 5,
|
||||
ease: 'none'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* --- Counter section scale in --- */
|
||||
var counterItems = document.querySelectorAll('.counter-item');
|
||||
if (counterItems.length) {
|
||||
counterItems.forEach(function (item, i) {
|
||||
gsap.from(item, {
|
||||
scrollTrigger: {
|
||||
trigger: item,
|
||||
start: 'top 85%',
|
||||
},
|
||||
y: 35,
|
||||
scale: 0.92,
|
||||
opacity: 0,
|
||||
duration: 0.75,
|
||||
delay: i * 0.1,
|
||||
ease: 'back.out(1.5)'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* --- Sponsors footer line --- */
|
||||
var sponsorFooter = document.querySelector('.sponsors-footer');
|
||||
if (sponsorFooter) {
|
||||
gsap.from(sponsorFooter, {
|
||||
scrollTrigger: {
|
||||
trigger: sponsorFooter,
|
||||
start: 'top 90%'
|
||||
},
|
||||
y: 20,
|
||||
opacity: 0,
|
||||
duration: 0.7,
|
||||
ease: 'power2.out'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* ============================
|
||||
Magnetic Button Effect (subtle)
|
||||
============================ */
|
||||
var magnetBtns = document.querySelectorAll('.btn-default, .readmore-btn');
|
||||
magnetBtns.forEach(function (btn) {
|
||||
btn.addEventListener('mousemove', function (e) {
|
||||
var rect = btn.getBoundingClientRect();
|
||||
var cx = rect.left + rect.width / 2;
|
||||
var cy = rect.top + rect.height / 2;
|
||||
var dx = (e.clientX - cx) / (rect.width / 2);
|
||||
var dy = (e.clientY - cy) / (rect.height / 2);
|
||||
var strength = btn.classList.contains('readmore-btn') ? 6 : 8;
|
||||
btn.style.transform = 'translate(' + (dx * strength) + 'px, ' + (dy * strength) + 'px)';
|
||||
});
|
||||
|
||||
btn.addEventListener('mouseleave', function () {
|
||||
btn.style.transform = '';
|
||||
btn.style.transition = 'transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)';
|
||||
});
|
||||
|
||||
btn.addEventListener('mouseenter', function () {
|
||||
btn.style.transition = 'transform 0.1s ease';
|
||||
});
|
||||
});
|
||||
|
||||
/* ============================
|
||||
Active nav link highlight
|
||||
============================ */
|
||||
var currentPath = window.location.pathname.split('/').pop() || 'index.html';
|
||||
var navLinks = document.querySelectorAll('.main-menu .nav-item a');
|
||||
navLinks.forEach(function (link) {
|
||||
var href = link.getAttribute('href') || '';
|
||||
var linkFile = href.split('/').pop();
|
||||
if (linkFile === currentPath || (currentPath === '' && linkFile === 'index.html')) {
|
||||
link.closest('.nav-item').classList.add('active');
|
||||
}
|
||||
});
|
||||
|
||||
/* ============================
|
||||
Smooth Ticker Speed Boost on hover
|
||||
============================ */
|
||||
var tickerBoxes = document.querySelectorAll('.scrolling-ticker-box');
|
||||
tickerBoxes.forEach(function (box) {
|
||||
box.addEventListener('mouseenter', function () {
|
||||
box.style.animationPlayState = 'paused';
|
||||
var content = box.querySelectorAll('.scrolling-content');
|
||||
content.forEach(function (c) { c.style.animationPlayState = 'paused'; });
|
||||
});
|
||||
box.addEventListener('mouseleave', function () {
|
||||
box.style.animationPlayState = '';
|
||||
var content = box.querySelectorAll('.scrolling-content');
|
||||
content.forEach(function (c) { c.style.animationPlayState = ''; });
|
||||
});
|
||||
});
|
||||
|
||||
/* ============================
|
||||
Image tilt effect on about images
|
||||
============================ */
|
||||
var tiltImages = document.querySelectorAll('.about-img-1, .about-img-2, .mission-img .image-anime');
|
||||
tiltImages.forEach(function (el) {
|
||||
el.addEventListener('mousemove', function (e) {
|
||||
var rect = el.getBoundingClientRect();
|
||||
var cx = rect.left + rect.width / 2;
|
||||
var cy = rect.top + rect.height / 2;
|
||||
var dx = (e.clientX - cx) / (rect.width / 2);
|
||||
var dy = (e.clientY - cy) / (rect.height / 2);
|
||||
var img = el.querySelector('img, figure');
|
||||
if (img) {
|
||||
img.style.transform = 'perspective(600px) rotateY(' + (dx * 4) + 'deg) rotateX(' + (-dy * 4) + 'deg) scale(1.03)';
|
||||
img.style.transition = 'transform 0.1s ease';
|
||||
/* ---- Active nav link ---- */
|
||||
(function () {
|
||||
var currentFile = window.location.pathname.split('/').pop() || 'index.html';
|
||||
var links = document.querySelectorAll('.navbar-nav .nav-link');
|
||||
links.forEach(function (link) {
|
||||
var href = link.getAttribute('href') || '';
|
||||
var file = href.split('/').pop();
|
||||
if (file === currentFile || (currentFile === '' && file === 'index.html')) {
|
||||
var item = link.closest('.nav-item');
|
||||
if (item) item.classList.add('active');
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
el.addEventListener('mouseleave', function () {
|
||||
var img = el.querySelector('img, figure');
|
||||
if (img) {
|
||||
img.style.transform = '';
|
||||
img.style.transition = 'transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)';
|
||||
}
|
||||
/* ---- Ticker pause on hover ---- */
|
||||
(function () {
|
||||
var ticker = document.querySelector('.ticker-wrap');
|
||||
if (!ticker) return;
|
||||
var inner = ticker.querySelector('.ticker');
|
||||
if (!inner) return;
|
||||
ticker.addEventListener('mouseenter', function () {
|
||||
inner.style.animationPlayState = 'paused';
|
||||
});
|
||||
});
|
||||
|
||||
/* ============================
|
||||
Counter box animated number suffix
|
||||
============================ */
|
||||
// Ensure counter values have proper styling after WOW init
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var counterTitles = document.querySelectorAll('.counter-title h2');
|
||||
counterTitles.forEach(function (h2) {
|
||||
h2.style.letterSpacing = '-0.02em';
|
||||
ticker.addEventListener('mouseleave', function () {
|
||||
inner.style.animationPlayState = '';
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
})();
|
||||
|
||||
315
js/home-animations.js
Normal file
315
js/home-animations.js
Normal file
@@ -0,0 +1,315 @@
|
||||
/* =====================================================
|
||||
TSCB — Homepage GSAP Masterpiece
|
||||
Linked ONLY in index.html | v1.0
|
||||
===================================================== */
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* Guard: only run when there is a hero section */
|
||||
if (!document.querySelector('.hero')) return;
|
||||
|
||||
/* Register GSAP plugins (safe to call multiple times) */
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
/* ====================================================
|
||||
1. HERO — ORBS ENTRANCE + MOUSE PARALLAX
|
||||
==================================================== */
|
||||
|
||||
/* Orbs drift in after preloader */
|
||||
gsap.from('.hero-orb-1', {
|
||||
scale: 0.2, opacity: 0, duration: 2.8,
|
||||
ease: 'power3.out', delay: 0.85
|
||||
});
|
||||
gsap.from('.hero-orb-2', {
|
||||
scale: 0.2, opacity: 0, duration: 3.2,
|
||||
ease: 'power3.out', delay: 1.0
|
||||
});
|
||||
gsap.from('.hero-orb-3', {
|
||||
scale: 0.2, opacity: 0, duration: 2.2,
|
||||
ease: 'power3.out', delay: 1.3
|
||||
});
|
||||
|
||||
/* Orb glow pulse after preloader (subtle depth feel) */
|
||||
gsap.to('.hero-orb-1', {
|
||||
opacity: 0.85, scale: 1.08,
|
||||
duration: 3, ease: 'sine.inOut',
|
||||
yoyo: true, repeat: -1, delay: 1.8
|
||||
});
|
||||
|
||||
/* Orb scroll parallax */
|
||||
ScrollTrigger.create({
|
||||
trigger: '.hero',
|
||||
start: 'top top',
|
||||
end: 'bottom top',
|
||||
scrub: 1.8,
|
||||
onUpdate: function (self) {
|
||||
var p = self.progress;
|
||||
gsap.set('.hero-orb-1', { y: p * -130, x: p * 45 });
|
||||
gsap.set('.hero-orb-2', { y: p * -90, x: p * -35 });
|
||||
gsap.set('.hero-orb-3', { y: p * -60, rotate: p * 50 });
|
||||
}
|
||||
});
|
||||
|
||||
/* Hero mouse parallax — orbs follow the cursor */
|
||||
var heroEl = document.querySelector('.hero');
|
||||
heroEl.addEventListener('mousemove', function (e) {
|
||||
var rect = heroEl.getBoundingClientRect();
|
||||
var xR = (e.clientX - rect.left) / rect.width - 0.5;
|
||||
var yR = (e.clientY - rect.top) / rect.height - 0.5;
|
||||
gsap.to('.hero-orb-1', { x: xR * 55, y: yR * 35, duration: 1.8, ease: 'power2.out', overwrite: 'auto' });
|
||||
gsap.to('.hero-orb-2', { x: xR * -40, y: yR * -25, duration: 2.0, ease: 'power2.out', overwrite: 'auto' });
|
||||
gsap.to('.hero-orb-3', { x: xR * 25, y: yR * 18, duration: 1.4, ease: 'power2.out', overwrite: 'auto' });
|
||||
});
|
||||
|
||||
/* ====================================================
|
||||
2. COUNTER SECTION — 3-STAGE DRAMATIC ENTRANCE
|
||||
(counter-item has no .wow class — safe to animate)
|
||||
==================================================== */
|
||||
|
||||
var counterSection = document.querySelector('.our-counter');
|
||||
if (counterSection) {
|
||||
|
||||
/* Stage 1 — the whole box scales in */
|
||||
gsap.from('.counter-box', {
|
||||
scrollTrigger: { trigger: '.our-counter', start: 'top 78%', once: true },
|
||||
scale: 0.88, opacity: 0, borderRadius: '40px',
|
||||
duration: 1.2, ease: 'power4.out',
|
||||
immediateRender: false
|
||||
});
|
||||
|
||||
/* Stage 2 — individual items slide up with stagger */
|
||||
gsap.from('.counter-item', {
|
||||
scrollTrigger: { trigger: '.our-counter', start: 'top 78%', once: true },
|
||||
y: 60, opacity: 0,
|
||||
duration: 0.85, stagger: 0.18, delay: 0.25,
|
||||
ease: 'back.out(1.6)',
|
||||
immediateRender: false
|
||||
});
|
||||
|
||||
/* Stage 3 — counter h2 numbers pop with elastic */
|
||||
gsap.from('.counter-title h2', {
|
||||
scrollTrigger: { trigger: '.our-counter', start: 'top 78%', once: true },
|
||||
scale: 0.4, opacity: 0,
|
||||
duration: 1.1, stagger: 0.2, delay: 0.55,
|
||||
ease: 'elastic.out(1.1, 0.52)',
|
||||
immediateRender: false
|
||||
});
|
||||
|
||||
/* Counter hover — h2 magnify */
|
||||
document.querySelectorAll('.counter-item').forEach(function (item) {
|
||||
item.addEventListener('mouseenter', function () {
|
||||
gsap.to(item.querySelector('.counter-title h2'), {
|
||||
scale: 1.12, color: '#d92800',
|
||||
duration: 0.35, ease: 'back.out(2)'
|
||||
});
|
||||
});
|
||||
item.addEventListener('mouseleave', function () {
|
||||
gsap.to(item.querySelector('.counter-title h2'), {
|
||||
scale: 1, color: '',
|
||||
duration: 0.5, ease: 'power2.out'
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* ====================================================
|
||||
3. MISSION IMAGE — SCROLL PARALLAX
|
||||
(the reveal animation in function.js still runs)
|
||||
==================================================== */
|
||||
|
||||
gsap.to('.mission-img', {
|
||||
scrollTrigger: {
|
||||
trigger: '.our-mission',
|
||||
start: 'top bottom',
|
||||
end: 'bottom top',
|
||||
scrub: 1.8
|
||||
},
|
||||
y: -60, ease: 'none'
|
||||
});
|
||||
|
||||
gsap.to('.mission-life-circle', {
|
||||
scrollTrigger: {
|
||||
trigger: '.our-mission',
|
||||
start: 'top bottom',
|
||||
end: 'bottom top',
|
||||
scrub: 2.5
|
||||
},
|
||||
y: -95, rotate: 35, ease: 'none'
|
||||
});
|
||||
|
||||
/* ====================================================
|
||||
4. SERVICE TICKER — FADE IN + SUBTLE SCALE
|
||||
==================================================== */
|
||||
|
||||
gsap.from('.service-ticker', {
|
||||
scrollTrigger: { trigger: '.service-ticker', start: 'top 92%', once: true },
|
||||
opacity: 0, scaleY: 0.6,
|
||||
transformOrigin: 'center center',
|
||||
duration: 0.7, ease: 'back.out(2)',
|
||||
immediateRender: false
|
||||
});
|
||||
|
||||
/* ====================================================
|
||||
5. SERVICE CARDS — 3D TILT ON HOVER
|
||||
(entrance handled by WOW.js — we layer on tilt only)
|
||||
==================================================== */
|
||||
|
||||
document.querySelectorAll('.service-item').forEach(function (card) {
|
||||
card.style.transformStyle = 'preserve-3d';
|
||||
card.addEventListener('mousemove', function (e) {
|
||||
var rect = card.getBoundingClientRect();
|
||||
var rotX = ((e.clientY - rect.top - rect.height / 2) / (rect.height / 2)) * -10;
|
||||
var rotY = ((e.clientX - rect.left - rect.width / 2) / (rect.width / 2)) * 10;
|
||||
gsap.to(card, {
|
||||
rotationX: rotX, rotationY: rotY,
|
||||
transformPerspective: 700,
|
||||
duration: 0.35, ease: 'power2.out', overwrite: 'auto'
|
||||
});
|
||||
});
|
||||
card.addEventListener('mouseleave', function () {
|
||||
gsap.to(card, {
|
||||
rotationX: 0, rotationY: 0,
|
||||
duration: 0.75, ease: 'elastic.out(1, 0.45)', overwrite: 'auto'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/* ====================================================
|
||||
6. SPONSORS — GUARANTEED VISIBILITY
|
||||
Two-trigger system: ScrollTrigger + load-time check
|
||||
gsap.set() owns initial state (no CSS opacity:0 ever)
|
||||
==================================================== */
|
||||
|
||||
var sponsorItems = document.querySelectorAll('.sponsor-logo-item');
|
||||
if (sponsorItems.length) {
|
||||
|
||||
/* Immediately set invisible via GSAP — no CSS conflict */
|
||||
gsap.set(sponsorItems, { opacity: 0, y: 60, scale: 0.78, rotationY: 15 });
|
||||
|
||||
var sponsorsPlayed = false;
|
||||
function playSponsors() {
|
||||
if (sponsorsPlayed) return;
|
||||
sponsorsPlayed = true;
|
||||
gsap.to(sponsorItems, {
|
||||
opacity: 1, y: 0, scale: 1, rotationY: 0,
|
||||
duration: 0.9, stagger: 0.22,
|
||||
ease: 'back.out(1.8)',
|
||||
clearProps: 'rotationY'
|
||||
});
|
||||
}
|
||||
|
||||
/* Trigger 1: normal scroll */
|
||||
ScrollTrigger.create({
|
||||
trigger: '.our-sponsors-section',
|
||||
start: 'top 88%',
|
||||
once: true,
|
||||
onEnter: playSponsors
|
||||
});
|
||||
|
||||
/* Trigger 2: already visible when page finishes loading */
|
||||
window.addEventListener('load', function () {
|
||||
setTimeout(function () {
|
||||
var sec = document.querySelector('.our-sponsors-section');
|
||||
if (!sec) return;
|
||||
var rect = sec.getBoundingClientRect();
|
||||
if (rect.top < window.innerHeight * 0.92) {
|
||||
playSponsors();
|
||||
}
|
||||
}, 980);
|
||||
});
|
||||
|
||||
/* 3D tilt on hover — works after animation completes */
|
||||
sponsorItems.forEach(function (card) {
|
||||
card.style.transformStyle = 'preserve-3d';
|
||||
card.addEventListener('mousemove', function (e) {
|
||||
var rect = card.getBoundingClientRect();
|
||||
var rotX = ((e.clientY - rect.top - rect.height / 2) / (rect.height / 2)) * -8;
|
||||
var rotY = ((e.clientX - rect.left - rect.width / 2) / (rect.width / 2)) * 8;
|
||||
gsap.to(card, {
|
||||
rotationX: rotX, rotationY: rotY,
|
||||
transformPerspective: 500,
|
||||
duration: 0.3, ease: 'power2.out', overwrite: 'auto'
|
||||
});
|
||||
});
|
||||
card.addEventListener('mouseleave', function () {
|
||||
gsap.to(card, {
|
||||
rotationX: 0, rotationY: 0,
|
||||
duration: 0.65, ease: 'elastic.out(1, 0.45)', overwrite: 'auto'
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* ====================================================
|
||||
7. CTA BOX — SPLIT ENTRANCE
|
||||
(cta-box-btn has .wow — we animate cta-box-content only)
|
||||
==================================================== */
|
||||
|
||||
gsap.from('.cta-box-content', {
|
||||
scrollTrigger: { trigger: '.cta-box', start: 'top 82%', once: true },
|
||||
x: -70, opacity: 0,
|
||||
duration: 1.1, ease: 'power4.out',
|
||||
immediateRender: false
|
||||
});
|
||||
|
||||
/* ====================================================
|
||||
8. GLOBAL SCROLL PROGRESS + SECTION FLASH
|
||||
Brief red flash on the progress bar when entering a section
|
||||
==================================================== */
|
||||
|
||||
var sectionFlashSections = document.querySelectorAll('.our-counter, .our-services, .our-sponsors-section, .cta-box');
|
||||
sectionFlashSections.forEach(function (sec) {
|
||||
ScrollTrigger.create({
|
||||
trigger: sec,
|
||||
start: 'top 60%',
|
||||
once: true,
|
||||
onEnter: function () {
|
||||
var bar = document.getElementById('tscb-progress');
|
||||
if (!bar) return;
|
||||
gsap.fromTo(bar, { boxShadow: '0 0 0 0 rgba(217,40,0,0)' },
|
||||
{ boxShadow: '0 0 18px 4px rgba(217,40,0,0.7)', duration: 0.3, yoyo: true, repeat: 1, ease: 'power2.inOut' });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* ====================================================
|
||||
9. ABOUT-LIST ICON BOXES — STAGGER SPIN-IN
|
||||
(icons inside .about-list-item — no .wow class on the icon)
|
||||
==================================================== */
|
||||
|
||||
gsap.from('#home-about .about-list-item .icon-box', {
|
||||
scrollTrigger: { trigger: '#home-about .about-content-body', start: 'top 82%', once: true },
|
||||
scale: 0, rotate: -180, opacity: 0,
|
||||
duration: 0.75, stagger: 0.2, delay: 0.15,
|
||||
ease: 'back.out(2)',
|
||||
immediateRender: false
|
||||
});
|
||||
|
||||
/* ====================================================
|
||||
10. SCROLL-TRIGGERED BACKGROUND GRADIENT SHIFT
|
||||
on the hero as user scrolls away
|
||||
==================================================== */
|
||||
|
||||
ScrollTrigger.create({
|
||||
trigger: '.hero',
|
||||
start: 'top top',
|
||||
end: 'bottom top',
|
||||
scrub: 1,
|
||||
onUpdate: function (self) {
|
||||
heroEl.style.filter = 'brightness(' + (1 - self.progress * 0.18) + ')';
|
||||
}
|
||||
});
|
||||
|
||||
/* ====================================================
|
||||
FINALIZE: refresh ScrollTrigger after full load
|
||||
(handles lazy images and font layout shifts)
|
||||
==================================================== */
|
||||
|
||||
window.addEventListener('load', function () {
|
||||
setTimeout(function () {
|
||||
ScrollTrigger.refresh();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user