JSbanner-volt.jsโ GSAP animation timeline
(() => {
const root = document.getElementById('volt');
if (!root) return;
// -------------------------------------------------------------------
// SLIDES
// -------------------------------------------------------------------
const slides = [
{
line1: 'DIGITAL DREAMS',
line2: 'REAL RESULTS',
desc: 'Stay Informed, Stay Ahead: Unveiling the Future of Technology, Gadgets, and Innovation. Your Gateway to the Digital Universe where Innovation Meets Insight.',
cta: 'Get Started',
image: 'https://images.unsplash.com/photo-1558494949-ef010cbdcc31?auto=format&fit=crop&w=1600&q=80',
},
{
line1: 'CREATIVE VISION',
line2: 'BOLD EXECUTION',
desc: 'From first sketch to final shipping brand: end-to-end identity, product, and campaign design that sparks conversation and converts intent into loyalty.',
cta: 'Explore Studio',
image: 'https://images.unsplash.com/photo-1517077304055-6e89abbf09b0?auto=format&fit=crop&w=1600&q=80',
},
{
line1: 'SMART STRATEGY',
line2: 'SCALABLE GROWTH',
desc: 'Data-driven playbooks, weekly experiments, and market-fit insights that compound into month-over-month gains for hungry startups and mature teams alike.',
cta: 'See Case Studies',
image: 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?auto=format&fit=crop&w=1600&q=80',
},
{
line1: 'BUILT FOR TEAMS',
line2: 'READY FOR MORE',
desc: 'Collaboration tools, workflow automation, and shared dashboards designed so every function โ design, dev, ops, marketing โ moves in the same direction.',
cta: 'Talk to Us',
image: 'https://images.unsplash.com/photo-1522071820081-009f0129c71c?auto=format&fit=crop&w=1600&q=80',
},
];
let currentSlide = 0;
// -------------------------------------------------------------------
// BUILD LOADER ELECTRIC ARCS
// -------------------------------------------------------------------
const arcsHost = document.getElementById('loader-arcs');
const ARC_COUNT = 8;
for (let i = 0; i < ARC_COUNT; i++) {
const arc = document.createElement('span');
const angle = (i / ARC_COUNT) * 360;
arc.style.left = '50%';
arc.style.top = '50%';
arc.style.width = (30 + Math.random() * 40) + 'px';
arc.style.transform = `rotate(${angle}deg) translate(60px, 0)`;
arcsHost.appendChild(arc);
}
const arcEls = arcsHost.querySelectorAll('span');
// -------------------------------------------------------------------
// REFERENCES
// -------------------------------------------------------------------
const loader = document.getElementById('loader');
const loaderCounter = document.getElementById('loader-counter');
const loaderLabel = document.getElementById('loader-label');
const boltOutline = document.getElementById('bolt-outline');
const boltFill = document.getElementById('bolt-fill');
const loaderCircle = document.getElementById('loader-circle');
const magnetics = root.querySelectorAll('[data-magnetic]');
const descEl = document.getElementById('desc');
const ctaPill = document.getElementById('cta-pill');
const ctaLabel = document.getElementById('cta-label');
const form = root.querySelector('[data-form]');
const titleLine1 = document.getElementById('title-line-1');
const titleLine2 = document.getElementById('title-line-2');
const line2Text = document.getElementById('line2-text');
const titleStar = document.getElementById('title-star');
const photoImg = document.getElementById('photo-img');
const imageWrap = document.getElementById('image-wrap');
const photo = document.getElementById('photo');
const arrowBtn = root.querySelector('[data-arrow]');
const dots = root.querySelectorAll('.slide-dots span');
const navLinks = root.querySelectorAll('[data-slide]');
const logoMark = document.getElementById('logo-mark');
// -------------------------------------------------------------------
// HELPERS
// -------------------------------------------------------------------
function makeChars(el, text) {
el.innerHTML = '';
[...text].forEach((ch) => {
const span = document.createElement('span');
span.className = 'hero__char';
if (ch === ' ') { span.classList.add('is-space'); span.innerHTML = ' '; }
else span.textContent = ch;
el.appendChild(span);
});
return el.querySelectorAll('.hero__char');
}
// -------------------------------------------------------------------
// INITIAL STATES
// -------------------------------------------------------------------
gsap.set(magnetics, { y: -15, opacity: 0 });
gsap.set(descEl, { y: 20, opacity: 0 });
gsap.set(ctaPill, { scale: 0.85, opacity: 0 });
gsap.set(form, { y: 20, opacity: 0 });
gsap.set(photo, { scale: 1.15, opacity: 0 });
gsap.set(arrowBtn, { scale: 0, opacity: 0, rotation: -45 });
gsap.set(titleStar, { scale: 0, rotation: -90 });
gsap.set(document.querySelector('[data-dots]'), { y: 10, opacity: 0 });
// -------------------------------------------------------------------
// LOADER TIMELINE
// -------------------------------------------------------------------
const loaderTl = gsap.timeline({ onComplete: playScene });
// Bolt draws
loaderTl.to(boltOutline, {
strokeDashoffset: 0,
duration: 1.2,
ease: 'power2.inOut',
}, 0);
// Bolt fills
loaderTl.to(boltFill, {
opacity: 1,
duration: 0.4,
ease: 'power2.out',
}, 1);
// Progress circle fills
loaderTl.to(loaderCircle, {
strokeDashoffset: 0,
duration: 2,
ease: 'power1.inOut',
}, 0.2);
// Arcs crackle
arcEls.forEach((arc, i) => {
gsap.to(arc, {
scaleX: () => Math.random() * 0.8 + 0.2,
opacity: () => Math.random() * 0.8 + 0.2,
duration: 0.1,
repeat: -1,
ease: 'none',
delay: i * 0.05,
});
});
// Counter
const p = { v: 0 };
loaderTl.to(p, {
v: 100,
duration: 2.2,
ease: 'power1.inOut',
onUpdate: () => {
loaderCounter.textContent = Math.floor(p.v) + '%';
if (p.v > 30 && loaderLabel.textContent === 'CHARGING') loaderLabel.textContent = 'POWERING UP';
if (p.v > 70 && loaderLabel.textContent === 'POWERING UP') loaderLabel.textContent = 'FULL VOLTAGE';
if (p.v > 96 && loaderLabel.textContent === 'FULL VOLTAGE') loaderLabel.textContent = 'READY';
},
}, 0);
// Flash on completion
loaderTl.to(boltFill, {
opacity: 0.3,
duration: 0.1,
yoyo: true,
repeat: 3,
ease: 'sine.inOut',
}, '+=0.2');
// Exit
loaderTl.to([loaderCounter, loaderLabel], {
y: -10, opacity: 0, duration: 0.3, stagger: 0.05,
}, '+=0.2');
loaderTl.to('.loader__stage', {
scale: 2, opacity: 0, duration: 0.6, ease: 'power3.in',
}, '-=0.2');
loaderTl.to(loader, {
opacity: 0, duration: 0.4, ease: 'power2.inOut',
}, '-=0.3');
loaderTl.set(loader, { display: 'none' });
// -------------------------------------------------------------------
// RENDER SLIDE
// -------------------------------------------------------------------
let line1Chars = [], line2Chars = [];
function renderSlide(idx, direction = 1) {
const slide = slides[idx];
const outTl = gsap.timeline();
outTl.to([...line1Chars, ...line2Chars, titleStar], {
yPercent: -110,
opacity: 0,
duration: 0.35,
stagger: 0.015,
ease: 'power2.in',
}, 0);
outTl.to(descEl, { y: -15, opacity: 0, duration: 0.3, ease: 'power2.in' }, 0);
outTl.to(ctaLabel, { y: -15, opacity: 0, duration: 0.25, ease: 'power2.in' }, 0);
outTl.to(photo, {
scale: 1.08, opacity: 0.3, duration: 0.4, ease: 'power2.in',
}, 0);
outTl.call(() => {
// Rebuild title
line1Chars = makeChars(titleLine1, slide.line1);
line2Text.textContent = '';
const line2CharsList = [...slide.line2].map((ch) => {
const span = document.createElement('span');
span.className = 'hero__char';
if (ch === ' ') { span.classList.add('is-space'); span.innerHTML = ' '; }
else span.textContent = ch;
line2Text.appendChild(span);
return span;
});
line2Chars = line2CharsList;
descEl.textContent = slide.desc;
ctaLabel.textContent = slide.cta;
photoImg.src = slide.image;
// Update dots
dots.forEach((d, i) => d.classList.toggle('is-active', i === idx));
// Update nav active
navLinks.forEach((l, i) => l.classList.toggle('is-active', i === idx));
// Animate in
gsap.set([...line1Chars, ...line2Chars], { yPercent: 110, opacity: 0 });
gsap.set(titleStar, { scale: 0, rotation: -90 });
gsap.set(descEl, { y: 15, opacity: 0 });
gsap.set(ctaLabel, { y: 15, opacity: 0 });
gsap.to(line1Chars, {
yPercent: 0, opacity: 1,
duration: 0.7,
stagger: 0.018,
ease: 'power3.out',
});
gsap.to(line2Chars, {
yPercent: 0, opacity: 1,
duration: 0.7,
stagger: 0.018,
delay: 0.15,
ease: 'power3.out',
});
gsap.to(titleStar, {
scale: 1, rotation: 0,
duration: 0.6,
delay: 0.3,
ease: 'back.out(2)',
});
gsap.to(descEl, {
y: 0, opacity: 1,
duration: 0.6,
delay: 0.25,
ease: 'power3.out',
});
gsap.to(ctaLabel, {
y: 0, opacity: 1,
duration: 0.5,
delay: 0.35,
ease: 'power3.out',
});
gsap.to(photo, {
scale: 1, opacity: 1,
duration: 0.9,
ease: 'power3.out',
});
attachTitleHover();
});
}
function attachTitleHover() {
[...line1Chars, ...line2Chars].forEach((c) => {
c.addEventListener('mouseenter', () => gsap.to(c, { color: '#a8dd1f', duration: 0.2 }));
c.addEventListener('mouseleave', () => gsap.to(c, { color: '#1a1a1a', duration: 0.4 }));
});
}
// -------------------------------------------------------------------
// MAIN SCENE ENTRANCE
// -------------------------------------------------------------------
function playScene() {
// Initial slide
const slide = slides[0];
line1Chars = makeChars(titleLine1, slide.line1);
line2Chars = [...slide.line2].map((ch) => {
const span = document.createElement('span');
span.className = 'hero__char';
if (ch === ' ') { span.classList.add('is-space'); span.innerHTML = ' '; }
else span.textContent = ch;
line2Text.appendChild(span);
return span;
});
descEl.textContent = slide.desc;
ctaLabel.textContent = slide.cta;
gsap.set([...line1Chars, ...line2Chars], { yPercent: 110, opacity: 0 });
const tl = gsap.timeline({ defaults: { ease: 'power3.out' } });
// Nav
tl.to(magnetics, {
y: 0, opacity: 1,
duration: 0.6,
stagger: 0.05,
}, 0);
// Title line 1
tl.to(line1Chars, {
yPercent: 0, opacity: 1,
duration: 0.9,
stagger: 0.025,
ease: 'expo.out',
}, 0.3);
// Star
tl.to(titleStar, {
scale: 1, rotation: 0,
duration: 0.6,
ease: 'back.out(2)',
}, 0.7);
// Title line 2
tl.to(line2Chars, {
yPercent: 0, opacity: 1,
duration: 0.9,
stagger: 0.025,
ease: 'expo.out',
}, 0.75);
// Form
tl.to(form, { y: 0, opacity: 1, duration: 0.7 }, 1.2);
// Desc + CTA
tl.to(descEl, { y: 0, opacity: 1, duration: 0.7 }, 1);
tl.to(ctaPill, {
scale: 1, opacity: 1,
duration: 0.7,
ease: 'back.out(1.6)',
}, 1.2);
// Photo
tl.to(photo, {
scale: 1, opacity: 1,
duration: 1.4,
ease: 'power4.out',
}, 0.8);
// Arrow button
tl.to(arrowBtn, {
scale: 1, opacity: 1, rotation: 0,
duration: 0.7,
ease: 'back.out(1.8)',
}, 1.4);
// Dots
tl.to(document.querySelector('[data-dots]'), {
y: 0, opacity: 1, duration: 0.5,
}, 1.6);
tl.call(() => {
attachTitleHover();
enableInteractions();
}, null, 1.7);
}
// -------------------------------------------------------------------
// INTERACTIONS
// -------------------------------------------------------------------
function enableInteractions() {
// ---- Magnetic ----
magnetics.forEach((el) => {
const strength = el.classList.contains('topnav__cta') ? 0.3
: el.classList.contains('cta-pill') ? 0.3
: el.classList.contains('image-wrap__arrow') ? 0.4
: el.classList.contains('topnav__logo') ? 0.2
: 0.22;
el.addEventListener('mousemove', (e) => {
const r = el.getBoundingClientRect();
const cx = r.left + r.width / 2;
const cy = r.top + r.height / 2;
gsap.to(el, {
x: (e.clientX - cx) * strength,
y: (e.clientY - cy) * strength,
duration: 0.4, ease: 'power3.out',
});
});
el.addEventListener('mouseleave', () => {
gsap.to(el, { x: 0, y: 0, duration: 0.6, ease: 'elastic.out(1, 0.4)' });
});
});
// ---- Logo bolt flash ----
const logoEl = root.querySelector('.topnav__logo');
logoEl.addEventListener('mouseenter', () => {
gsap.fromTo(logoMark,
{ scale: 1 },
{ scale: 1.3, duration: 0.15, yoyo: true, repeat: 1, ease: 'back.out(2)' }
);
});
// ---- Nav links: click advances slide ----
navLinks.forEach((link, i) => {
link.addEventListener('click', (e) => {
e.preventDefault();
if (i === currentSlide) return;
const direction = i > currentSlide ? 1 : -1;
currentSlide = i;
renderSlide(i, direction);
});
});
// ---- Dots click ----
dots.forEach((dot, i) => {
dot.addEventListener('click', () => {
if (i === currentSlide) return;
const direction = i > currentSlide ? 1 : -1;
currentSlide = i;
renderSlide(i, direction);
});
});
// ---- Slider prev/next arrows ----
const slidePrev = document.getElementById('slide-prev');
const slideNext = document.getElementById('slide-next');
const goSlide = (dir) => {
const next = (currentSlide + dir + slides.length) % slides.length;
currentSlide = next;
renderSlide(next, dir);
};
slidePrev.addEventListener('click', () => goSlide(-1));
slideNext.addEventListener('click', () => goSlide(1));
// ---- Keyboard ----
window.addEventListener('keydown', (e) => {
if (e.key === 'ArrowLeft') goSlide(-1);
if (e.key === 'ArrowRight') goSlide(1);
});
// Auto advance every 8s
let autoTimer = setInterval(() => {
const next = (currentSlide + 1) % slides.length;
currentSlide = next;
renderSlide(next, 1);
}, 8000);
root.addEventListener('mouseenter', () => clearInterval(autoTimer));
root.addEventListener('mouseleave', () => {
clearInterval(autoTimer);
autoTimer = setInterval(() => {
const next = (currentSlide + 1) % slides.length;
currentSlide = next;
renderSlide(next, 1);
}, 8000);
});
// ---- Arrow button click: spark burst ----
arrowBtn.addEventListener('click', (e) => {
e.preventDefault();
gsap.fromTo(arrowBtn,
{ scale: 1 },
{ scale: 0.9, duration: 0.1, yoyo: true, repeat: 1, ease: 'back.out(2)' }
);
const rect = arrowBtn.getBoundingClientRect();
const cx = rect.left + rect.width / 2;
const cy = rect.top + rect.height / 2;
for (let i = 0; i < 8; i++) {
const s = document.createElement('span');
s.style.cssText = `position:fixed;left:${cx}px;top:${cy}px;width:3px;height:20px;background:#c6ff3d;pointer-events:none;z-index:200;transform-origin:center;transform:translate(-50%,-50%) rotate(${i * 45}deg);box-shadow:0 0 8px #c6ff3d;`;
document.body.appendChild(s);
gsap.fromTo(s,
{ scale: 0, opacity: 1 },
{
scale: 2.5, opacity: 0,
duration: 0.6,
ease: 'power2.out',
onComplete: () => s.remove(),
}
);
}
});
// ---- CTA pill click ----
ctaPill.addEventListener('click', (e) => {
e.preventDefault();
gsap.fromTo(ctaPill,
{ scale: 1 },
{ scale: 0.95, duration: 0.1, yoyo: true, repeat: 1, ease: 'sine.inOut' }
);
});
// ---- Photo parallax ----
let mx = 0, my = 0, cmx = 0, cmy = 0;
imageWrap.addEventListener('mousemove', (e) => {
const r = imageWrap.getBoundingClientRect();
mx = ((e.clientX - r.left) / r.width - 0.5) * 2;
my = ((e.clientY - r.top) / r.height - 0.5) * 2;
});
imageWrap.addEventListener('mouseleave', () => { mx = 0; my = 0; });
gsap.ticker.add(() => {
cmx += (mx - cmx) * 0.05;
cmy += (my - cmy) * 0.05;
gsap.set(photoImg, {
scale: 1.05,
x: -cmx * 20,
y: -cmy * 15,
});
});
// ---- Star rotates on hover ----
titleStar.addEventListener('mouseenter', () => {
gsap.to(titleStar, { rotation: 180, duration: 0.7, ease: 'back.out(2)' });
});
titleStar.addEventListener('mouseleave', () => {
gsap.to(titleStar, { rotation: 0, duration: 0.6, ease: 'elastic.out(1, 0.4)' });
});
// Continuous star idle rotation
gsap.to(titleStar, {
rotation: '+=360',
duration: 20,
repeat: -1,
ease: 'none',
transformOrigin: 'center',
});
}
})();