1
0
Files
ag-index/assets/js/timeline.js

258 lines
8.5 KiB
JavaScript
Raw Normal View History

2025-12-19 13:18:34 +08:00
/**
* 智慧时间轴组件 - 横向展开式
* Smart Timeline Component - Horizontal Expansion
*/
(function() {
'use strict';
document.addEventListener('DOMContentLoaded', initTimeline);
function forEachNode(list, callback) {
Array.prototype.forEach.call(list, callback);
}
function supportsSmoothScroll() {
return 'scrollBehavior' in document.documentElement.style;
}
2025-12-19 13:18:34 +08:00
function initTimeline() {
var container = document.querySelector('.timeline-container');
2025-12-19 13:18:34 +08:00
if (!container) return;
var items = container.querySelectorAll('.timeline-item');
2025-12-19 13:18:34 +08:00
if (!items.length) return;
var hasIntersectionObserver = 'IntersectionObserver' in window;
var touchStartX = null;
var scrollLeft = null;
var isDragging = false;
var startX;
var dragScrollLeft;
var currentIndex = 0;
var autoPlayIndex = 0;
var autoPlayTimer = null;
var isUserInteracting = false;
var interactionTimer = null;
var autoPlayInterval = 3000;
var pauseAfterInteraction = 5000;
var observerOptions = {
2025-12-19 13:18:34 +08:00
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
function updateExpandedState(active) {
if (active) {
container.classList.add('has-auto-expanded');
} else {
container.classList.remove('has-auto-expanded');
}
}
2025-12-19 13:18:34 +08:00
function clearExpandedItems() {
forEachNode(items, function(item) {
item.classList.remove('auto-expanded');
});
updateExpandedState(false);
}
2025-12-19 13:18:34 +08:00
function animateItems(useDelay) {
forEachNode(items, function(item, index) {
var delay = useDelay ? index * 80 : 0;
setTimeout(function() {
2025-12-19 13:18:34 +08:00
item.style.opacity = '1';
item.style.transform = 'translateY(0)';
}, delay);
2025-12-19 13:18:34 +08:00
});
}
forEachNode(items, function(item) {
2025-12-19 13:18:34 +08:00
item.style.opacity = '0';
item.style.transform = 'translateY(20px)';
item.style.transition = 'opacity 0.5s ease, transform 0.5s ease, flex 0.5s cubic-bezier(0.25, 0.1, 0.25, 1), background 0.5s ease';
});
if (hasIntersectionObserver) {
var observer = new IntersectionObserver(function(entries) {
for (var entryIndex = 0; entryIndex < entries.length; entryIndex += 1) {
if (entries[entryIndex].isIntersecting) {
animateItems(true);
observer.unobserve(entries[entryIndex].target);
}
}
}, observerOptions);
2025-12-19 13:18:34 +08:00
observer.observe(container);
} else {
animateItems(false);
}
container.addEventListener('touchstart', function(e) {
2025-12-19 13:18:34 +08:00
touchStartX = e.touches[0].pageX;
scrollLeft = container.scrollLeft;
}, { passive: true });
container.addEventListener('touchmove', function(e) {
2025-12-19 13:18:34 +08:00
if (!touchStartX) return;
var x = e.touches[0].pageX;
var walk = (touchStartX - x) * 1.5;
2025-12-19 13:18:34 +08:00
container.scrollLeft = scrollLeft + walk;
}, { passive: true });
container.addEventListener('touchend', function() {
2025-12-19 13:18:34 +08:00
touchStartX = null;
});
container.addEventListener('wheel', function(e) {
2025-12-19 13:18:34 +08:00
if (container.scrollWidth > container.clientWidth) {
if (Math.abs(e.deltaX) < Math.abs(e.deltaY)) {
e.preventDefault();
container.scrollLeft += e.deltaY;
}
}
}, { passive: false });
container.addEventListener('mousedown', function(e) {
2025-12-19 13:18:34 +08:00
if (container.scrollWidth <= container.clientWidth) return;
if (e.target.closest && e.target.closest('.timeline-item')) return;
2025-12-19 13:18:34 +08:00
isDragging = true;
container.style.cursor = 'grabbing';
startX = e.pageX - container.offsetLeft;
dragScrollLeft = container.scrollLeft;
});
container.addEventListener('mouseleave', function() {
2025-12-19 13:18:34 +08:00
isDragging = false;
container.style.cursor = '';
});
container.addEventListener('mouseup', function() {
2025-12-19 13:18:34 +08:00
isDragging = false;
container.style.cursor = '';
});
container.addEventListener('mousemove', function(e) {
2025-12-19 13:18:34 +08:00
if (!isDragging) return;
e.preventDefault();
var x = e.pageX - container.offsetLeft;
var walk = (x - startX) * 2;
2025-12-19 13:18:34 +08:00
container.scrollLeft = dragScrollLeft - walk;
});
forEachNode(items, function(item) {
item.addEventListener('click', function() {
var dot = item.querySelector('.dot');
2025-12-19 13:18:34 +08:00
if (dot) {
dot.style.transform = 'translate(-50%, -50%) scale(2)';
setTimeout(function() {
2025-12-19 13:18:34 +08:00
dot.style.transform = '';
}, 200);
}
});
});
document.addEventListener('keydown', function(e) {
2025-12-19 13:18:34 +08:00
if (!isElementInViewport(container)) return;
if (e.key === 'ArrowLeft') {
currentIndex = Math.max(0, currentIndex - 1);
scrollToItem(currentIndex);
} else if (e.key === 'ArrowRight') {
currentIndex = Math.min(items.length - 1, currentIndex + 1);
scrollToItem(currentIndex);
}
});
function scrollToItem(index) {
if (container.scrollWidth <= container.clientWidth) return;
var item = items[index];
2025-12-19 13:18:34 +08:00
if (!item) return;
var containerRect = container.getBoundingClientRect();
var itemRect = item.getBoundingClientRect();
var targetScrollLeft = container.scrollLeft + (itemRect.left - containerRect.left) - (containerRect.width / 2) + (itemRect.width / 2);
2025-12-19 13:18:34 +08:00
if (typeof container.scrollTo === 'function' && supportsSmoothScroll()) {
container.scrollTo({
left: targetScrollLeft,
behavior: 'smooth'
});
return;
}
2025-12-19 13:18:34 +08:00
container.scrollLeft = targetScrollLeft;
2025-12-19 13:18:34 +08:00
}
function expandItem(index) {
clearExpandedItems();
2025-12-19 13:18:34 +08:00
if (items[index]) {
items[index].classList.add('auto-expanded');
updateExpandedState(true);
2025-12-19 13:18:34 +08:00
}
}
function autoPlay() {
if (isUserInteracting) return;
expandItem(autoPlayIndex);
autoPlayIndex = (autoPlayIndex + 1) % items.length;
}
function startAutoPlay() {
if (autoPlayTimer) return;
autoPlayTimer = setInterval(autoPlay, autoPlayInterval);
autoPlay();
}
function stopAutoPlay() {
if (autoPlayTimer) {
clearInterval(autoPlayTimer);
autoPlayTimer = null;
}
clearExpandedItems();
2025-12-19 13:18:34 +08:00
}
function pauseAutoPlay() {
isUserInteracting = true;
stopAutoPlay();
if (interactionTimer) {
clearTimeout(interactionTimer);
}
interactionTimer = setTimeout(function() {
2025-12-19 13:18:34 +08:00
isUserInteracting = false;
if (hasIntersectionObserver && isElementInViewport(container)) {
2025-12-19 13:18:34 +08:00
startAutoPlay();
}
}, pauseAfterInteraction);
}
container.addEventListener('mouseenter', pauseAutoPlay);
container.addEventListener('touchstart', pauseAutoPlay, { passive: true });
if (hasIntersectionObserver) {
var autoPlayObserver = new IntersectionObserver(function(entries) {
for (var entryIndex = 0; entryIndex < entries.length; entryIndex += 1) {
if (entries[entryIndex].isIntersecting && !isUserInteracting) {
startAutoPlay();
} else {
stopAutoPlay();
}
2025-12-19 13:18:34 +08:00
}
}, { threshold: 0.3 });
2025-12-19 13:18:34 +08:00
autoPlayObserver.observe(container);
}
2025-12-19 13:18:34 +08:00
}
function isElementInViewport(el) {
var rect = el.getBoundingClientRect();
2025-12-19 13:18:34 +08:00
return (
rect.top < window.innerHeight &&
rect.bottom > 0
);
}
})();