
Burada tüm GSAP sınıflarına, GSAP'a bağlı Webflow değişkenlerine ve GSAP'ı nasıl devre dışı bırakacağınıza dair talimatlara erişebilirsiniz. Bu, bu şablonu tamamen kişiselleştirmenize ve önemli ölçüde değiştirmenize olanak tanır.
Bu şablon, akıcı ve etkileşimli animasyonlar oluşturmak için GSAP (GreenSock Animation Platform) ve ScrollTrigger kullanır. Ek kütüphaneler arasında metin animasyonları için SplitType bulunur. Bu kurulumla şunları yapabilirsiniz:
// ===== UPDATED BUTTON HOVER ANIMATION =====
document.querySelectorAll(".button-wrapper,.dropdown-nav-link").forEach(button => {
const text1 = button.querySelector(".btn-text-1,.nav-link-text-1");
const text2 = button.querySelector(".btn-text-2,.nav-link-text-2");
if (!text1 || !text2) return;
// Split text into individual spans
function splitText(textEl) {
const text = textEl.textContent.trim();
if (!text) return;
textEl.innerHTML = '';
text.split('').forEach(letter => {
const span = document.createElement('span');
if (letter === ' ') {
span.innerHTML = ' ';
span.style.display = 'inline-block';
span.style.width = '0.3em';
} else {
span.textContent = letter;
span.style.display = 'inline-block';
}
textEl.appendChild(span);
});
textEl.style.position = 'relative';
}
splitText(text1);
splitText(text2);
const spans1 = text1.querySelectorAll('span');
const spans2 = text2.querySelectorAll('span');
const baseStagger = 0.04;
// set initial state for text2
gsap.set(spans2, { y: 40, opacity: 0 });
const tl = gsap.timeline({ paused: true });
// animate text1 up and out
tl.to(spans1, {
y: -45,
opacity: 0,
stagger: baseStagger,
duration: 0.20, // shorter duration
ease: "power2.in"
}, 0.001);
// animate text2 from below into place
tl.to(spans2, {
y: -26,
opacity: 1,
stagger: baseStagger,
duration: 0.20, // shorter duratio
ease: "power2.out"
}, 0.001);
// hover triggers
button.addEventListener("mouseenter", () => tl.play());
button.addEventListener("mouseleave", () => tl.reverse());
});Düğmelerin (.button-wrapper ve .dropdown-nav-link) iki metin katmanı vardır:
// ===== IMAGE SCROLL ANIMATION =====
gsap.utils.toArray(".blur-image-animation").forEach((img) => {
gsap.fromTo(img,
{
filter: "blur(10px)",
scale: 1.1,
opacity: 0,
},
{
scrollTrigger: {
trigger: img,
start: "top 80%",
toggleActions: "play none none none",
once: true
},
filter: "blur(0px)",
scale: 1,
opacity: 1,
duration: 1.2,
ease: "power2.out"
}
);
});
}); // ===== BIG HEADING SCROLL ANIMATION =====
// Only run heading animation if screen width is above 768px (adjust if needed)
if (window.innerWidth > 768) {
document.querySelectorAll(".text-reveal-animation").forEach((heading) => {
const split = new SplitType(heading, { types: 'chars' });
const chars = split.chars;
const center = (chars.length - 1) / 2;
gsap.set(chars, {
opacity: 0,
filter: 'blur(10px)',
scale: 0.8
});
gsap.to(chars, {
opacity: 1,
filter: 'blur(0px)',
scale: 1,
duration: 0.6,
ease: 'power2.out',
stagger: (i) => Math.abs(i - center) * 0.04,
scrollTrigger: {
trigger: heading,
start: 'top 90%',
once: true
}
});
});
}<script>
// ===== COUNTER ANIMATION =====
window.addEventListener("load", function() {
const counters = document.querySelectorAll("[data-count]");
const observer = new IntersectionObserver( (entries, observer) => {
entries.forEach(entry => {
if (!entry.isIntersecting)
return;
const el = entry.target;
// Remove the thousand separator dots, then convert to a number.
const target = parseInt(el.getAttribute("data-count").replace(/\./g, ""), 10);
const duration = 3000;
// in ms
const frameRate = 60;
const totalFrames = Math.round(duration / (1000 / frameRate));
let frame = 0;
const count = () => {
frame++;
const progress = frame / totalFrames;
const current = Math.round(target * progress);
// Format the number with thousand separators
el.innerText = current.toLocaleString("id-ID");
if (frame < totalFrames) {
requestAnimationFrame(count);
} else {
el.innerText = target.toLocaleString("id-ID");
}
}
;
requestAnimationFrame(count);
observer.unobserve(el);
}
);
}
,{
threshold: 0.6
});
counters.forEach(el => observer.observe(el));
});
</script>Bu JavaScript kodu, öğe görünüm alanında görünür olduğunda başlayan akıcı bir sayma animasyonu oluşturur. Genellikle kaydırmada animasyonlu istatistikler veya sayı sayaçları için kullanılır.
Bu betik, data-count özniteliğine sahip HTML öğelerinin içindeki sayısal değerleri animasyonla gösterir. Kullanıcı bu öğeleri içeren bir bölüme kaydırdığında, sayılar 0'dan hedef değerlerine kadar akıcı bir şekilde sayılır.
// TIMER JS (GSAP-based)
const newYearEnd = new Tarih('Jan 01 2027 00:00:00');
const time = {
days: 0,
hours: 0,
minutes: 0,
seconds: 0
};
// Elements
const __days = document.querySelector("#days");
const __hours = document.querySelector("#hours");
const __minutes = document.querySelector("#minutes");
const __seconds = document.querySelector("#seconds");
// Function to calculate remaining time
function getTime() {
const current = new Tarih();
const totalSeconds = (newYearEnd - current) / 1000;
return {
days: Math.floor(totalSeconds / 3600 / 24),
hours: Math.floor(totalSeconds / 3600) % 24,
minutes: Math.floor(totalSeconds / 60) % 60,
seconds: Math.floor(totalSeconds % 60)
};
}
// Smoothly animate numbers
gsap.to(time, {
days: getTime().days,
hours: getTime().hours,
minutes: getTime().minutes,
seconds: getTime().seconds,
duration: 1,
repeat: -1,
ease: "none",
onUpdate: () => {
const t = getTime(); // recalc real time on each frame
__days.innerHTML = t.days < 10 ? `0${t.days}` : t.days;
__hours.innerHTML = t.hours < 10 ? `0${t.hours}` : t.hours;
__minutes.innerHTML = t.minutes < 10 ? `0${t.minutes}` : t.minutes;
__seconds.innerHTML = t.seconds < 10 ? `0${t.seconds}` : t.seconds;
}
});/*
Webflow ||= [];
Webflow.push(() => {
gsap.registerPlugin(ScrollTrigger);
// All GSAP animations here are now inactive
});
*/
- Düğme Sarmalayıcı
- Btn Text 1 / Btn Text 2
- Metin Ortaya Çıkma Animasyonu
- Bulanık Görsel Animasyonu