aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
327 lines (325 loc) • 11 kB
JavaScript
// Easing functions
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;
},
easeInCirc: t => 1 - Math.sqrt(1 - Math.pow(t, 2)),
easeOutCirc: t => Math.sqrt(1 - Math.pow(t - 1, 2)),
easeInOutCirc: t => {
return t < 0.5 ? (1 - Math.sqrt(1 - Math.pow(2 * t, 2))) / 2 : (Math.sqrt(1 - Math.pow(-2 * t + 2, 2)) + 1) / 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;
},
easeOutBounce: t => {
const n1 = 7.5625;
const d1 = 2.75;
if (t < 1 / d1) {
return n1 * t * t;
} else if (t < 2 / d1) {
return n1 * (t -= 1.5 / d1) * t + 0.75;
} else if (t < 2.5 / d1) {
return n1 * (t -= 2.25 / d1) * t + 0.9375;
} else {
return n1 * (t -= 2.625 / d1) * t + 0.984375;
}
},
easeInBounce: t => 1 - easingFunctions.easeOutBounce(1 - t),
easeInOutBounce: t => t < 0.5 ? (1 - easingFunctions.easeOutBounce(1 - 2 * t)) / 2 : (1 + easingFunctions.easeOutBounce(2 * t - 1)) / 2
};
// Animation utilities
class ChartAnimationUtils {
static createKeyframeSequence(from, to, steps = 10, easing = 'easeOutQuad') {
const keyframes = [];
const easeFn = easingFunctions[easing];
for (let i = 0; i <= steps; i++) {
const progress = i / steps;
const easedProgress = easeFn(progress);
const value = from + (to - from) * easedProgress;
keyframes.push({
progress,
value,
easing: easeFn
});
}
return keyframes;
}
static interpolateValue(from, to, progress, easing = 'linear') {
const easeFn = easingFunctions[easing];
const easedProgress = easeFn(progress);
return from + (to - from) * easedProgress;
}
static animateDataPoints(dataPoints, targetValues, progress, easing = 'easeOutQuad') {
return dataPoints.map((point, index) => {
const targetValue = targetValues[index];
if (targetValue === undefined || typeof point.y !== 'number') {
return point;
}
const animatedValue = this.interpolateValue(point.y, targetValue, progress, easing);
return {
...point,
y: animatedValue
};
});
}
static animateSeries(series, targetSeries, progress, easing = 'easeOutQuad', staggerDelay = 0) {
return series.map((seriesItem, seriesIndex) => {
const targetSeriesItem = targetSeries[seriesIndex];
if (!targetSeriesItem) return seriesItem;
const seriesProgress = Math.max(0, Math.min(1, progress - staggerDelay * seriesIndex / 1000));
const animatedData = this.animateDataPoints(seriesItem.data, targetSeriesItem.data.map(p => typeof p.y === 'number' ? p.y : 0), seriesProgress, easing);
return {
...seriesItem,
data: animatedData
};
});
}
static createSequence(keyframes, duration, delay = 0, repeat = false, yoyo = false) {
return {
keyframes,
duration,
delay,
repeat,
yoyo
};
}
static getValueAtProgress(sequence, progress) {
if (sequence.keyframes.length === 0) return 0;
if (sequence.keyframes.length === 1) return sequence.keyframes[0].value;
// Find the appropriate keyframe pair
for (let i = 0; i < sequence.keyframes.length - 1; i++) {
const current = sequence.keyframes[i];
const next = sequence.keyframes[i + 1];
if (progress >= current.progress && progress <= next.progress) {
const localProgress = (progress - current.progress) / (next.progress - current.progress);
const easedProgress = next.easing ? next.easing(localProgress) : localProgress;
return current.value + (next.value - current.value) * easedProgress;
}
}
// Return last keyframe value if progress is beyond the sequence
return sequence.keyframes[sequence.keyframes.length - 1].value;
}
static createSpringAnimation(from, to, config = {}) {
const {
stiffness = 100,
damping = 10,
mass = 1
} = config;
// Generate keyframes based on spring physics
const keyframes = [];
const steps = 60; // 60fps for smooth animation
const dt = 1 / steps;
let position = from;
let velocity = 0;
let time = 0;
const maxTime = 2; // Maximum animation time in seconds
while (time < maxTime) {
const displacement = to - position;
const springForce = displacement * stiffness;
const dampingForce = -velocity * damping;
const acceleration = (springForce + dampingForce) / mass;
velocity += acceleration * dt;
position += velocity * dt;
const progress = Math.min(time / maxTime, 1);
keyframes.push({
progress,
value: position,
easing: easingFunctions.easeOutQuad
});
// Stop if we're close enough to target and velocity is low
if (Math.abs(displacement) < 0.01 && Math.abs(velocity) < 0.01) {
break;
}
time += dt;
}
// Ensure we end at the target value
keyframes.push({
progress: 1,
value: to,
easing: easingFunctions.easeOutQuad
});
return {
keyframes,
duration: time * 1000,
delay: 0,
repeat: false,
yoyo: false
};
}
static createMorphingAnimation(fromShape, toShape, config = {}) {
const {
steps = 20,
easing = 'easeInOutQuad'
} = config;
const keyframes = [];
for (let i = 0; i <= steps; i++) {
const progress = i / steps;
const easedProgress = easingFunctions[easing](progress);
keyframes.push({
progress,
value: easedProgress,
easing: easingFunctions[easing]
});
}
return {
keyframes,
duration: 1000,
delay: 0,
repeat: false,
yoyo: false
};
}
}
// Utility functions for common animation patterns
const createFadeInAnimation = (duration = 500, delay = 0) => {
return ChartAnimationUtils.createSequence(ChartAnimationUtils.createKeyframeSequence(0, 1, 10, 'easeOutQuad'), duration, delay, false, false);
};
const createSlideInAnimation = (direction = 'up', distance = 50, duration = 500, delay = 0) => {
return ChartAnimationUtils.createSequence(ChartAnimationUtils.createKeyframeSequence(0, 1, 10, 'easeOutQuad'), duration, delay, false, false);
};
const createScaleInAnimation = (from = 0, to = 1, duration = 500, delay = 0) => {
return ChartAnimationUtils.createSequence(ChartAnimationUtils.createKeyframeSequence(from, to, 10, 'easeOutBack'), duration, delay, false, false);
};
const createBounceAnimation = (amplitude = 20, duration = 1000, delay = 0) => {
const keyframes = [{
progress: 0,
value: 0,
easing: easingFunctions.easeOutQuad
}, {
progress: 0.2,
value: -amplitude * 0.6,
easing: easingFunctions.easeOutQuad
}, {
progress: 0.4,
value: amplitude * 0.4,
easing: easingFunctions.easeOutQuad
}, {
progress: 0.6,
value: -amplitude * 0.2,
easing: easingFunctions.easeOutQuad
}, {
progress: 0.8,
value: amplitude * 0.1,
easing: easingFunctions.easeOutQuad
}, {
progress: 1,
value: 0,
easing: easingFunctions.easeOutQuad
}];
return ChartAnimationUtils.createSequence(keyframes, duration, delay, false, false);
};
// Utility functions for common animation patterns
const calculateDamping = (dampingRatio, stiffness, mass) => {
return 2 * Math.sqrt(mass * stiffness) * dampingRatio;
};
const createAnimationOptions = (config = {}) => {
const {
duration = 1000,
easing = 'easeOutQuad',
delay = 0
} = config;
return {
duration,
easing: easingFunctions[easing] || easingFunctions.easeOutQuad,
delay,
fill: 'both'
};
};
const pathAnimationPlugin = {
id: 'pathAnimation',
beforeInit: chart => {
chart.pathAnimation = {
progress: 0,
duration: 1000,
startTime: 0,
animating: false
};
},
beforeDraw: (chart, args, options) => {
chart.ctx;
const pathAnim = chart.pathAnimation;
if (!pathAnim.animating) return;
const elapsed = Date.now() - pathAnim.startTime;
const progress = Math.min(elapsed / pathAnim.duration, 1);
// Update progress
pathAnim.progress = progress;
// Apply path animation to line datasets
chart.data.datasets.forEach((dataset, datasetIndex) => {
const meta = chart.getDatasetMeta(datasetIndex);
if (meta.type === 'line' && meta.data) {
meta.data.forEach((element, index) => {
if (index / meta.data.length <= progress) {
element._view = element._model;
} else {
// Hide elements that haven't been drawn yet
element._view = {
...element._model,
skip: true
};
}
});
}
});
// Complete animation when done
if (progress >= 1) {
pathAnim.animating = false;
chart.update('none');
}
},
// Utility method to start path animation
startAnimation: (chart, duration = 1000) => {
if (chart.pathAnimation) {
chart.pathAnimation.startTime = Date.now();
chart.pathAnimation.duration = duration;
chart.pathAnimation.animating = true;
chart.pathAnimation.progress = 0;
}
}
};
// Additional utility functions
const createEntranceAnimation = (series, type = 'fadeIn', config = {}, onUpdate, onComplete) => {
const {
duration = 1000,
delay = 0,
stagger = 100
} = config;
return {
duration,
delay,
stagger,
easing: 'easeOutCubic',
onUpdate,
onComplete
};
};
export { ChartAnimationUtils, calculateDamping, createAnimationOptions, createBounceAnimation, createEntranceAnimation, createFadeInAnimation, createScaleInAnimation, createSlideInAnimation, easingFunctions, pathAnimationPlugin };
//# sourceMappingURL=ChartAnimationUtils.js.map