UNPKG

my-animation-lib

Version:

A powerful animation library combining Three.js, GSAP, custom scroll triggers, and advanced effects with MathUtils integration

453 lines (404 loc) 12.7 kB
/** * DOM Utilities for common DOM operations */ export class DOMUtils { /** * Create an element with attributes and content * @param {string} tag - HTML tag name * @param {Object} attributes - Element attributes * @param {string|Element} content - Element content * @returns {HTMLElement} */ static createElement(tag, attributes = {}, content = '') { const element = document.createElement(tag); // Set attributes Object.keys(attributes).forEach(key => { if (key === 'className') { element.className = attributes[key]; } else if (key === 'style' && typeof attributes[key] === 'object') { Object.assign(element.style, attributes[key]); } else { element.setAttribute(key, attributes[key]); } }); // Set content if (typeof content === 'string') { element.textContent = content; } else if (content instanceof Element) { element.appendChild(content); } return element; } /** * Get element by selector * @param {string} selector - CSS selector * @param {Element} parent - Parent element (defaults to document) * @returns {Element|null} */ static getElement(selector, parent = document) { return parent.querySelector(selector); } /** * Get all elements by selector * @param {string} selector - CSS selector * @param {Element} parent - Parent element (defaults to document) * @returns {NodeList} */ static getElements(selector, parent = document) { return parent.querySelectorAll(selector); } /** * Add event listener with options * @param {Element} element - Target element * @param {string} event - Event type * @param {Function} handler - Event handler * @param {Object} options - Event options */ static addEvent(element, event, handler, options = {}) { element.addEventListener(event, handler, options); } /** * Remove event listener * @param {Element} element - Target element * @param {string} event - Event type * @param {Function} handler - Event handler * @param {Object} options - Event options */ static removeEvent(element, event, handler, options = {}) { element.removeEventListener(event, handler, options); } /** * Add multiple event listeners * @param {Element} element - Target element * @param {Object} events - Event handlers object * @param {Object} options - Event options */ static addEvents(element, events, options = {}) { Object.keys(events).forEach(event => { this.addEvent(element, event, events[event], options); }); } /** * Remove multiple event listeners * @param {Element} element - Target element * @param {Object} events - Event handlers object * @param {Object} options - Event options */ static removeEvents(element, events, options = {}) { Object.keys(events).forEach(event => { this.removeEvent(element, event, events[event], options); }); } /** * Add CSS class to element * @param {Element} element - Target element * @param {string} className - CSS class name */ static addClass(element, className) { element.classList.add(className); } /** * Remove CSS class from element * @param {Element} element - Target element * @param {string} className - CSS class name */ static removeClass(element, className) { element.classList.remove(className); } /** * Toggle CSS class on element * @param {Element} element - Target element * @param {string} className - CSS class name * @returns {boolean} - Whether class was added */ static toggleClass(element, className) { return element.classList.toggle(className); } /** * Check if element has CSS class * @param {Element} element - Target element * @param {string} className - CSS class name * @returns {boolean} */ static hasClass(element, className) { return element.classList.contains(className); } /** * Set element style * @param {Element} element - Target element * @param {string|Object} property - CSS property or style object * @param {string} value - CSS value (if property is string) */ static setStyle(element, property, value) { if (typeof property === 'object') { Object.assign(element.style, property); } else { element.style[property] = value; } } /** * Get element style * @param {Element} element - Target element * @param {string} property - CSS property * @returns {string} */ static getStyle(element, property) { return window.getComputedStyle(element)[property]; } /** * Set element attributes * @param {Element} element - Target element * @param {Object} attributes - Attributes object */ static setAttributes(element, attributes) { Object.keys(attributes).forEach(key => { element.setAttribute(key, attributes[key]); }); } /** * Get element attribute * @param {Element} element - Target element * @param {string} attribute - Attribute name * @returns {string|null} */ static getAttribute(element, attribute) { return element.getAttribute(attribute); } /** * Remove element attribute * @param {Element} element - Target element * @param {string} attribute - Attribute name */ static removeAttribute(element, attribute) { element.removeAttribute(attribute); } /** * Append child to element * @param {Element} parent - Parent element * @param {Element} child - Child element */ static appendChild(parent, child) { parent.appendChild(child); } /** * Insert element before reference * @param {Element} parent - Parent element * @param {Element} element - Element to insert * @param {Element} reference - Reference element */ static insertBefore(parent, element, reference) { parent.insertBefore(element, reference); } /** * Remove element from DOM * @param {Element} element - Element to remove */ static removeElement(element) { if (element.parentNode) { element.parentNode.removeChild(element); } } /** * Clear element content * @param {Element} element - Target element */ static clearContent(element) { element.innerHTML = ''; } /** * Get element dimensions and position * @param {Element} element - Target element * @returns {Object} - Object with width, height, top, left, right, bottom */ static getElementRect(element) { const rect = element.getBoundingClientRect(); return { width: rect.width, height: rect.height, top: rect.top + window.scrollY, left: rect.left + window.scrollX, right: rect.right + window.scrollX, bottom: rect.bottom + window.scrollY }; } /** * Check if element is in viewport * @param {Element} element - Target element * @param {number} threshold - Visibility threshold (0-1) * @returns {boolean} */ static isInViewport(element, threshold = 0) { const rect = element.getBoundingClientRect(); const windowHeight = window.innerHeight; const windowWidth = window.innerWidth; const visibleHeight = Math.min(rect.bottom, windowHeight) - Math.max(rect.top, 0); const visibleWidth = Math.min(rect.right, windowWidth) - Math.max(rect.left, 0); const visibleArea = visibleHeight * visibleWidth; const totalArea = rect.height * rect.width; return visibleArea / totalArea > threshold; } /** * Get scroll position * @returns {Object} - Object with x and y scroll positions */ static getScrollPosition() { return { x: window.pageXOffset || document.documentElement.scrollLeft, y: window.pageYOffset || document.documentElement.scrollTop }; } /** * Scroll to element * @param {Element} element - Target element * @param {Object} options - Scroll options */ static scrollToElement(element, options = {}) { const { behavior = 'smooth', block = 'start', inline = 'nearest' } = options; element.scrollIntoView({ behavior, block, inline }); } /** * Scroll to position * @param {number} x - X position * @param {number} y - Y position * @param {Object} options - Scroll options */ static scrollToPosition(x, y, options = {}) { const { behavior = 'smooth' } = options; window.scrollTo({ left: x, top: y, behavior }); } /** * Debounce function execution * @param {Function} func - Function to debounce * @param {number} wait - Wait time in milliseconds * @returns {Function} - Debounced function */ static debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } /** * Throttle function execution * @param {Function} func - Function to throttle * @param {number} limit - Time limit in milliseconds * @returns {Function} - Throttled function */ static throttle(func, limit) { let inThrottle; return function executedFunction(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } /** * Wait for element to exist in DOM * @param {string} selector - CSS selector * @param {number} timeout - Timeout in milliseconds * @returns {Promise<Element>} */ static waitForElement(selector, timeout = 5000) { return new Promise((resolve, reject) => { const element = document.querySelector(selector); if (element) { resolve(element); return; } const observer = new MutationObserver((mutations) => { const element = document.querySelector(selector); if (element) { observer.disconnect(); resolve(element); } }); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(() => { observer.disconnect(); reject(new Error(`Element ${selector} not found within ${timeout}ms`)); }, timeout); }); } /** * Create and dispatch custom event * @param {string} eventName - Event name * @param {Object} detail - Event detail * @param {Element} target - Target element (defaults to document) */ static dispatchCustomEvent(eventName, detail = {}, target = document) { const event = new CustomEvent(eventName, { detail, bubbles: true, cancelable: true }); target.dispatchEvent(event); } /** * Get computed styles for element * @param {Element} element - Target element * @param {Array<string>} properties - CSS properties to get * @returns {Object} - Object with property values */ static getComputedStyles(element, properties = []) { const styles = window.getComputedStyle(element); const result = {}; properties.forEach(property => { result[property] = styles[property]; }); return result; } /** * Animate element property * @param {Element} element - Target element * @param {string} property - CSS property * @param {*} from - Start value * @param {*} to - End value * @param {Object} options - Animation options */ static animateProperty(element, property, from, to, options = {}) { const { duration = 1000, easing = 'ease', onUpdate = null, onComplete = null } = options; const startTime = performance.now(); const startValue = from; const changeValue = to - from; function animate(currentTime) { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); const currentValue = startValue + changeValue * progress; element.style[property] = currentValue; if (onUpdate) { onUpdate(currentValue, progress); } if (progress < 1) { requestAnimationFrame(animate); } else if (onComplete) { onComplete(); } } requestAnimationFrame(animate); } }