GSAP Animations
animation.ts
ts
type AnimationFunction = (
timeline: gsap.core.Timeline,
element: HTMLElement | null,
options?: AnimationOptions
) => gsap.core.Timeline;
type AnimationOptions = {
duration?: number;
ease?: string;
position?: number | string;
y0?: string;
scale0?: number;
lineHeight?: number;
};
const DEFAULT_DURATION = 0.75;
const DEFAULT_POSITION = "+=0";
const DEFAULT_EASE_FADE = "circ.inOut";
const DEFAULT_EASE_SLIDE = "power1.out";
const DEFAULT_EASE_ZOOM = "power3.inOut";
export const fadeIn: AnimationFunction = (timeline, element, options = {}) => {
const {
duration = DEFAULT_DURATION,
ease = DEFAULT_EASE_FADE,
position = "<50%",
} = options;
if (element) {
timeline.fromTo(
element,
{ opacity: 0 },
{ opacity: 1, duration, ease },
position
);
}
return timeline;
};
export const fadeOut: AnimationFunction = (timeline, element, options = {}) => {
const {
duration = DEFAULT_DURATION,
ease = DEFAULT_EASE_FADE,
position = "<+50%",
} = options;
if (element) {
timeline.to(element, { opacity: 0, duration, ease }, position);
}
return timeline;
};
export const slideUp: AnimationFunction = (timeline, element, options = {}) => {
const {
duration = DEFAULT_DURATION,
ease = DEFAULT_EASE_SLIDE,
y0 = "50%",
position = "<50%",
lineHeight = null,
} = options;
if (element) {
if (lineHeight) {
timeline.fromTo(
element,
{ y: y0, opacity: 0, lineHeight: `${lineHeight * 1.5}px` },
{ y: "0%", opacity: 1, lineHeight: `${lineHeight}px`, duration, ease },
position
);
} else {
timeline.fromTo(
element,
{ y: y0, opacity: 0 },
{ y: "0%", opacity: 1, duration, ease },
position
);
}
}
return timeline;
};
export const slideDown: AnimationFunction = (
timeline,
element,
options = {}
) => {
const {
duration = DEFAULT_DURATION,
ease = DEFAULT_EASE_SLIDE,
y0 = "100%",
position = "<+50%",
} = options;
if (element) {
timeline.to(element, { y: y0, opacity: 0, duration, ease }, position);
}
return timeline;
};
export const zoomIn: AnimationFunction = (timeline, element, options = {}) => {
const {
duration = DEFAULT_DURATION,
ease = DEFAULT_EASE_ZOOM,
position = DEFAULT_POSITION,
scale0 = 0,
} = options;
if (element) {
timeline.fromTo(
element,
{ scale: scale0, opacity: 0 },
{ scale: 1, opacity: 1, duration, ease },
position
);
}
return timeline;
};
export const zoomOut: AnimationFunction = (timeline, element, options = {}) => {
const {
duration = DEFAULT_DURATION,
ease = DEFAULT_EASE_ZOOM,
position = DEFAULT_POSITION,
} = options;
if (element) {
timeline.to(element, { scale: 0, opacity: 0, duration, ease }, position);
}
return timeline;
};
export const zoomInWobble: AnimationFunction = (
timeline,
element,
options = {}
) => {
return zoomIn(timeline, element, {
...options,
duration: 1.5,
ease: "elastic.out",
});
};
export const zoomOutWobble: AnimationFunction = (
timeline,
element,
options = {}
) => {
return zoomOut(timeline, element, {
...options,
duration: 1.5,
ease: "elastic.out",
});
};
type FadeSwapFunction = (
timeline: gsap.core.Timeline,
element1: HTMLElement | null,
element2: HTMLElement | null,
display?: string,
position?: number | string
) => gsap.core.Timeline;
export const fadeSwap: FadeSwapFunction = (
timeline,
element1,
element2,
display = "flex",
position = DEFAULT_POSITION
) => {
if (element1 && element2) {
timeline.to(element1, {
opacity: 0,
duration: DEFAULT_DURATION,
onComplete: () => {
element1.style.display = "none";
element2.style.display = display;
},
position,
});
}
return timeline;
};
type PlayVideoFunction = (
timeline: gsap.core.Timeline,
element: React.RefObject<HTMLVideoElement> | null,
playRange?: [number, number],
options?: AnimationOptions
) => gsap.core.Timeline;
export const playVideo: PlayVideoFunction = (
timeline,
element,
playRange = [0, 10],
options = {}
) => {
const {
duration = playRange[1] - playRange[0],
position = DEFAULT_POSITION,
} = options;
const video = element?.current;
if (video) {
timeline.to(
video,
{
duration,
onStart: () => {
video.currentTime = playRange[0];
video.play().catch(() => {
console.warn("Failed to play video");
});
},
onComplete: () => video.pause(),
},
position
);
}
return timeline;
};
Implementation Example
tsx
import React from "react";
import {
slideUp,
playVideo,
zoomIn,
fadeIn,
fadeOut,
} from ".../animation.ts";
const AnimationExample: React.FC = () => {
const animateIn = () => {
const tl = gsap.timeline({ paused: true });
slideUp(tl, refs.background);
playVideo(tl, video, [0.5, 5]);
zoomIn(tl, refs.hero, { duration: 1, position: ">-=1" });
slideUp(tl, refs.content);
fadeIn(tl, refs.button, { position: 2 });
return tl;
};
const animateOut = () => {
const tl = gsap.timeline({ paused: true });
slideDown(tl, refs.background);
return tl;
};
useEffect(() => {
animateIn().play();
}, []);
return (
<>
...
<button
ref={setRef('button')}
onClick={() => animateOut().play()}
>
Animate Out
</button>
</>
);
};