| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- // Hover Grid Auto Setup with Edge Detection and Animation Div Creation
- // Debug elements - DOM references for visual debugging
- const debugInfo = document.getElementById('debugInfo');
- const directionSpan = document.getElementById('direction');
- const statusSpan = document.getElementById('status');
- // Updates the debug information display
- function updateDebug(direction, status) {
- if (directionSpan && statusSpan) {
- directionSpan.textContent = direction || '-';
- statusSpan.textContent = status || '-';
- }
- }
- // HoverGridSystem Class
- class HoverGridSystem {
- constructor() {
- this.activeCards = new Map(); // Tracks active cards and animations
- this.init();
- }
- init() {
- document.querySelectorAll('[data-hover-area]').forEach(parentGrid => {
- const gridSelector = parentGrid.getAttribute('data-hover-area');
- const grid = document.querySelector(gridSelector);
- if (!grid) {
- console.warn(`Grid not found: ${gridSelector}`);
- return;
- }
- const config = this.extractConfig(parentGrid);
- this.setupGrid(grid, config);
- });
- }
- extractConfig(element) {
- return {
- hoverColor: element.getAttribute('data-hover-color') || 'rgba(0, 191, 255, 0.3)',
- targetSelector: element.getAttribute('data-target') || null,
- targetColor: element.getAttribute('data-target-color') || '#00BFFF',
- animationDuration: element.getAttribute('data-duration') || '0.6s',
- animationEasing: element.getAttribute('data-easing') || 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'
- };
- }
- setupGrid(grid, config) {
- const cards = Array.from(grid.children);
- cards.forEach(card => {
- this.setupCard(card, config);
- });
- }
- setupCard(card, config) {
- if (getComputedStyle(card).position === 'static') {
- card.style.position = 'relative';
- }
- card.style.overflow = 'hidden';
- const overlay = document.createElement('div');
- overlay.className = 'anim-overlay';
- overlay.style.cssText = `
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: ${config.hoverColor};
- opacity: 0;
- z-index: 0;
- pointer-events: none;
- `;
- card.appendChild(overlay);
- // Ensure all content is above the overlay
- Array.from(card.children).forEach(child => {
- if (!child.classList.contains('anim-overlay')) {
- child.style.position = 'relative';
- child.style.zIndex = '1';
- }
- });
- if (config.targetSelector) {
- const targetElement = card.querySelector(config.targetSelector);
- if (targetElement) {
- targetElement.style.transition = `color ${config.animationDuration} ${config.animationEasing}`;
- }
- }
- card.addEventListener('mouseenter', (e) => this.handleMouseEnter(card, overlay, e, config));
- card.addEventListener('mouseleave', (e) => this.handleMouseLeave(card, overlay, e, config));
- }
- handleMouseEnter(card, overlay, event, config) {
- const direction = this.getDirection(card, event.clientX, event.clientY);
- updateDebug(direction, 'ENTER');
- this.cancelAnimation(card);
- if (config.targetSelector) {
- const targetElement = card.querySelector(config.targetSelector);
- if (targetElement) {
- targetElement.style.color = config.targetColor;
- }
- }
- this.animateEntry(overlay, direction, config);
- this.activeCards.set(card, {
- overlay: overlay,
- direction: direction,
- isActive: true,
- config: config
- });
- }
- handleMouseLeave(card, overlay, event, config) {
- const direction = this.getDirection(card, event.clientX, event.clientY);
- updateDebug(direction, 'LEAVE');
- if (config.targetSelector) {
- const targetElement = card.querySelector(config.targetSelector);
- if (targetElement) {
- targetElement.style.color = '';
- }
- }
- this.animateExit(overlay, direction, config);
- const duration = parseFloat(config.animationDuration) * 1000;
- setTimeout(() => {
- this.activeCards.delete(card);
- }, duration + 100);
- }
- getDirection(element, mouseX, mouseY) {
- const rect = element.getBoundingClientRect();
- const centerX = rect.left + rect.width / 2;
- const centerY = rect.top + rect.height / 2;
- const angle = Math.atan2(mouseY - centerY, mouseX - centerX);
- const degree = angle * (180 / Math.PI);
- if (degree >= -45 && degree < 45) {
- return 'right';
- } else if (degree >= 45 && degree < 135) {
- return 'bottom';
- } else if (degree >= 135 || degree < -135) {
- return 'left';
- } else {
- return 'top';
- }
- }
- animateEntry(overlay, direction, config) {
- overlay.style.transition = 'none';
- overlay.style.opacity = '1';
- overlay.style.transform = this.getInitialSlideTransform(direction);
- overlay.offsetHeight;
- overlay.style.transition = `transform ${config.animationDuration} ${config.animationEasing}`;
- overlay.style.transform = 'translate(0, 0)';
- }
- animateExit(overlay, direction, config) {
- const exitEasing = 'cubic-bezier(0.55, 0.085, 0.68, 0.53)';
- overlay.style.transition = `transform ${config.animationDuration} ${exitEasing}`;
- overlay.style.transform = this.getExitSlideTransform(direction);
- const duration = parseFloat(config.animationDuration) * 1000;
- setTimeout(() => {
- overlay.style.opacity = '0';
- overlay.style.transition = 'none';
- }, duration);
- }
- getInitialSlideTransform(direction) {
- switch(direction) {
- case 'top':
- return 'translateY(-100%)';
- case 'right':
- return 'translateX(100%)';
- case 'bottom':
- return 'translateY(100%)';
- case 'left':
- return 'translateX(-100%)';
- default:
- return 'translateY(-100%)';
- }
- }
- getExitSlideTransform(direction) {
- switch(direction) {
- case 'top':
- return 'translateY(-100%)';
- case 'right':
- return 'translateX(100%)';
- case 'bottom':
- return 'translateY(100%)';
- case 'left':
- return 'translateX(-100%)';
- default:
- return 'translateY(-100%)';
- }
- }
- cancelAnimation(card) {
- const cardData = this.activeCards.get(card);
- if (cardData && cardData.overlay) {
- cardData.overlay.style.transition = 'none';
- }
- }
- }
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', () => {
- new HoverGridSystem();
- });
- } else {
- new HoverGridSystem();
- }
- if (debugInfo) {
- setTimeout(() => {
- debugInfo.style.opacity = '0.3';
- debugInfo.style.transition = 'opacity 0.5s ease';
- }, 5000);
- }
|