92 lines
3.2 KiB
JavaScript
92 lines
3.2 KiB
JavaScript
// ===== SCROLL REVEAL — jf-* cards di dalam #assignment-body =====
|
|
function initScrollReveal() {
|
|
const body = document.getElementById('assignment-body');
|
|
if (!body) return;
|
|
|
|
const targets = body.querySelectorAll(
|
|
'.jf-stat-card, .jf-stat-row-card, .jf-prestasi-card, ' +
|
|
'.jf-sc-card, .jf-karir-card, .jf-alumni-card, ' +
|
|
'.jf-infra-card, .jf-mitra-card, .jf-tl-item, .jf-kontak-item'
|
|
);
|
|
targets.forEach(el => {
|
|
el.style.opacity = '0';
|
|
el.style.transform = 'translateY(24px)';
|
|
el.style.transition = 'opacity 0.55s ease, transform 0.55s ease';
|
|
});
|
|
|
|
const obs = new IntersectionObserver((entries) => {
|
|
entries.forEach((entry, i) => {
|
|
if (!entry.isIntersecting) return;
|
|
setTimeout(() => {
|
|
entry.target.style.opacity = '1';
|
|
entry.target.style.transform = 'translateY(0)';
|
|
}, i * 70);
|
|
obs.unobserve(entry.target);
|
|
});
|
|
}, { threshold: 0.08 });
|
|
|
|
targets.forEach(el => obs.observe(el));
|
|
}
|
|
|
|
// ===== PRESTASI FILTER — tab filter di dalam #assignment-body =====
|
|
function initPrestasiFilter() {
|
|
const body = document.getElementById('assignment-body');
|
|
if (!body) return;
|
|
|
|
const tabs = body.querySelectorAll('.jf-tab-btn');
|
|
const cards = body.querySelectorAll('#prestasi-grid .jf-prestasi-card');
|
|
if (!tabs.length) return;
|
|
|
|
tabs.forEach(tab => {
|
|
tab.addEventListener('click', () => {
|
|
tabs.forEach(t => t.classList.remove('jf-tab-active'));
|
|
tab.classList.add('jf-tab-active');
|
|
const filter = tab.dataset.filter;
|
|
cards.forEach(card => {
|
|
const show = filter === 'all' || card.dataset.cat === filter;
|
|
card.style.display = show ? '' : 'none';
|
|
if (show) card.style.animation = 'jfFadeIn 0.35s ease forwards';
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
// ===== ANIMATED COUNTER — .jf-stat-num di dalam #assignment-body =====
|
|
function initCounters() {
|
|
const body = document.getElementById('assignment-body');
|
|
if (!body) return;
|
|
|
|
const obs = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (!entry.isIntersecting) return;
|
|
const el = entry.target;
|
|
const raw = el.textContent.trim();
|
|
const num = parseFloat(raw.replace(/[^0-9.]/g, ''));
|
|
if (isNaN(num) || num === 0) return;
|
|
|
|
const prefix = raw.match(/^[^0-9]*/)[0];
|
|
const suffix = raw.match(/[^0-9.]*$/)[0];
|
|
|
|
let start = 0;
|
|
const step = 16;
|
|
const increment = num / (1400 / step);
|
|
const timer = setInterval(() => {
|
|
start = Math.min(start + increment, num);
|
|
const display = Number.isInteger(num)
|
|
? Math.floor(start).toLocaleString('id-ID')
|
|
: start.toFixed(1);
|
|
el.textContent = prefix + display + suffix;
|
|
if (start >= num) clearInterval(timer);
|
|
}, step);
|
|
|
|
obs.unobserve(el);
|
|
});
|
|
}, { threshold: 0.5 });
|
|
|
|
body.querySelectorAll('.jf-stat-num').forEach(el => obs.observe(el));
|
|
}
|
|
|
|
// ===== INIT =====
|
|
initScrollReveal();
|
|
initPrestasiFilter();
|
|
initCounters(); |