@rxxuzi/gumi
Version:
Clean & minimal design system with delightful interactions
243 lines • 6.4 kB
JavaScript
// core/animation.ts
// Gumi.js v1.0.0 - Animation Utilities
import { $ } from './dom';
/**
* Animate element using Web Animations API
*/
export function animate(element, keyframes, options = {}) {
const el = $(element);
if (!el)
return Promise.resolve();
const defaultOptions = {
duration: options.duration || 300,
easing: options.easing || 'cubic-bezier(0.4, 0, 0.2, 1)',
fill: options.fill || 'forwards',
delay: options.delay || 0,
iterations: options.iterations || 1,
direction: options.direction || 'normal'
};
const animation = el.animate(keyframes, defaultOptions);
return new Promise((resolve) => {
animation.onfinish = () => resolve();
animation.oncancel = () => resolve();
});
}
/**
* Fade in animation
*/
export function fadeIn(element, options = {}) {
const el = $(element);
if (!el)
return Promise.resolve();
el.style.opacity = '0';
el.style.visibility = 'visible';
return animate(el, [
{ opacity: 0 },
{ opacity: 1 }
], {
duration: options.duration || 600,
...options
});
}
/**
* Fade out animation
*/
export function fadeOut(element, options = {}) {
const el = $(element);
if (!el)
return Promise.resolve();
return animate(el, [
{ opacity: 1 },
{ opacity: 0 }
], {
duration: options.duration || 300,
...options
}).then(() => {
if (options.fill !== 'none') {
el.style.visibility = 'hidden';
}
});
}
/**
* Slide up animation (hide)
*/
export function slideUp(element, options = {}) {
const el = $(element);
if (!el)
return Promise.resolve();
const height = el.offsetHeight;
el.style.overflow = 'hidden';
return animate(el, [
{ height: `${height}px`, opacity: 1 },
{ height: '0px', opacity: 0 }
], {
duration: options.duration || 300,
...options
}).then(() => {
el.style.display = 'none';
el.style.height = '';
el.style.overflow = '';
});
}
/**
* Slide down animation (show)
*/
export function slideDown(element, options = {}) {
const el = $(element);
if (!el)
return Promise.resolve();
// Get the height
el.style.display = 'block';
el.style.height = 'auto';
const height = el.offsetHeight;
el.style.height = '0px';
el.style.overflow = 'hidden';
return animate(el, [
{ height: '0px', opacity: 0 },
{ height: `${height}px`, opacity: 1 }
], {
duration: options.duration || 300,
...options
}).then(() => {
el.style.height = '';
el.style.overflow = '';
});
}
/**
* Scale in animation
*/
export function scaleIn(element, options = {}) {
const el = $(element);
if (!el)
return Promise.resolve();
el.style.visibility = 'visible';
return animate(el, [
{ transform: 'scale(0.9)', opacity: 0 },
{ transform: 'scale(1)', opacity: 1 }
], {
duration: options.duration || 300,
...options
});
}
/**
* Scale out animation
*/
export function scaleOut(element, options = {}) {
const el = $(element);
if (!el)
return Promise.resolve();
return animate(el, [
{ transform: 'scale(1)', opacity: 1 },
{ transform: 'scale(0.9)', opacity: 0 }
], {
duration: options.duration || 300,
...options
});
}
/**
* Slide in from direction
*/
export function slideIn(element, direction = 'left', options = {}) {
const el = $(element);
if (!el)
return Promise.resolve();
const transforms = {
left: 'translateX(-100%)',
right: 'translateX(100%)',
top: 'translateY(-100%)',
bottom: 'translateY(100%)'
};
el.style.visibility = 'visible';
return animate(el, [
{ transform: transforms[direction], opacity: 0 },
{ transform: 'translate(0)', opacity: 1 }
], {
duration: options.duration || 300,
...options
});
}
/**
* Bounce animation
*/
export function bounce(element, options = {}) {
const el = $(element);
if (!el)
return Promise.resolve();
return animate(el, [
{ transform: 'translateY(0)' },
{ transform: 'translateY(-20px)' },
{ transform: 'translateY(0)' }
], {
duration: options.duration || 1000,
...options
});
}
/**
* Shake animation
*/
export function shake(element, options = {}) {
const el = $(element);
if (!el)
return Promise.resolve();
return animate(el, [
{ transform: 'translateX(0)' },
{ transform: 'translateX(-10px)' },
{ transform: 'translateX(10px)' },
{ transform: 'translateX(-10px)' },
{ transform: 'translateX(10px)' },
{ transform: 'translateX(0)' }
], {
duration: options.duration || 500,
...options
});
}
/**
* Pulse animation
*/
export function pulse(element, options = {}) {
const el = $(element);
if (!el)
return Promise.resolve();
return animate(el, [
{ transform: 'scale(1)', opacity: 1 },
{ transform: 'scale(1.05)', opacity: 0.8 },
{ transform: 'scale(1)', opacity: 1 }
], {
duration: options.duration || 2000,
...options
});
}
/**
* Ripple effect
*/
export function ripple(event, element) {
const el = element ? $(element) : event.currentTarget;
if (!el)
return;
const ripple = document.createElement('span');
const rect = el.getBoundingClientRect();
const size = Math.max(rect.width, rect.height);
const x = event.clientX - rect.left - size / 2;
const y = event.clientY - rect.top - size / 2;
ripple.style.cssText = `
position: absolute;
width: ${size}px;
height: ${size}px;
background: rgba(255, 255, 255, 0.5);
border-radius: 50%;
top: ${y}px;
left: ${x}px;
pointer-events: none;
transform: scale(0);
`;
el.style.position = 'relative';
el.style.overflow = 'hidden';
el.appendChild(ripple);
animate(ripple, [
{ transform: 'scale(0)', opacity: 1 },
{ transform: 'scale(4)', opacity: 0 }
], { duration: 600 }).then(() => {
ripple.remove();
});
}
//# sourceMappingURL=animation.js.map