UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

435 lines (432 loc) 13.7 kB
import { InterpolationUtils } from './interpolation.js'; const easingFunctions = { linear: t => t, easeInQuad: t => t * t, easeOutQuad: t => t * (2 - t), easeInOutQuad: t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t, easeInCubic: t => t * t * t, easeOutCubic: t => --t * t * t + 1, easeInOutCubic: t => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1, easeInQuart: t => t * t * t * t, easeOutQuart: t => 1 - --t * t * t * t, easeInOutQuart: t => t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t, easeInSine: t => 1 - Math.cos(t * Math.PI / 2), easeOutSine: t => Math.sin(t * Math.PI / 2), easeInOutSine: t => -(Math.cos(Math.PI * t) - 1) / 2, easeInExpo: t => t === 0 ? 0 : Math.pow(2, 10 * t - 10), easeOutExpo: t => t === 1 ? 1 : 1 - Math.pow(2, -10 * t), easeInOutExpo: t => { if (t === 0) return 0; if (t === 1) return 1; return t < 0.5 ? Math.pow(2, 20 * t - 10) / 2 : (2 - Math.pow(2, -20 * t + 10)) / 2; }, easeInBack: t => { const c1 = 1.70158; const c3 = c1 + 1; return c3 * t * t * t - c1 * t * t; }, easeOutBack: t => { const c1 = 1.70158; const c3 = c1 + 1; return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2); }, easeInOutBack: t => { const c1 = 1.70158; const c2 = c1 * 1.525; return t < 0.5 ? Math.pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2) / 2 : (Math.pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2; }, easeOutElastic: t => { const c4 = 2 * Math.PI / 3; return t === 0 ? 0 : t === 1 ? 1 : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1; } }; class ChartAnimationUtils { /** * Animate chart data points with various effects */ static animateDataPoints(dataPoints, config, onUpdate, onComplete) { const { duration = 1000, stagger = 0, direction = 'up', easing = 'easeOutQuad', delay = 0 } = config; const startTime = Date.now() + delay; const easeFn = easingFunctions?.[easing]; const animatedPoints = [...dataPoints]; const animate = () => { const elapsed = Date.now() - startTime; const rawProgress = Math.min(elapsed / duration, 1); const progress = easeFn(rawProgress); // Apply direction-based animation dataPoints.forEach((point, index) => { const staggerDelay = index * stagger; const pointProgress = Math.max(0, Math.min(1, (elapsed - staggerDelay) / duration)); const easedPointProgress = easeFn(pointProgress); const animatedPoint = this.applyDirectionalAnimation(point, easedPointProgress, direction); if (animatedPoints) { animatedPoints[index] = animatedPoint; } }); onUpdate?.(animatedPoints, progress); if (rawProgress < 1) { requestAnimationFrame(animate); } else { onComplete?.(animatedPoints); } }; const timeoutId = setTimeout(() => { requestAnimationFrame(animate); }, delay); return () => clearTimeout(timeoutId); } /** * Animate entire chart series */ static animateSeries(series, config, onUpdate, onComplete) { const { duration = 1000, stagger = 100, direction = 'up', easing = 'easeOutQuad', delay = 0 } = config; const startTime = Date.now() + delay; const easeFn = easingFunctions?.[easing]; const animatedSeries = series.map(s => ({ ...s, data: [...s.data] })); const animate = () => { const elapsed = Date.now() - startTime; const rawProgress = Math.min(elapsed / duration, 1); const progress = easeFn(rawProgress); series.forEach((seriesItem, seriesIndex) => { const seriesDelay = seriesIndex * stagger; const seriesProgress = Math.max(0, Math.min(1, (elapsed - seriesDelay) / duration)); const easedSeriesProgress = easeFn(seriesProgress); seriesItem.data?.forEach((point, pointIndex) => { const animatedPoint = this.applyDirectionalAnimation(point, easedSeriesProgress, direction); if (animatedSeries[seriesIndex].data) { animatedSeries[seriesIndex].data[pointIndex] = animatedPoint; } }); }); onUpdate?.(animatedSeries, progress); if (rawProgress < 1) { requestAnimationFrame(animate); } else { onComplete?.(animatedSeries); } }; const timeoutId = setTimeout(() => { requestAnimationFrame(animate); }, delay); return () => clearTimeout(timeoutId); } /** * Create morphing animation between two data sets */ static createMorphingAnimation(fromSeries, toSeries, config, onUpdate, onComplete) { const { duration = 1000, easing = 'easeInOutQuad', delay = 0 } = config; const startTime = Date.now() + delay; const easeFn = easingFunctions?.[easing]; const morphedSeries = toSeries.map(s => ({ ...s, data: [...s.data] })); const animate = () => { const elapsed = Date.now() - startTime; const rawProgress = Math.min(elapsed / duration, 1); const progress = easeFn(rawProgress); toSeries.forEach((toSeriesItem, seriesIndex) => { const fromSeriesItem = fromSeries?.[seriesIndex]; if (!fromSeriesItem) return; toSeriesItem.data?.forEach((toPoint, pointIndex) => { const fromPoint = fromSeriesItem.data?.[pointIndex]; if (!fromPoint) return; const morphedPoint = { ...toPoint, x: InterpolationUtils.lerp(fromPoint.x, toPoint.x, progress), y: InterpolationUtils.lerp(fromPoint.y, toPoint.y, progress) }; if (morphedSeries[seriesIndex].data) { morphedSeries[seriesIndex].data[pointIndex] = morphedPoint; } }); }); onUpdate?.(morphedSeries, progress); if (rawProgress < 1) { requestAnimationFrame(animate); } else { onComplete?.(morphedSeries); } }; const timeoutId = setTimeout(() => { requestAnimationFrame(animate); }, delay); return () => clearTimeout(timeoutId); } /** * Create entrance animation for chart elements */ static createEntranceAnimation(series, effect, config, onUpdate, onComplete) { const { duration = 1000, stagger = 100, direction = 'up', delay = 0 } = config; const effects = { fadeIn: { opacity: 0 }, slideIn: this.getDirectionalTransform(direction, 50), scaleIn: { scale: 0 }, bounceIn: { scale: 0.3 }, elasticIn: { scale: 0, rotation: -180 } }; const effectConfig = effects?.[effect]; const animatedSeries = series.map(s => ({ ...s, data: [...s.data] })); return this.animateSeries(series, { ...config, duration, stagger, delay }, (updatedSeries, progress) => { updatedSeries.forEach((seriesItem, seriesIndex) => { seriesItem.data?.forEach((point, pointIndex) => { if (animatedSeries[seriesIndex].data) { animatedSeries[seriesIndex].data[pointIndex] = { ...point, ...this.interpolatePointProperties(effectConfig, point, progress) }; } }); }); onUpdate?.(animatedSeries, progress); }, onComplete); } /** * Create exit animation for chart elements */ static createExitAnimation(series, effect, config, onUpdate, onComplete) { const { duration = 500, stagger = 50, direction = 'down', delay = 0 } = config; const effects = { fadeOut: { opacity: 1 }, slideOut: this.getDirectionalTransform(direction, 0), scaleOut: { scale: 1 }, bounceOut: { scale: 1 } }; const effectConfig = effects?.[effect]; const animatedSeries = series.map(s => ({ ...s, data: [...s.data] })); return this.animateSeries(series, { ...config, duration, stagger, delay }, (updatedSeries, progress) => { const exitProgress = 1 - progress; updatedSeries.forEach((seriesItem, seriesIndex) => { seriesItem.data?.forEach((point, pointIndex) => { if (animatedSeries[seriesIndex].data) { animatedSeries[seriesIndex].data[pointIndex] = { ...point, ...this.interpolatePointProperties(effectConfig, point, exitProgress) }; } }); }); onUpdate?.(animatedSeries, progress); }, onComplete); } static applyDirectionalAnimation(point, progress, direction) { const transform = this.getDirectionalTransform(direction, 1 - progress); return { ...point, ...this.interpolatePointProperties(transform, point, progress) }; } static getDirectionalTransform(direction, distance) { switch (direction) { case 'up': return { y: distance * 50 }; case 'down': return { y: -distance * 50 }; case 'left': return { x: distance * 50 }; case 'right': return { x: -distance * 50 }; case 'center': return { scale: distance }; default: return { y: distance * 50 }; } } static interpolatePointProperties(transform, point, progress) { const result = {}; if (transform.x !== undefined) { result.x = point.x + transform.x * (1 - progress); } if (transform.y !== undefined) { result.y = point.y + transform.y * (1 - progress); } if (transform.scale !== undefined) { // Scale would typically be handled by CSS transform result.scale = InterpolationUtils.lerp(transform.scale, 1, progress); } if (transform.opacity !== undefined) { result.opacity = InterpolationUtils.lerp(transform.opacity, 1, progress); } if (transform.rotation !== undefined) { result.rotation = InterpolationUtils.lerp(transform.rotation, 0, progress); } return result; } /** * Create a smooth transition between chart states */ static createChartTransition(transition, onUpdate, onComplete) { const { from, to, duration, easing, stagger } = transition; const easeFn = easingFunctions?.[easing]; const startTime = Date.now(); const animatedSeries = to.map(s => ({ ...s, data: [...s.data] })); const animate = () => { const elapsed = Date.now() - startTime; const rawProgress = Math.min(elapsed / duration, 1); const progress = easeFn(rawProgress); to.forEach((toSeriesItem, seriesIndex) => { const fromSeriesItem = from?.[seriesIndex]; if (!fromSeriesItem) return; const seriesDelay = seriesIndex * stagger; const seriesProgress = Math.max(0, Math.min(1, (elapsed - seriesDelay) / duration)); const easedSeriesProgress = easeFn(seriesProgress); toSeriesItem.data?.forEach((toPoint, pointIndex) => { const fromPoint = fromSeriesItem.data?.[pointIndex]; if (!fromPoint) return; if (animatedSeries[seriesIndex].data) { animatedSeries[seriesIndex].data[pointIndex] = { ...toPoint, x: InterpolationUtils.lerp(fromPoint.x, toPoint.x, easedSeriesProgress), y: InterpolationUtils.lerp(fromPoint.y, toPoint.y, easedSeriesProgress) }; } }); }); onUpdate?.(animatedSeries, progress); if (rawProgress < 1) { requestAnimationFrame(animate); } else { onComplete?.(animatedSeries); } }; const animationId = requestAnimationFrame(animate); return () => cancelAnimationFrame(animationId); } } // Predefined animation presets const chartAnimationPresets = { gentleFadeIn: { duration: 800, easing: 'easeOutQuad', stagger: 50, direction: 'up' }, dramaticEntrance: { duration: 1200, easing: 'easeOutBack', stagger: 100, direction: 'center' }, smoothSlideIn: { duration: 600, easing: 'easeOutCubic', stagger: 75, direction: 'left' }, bouncyScale: { duration: 1000, easing: 'easeOutElastic', stagger: 150, direction: 'center' }, subtleMorph: { duration: 500, easing: 'easeInOutQuad', stagger: 0, direction: 'up' } }; // Utility functions for common chart animations const animateChart = { fadeIn: (series, config, callbacks) => ChartAnimationUtils.createEntranceAnimation(series, 'fadeIn', { ...chartAnimationPresets.gentleFadeIn, ...config }, callbacks?.onUpdate, callbacks?.onComplete), slideIn: (series, direction = 'up', config, callbacks) => ChartAnimationUtils.createEntranceAnimation(series, 'slideIn', { ...chartAnimationPresets.smoothSlideIn, direction, ...config }, callbacks?.onUpdate, callbacks?.onComplete), scaleIn: (series, config, callbacks) => ChartAnimationUtils.createEntranceAnimation(series, 'scaleIn', { ...chartAnimationPresets.bouncyScale, ...config }, callbacks?.onUpdate, callbacks?.onComplete), morph: (from, to, config, callbacks) => ChartAnimationUtils.createMorphingAnimation(from, to, { ...chartAnimationPresets.subtleMorph, ...config }, callbacks?.onUpdate, callbacks?.onComplete), transition: (transition, callbacks) => ChartAnimationUtils.createChartTransition(transition, callbacks?.onUpdate, callbacks?.onComplete) }; export { ChartAnimationUtils, animateChart, chartAnimationPresets }; //# sourceMappingURL=chartAnimations.js.map