svg-curtain-preloader
Лоадер·Шаблон: Webfolio - Creative Agency & Portfolio Next.js Template·Складність анімації: heavy·Адаптивний: Так
Файли-джерела
- out/home-main/index.html
div.loader-wrap
Бібліотеки
gsap
Summary
Full-viewport intro screen with a LOADING headline split letter-by-letter. A single SVG path is morphed by GSAP from a curve-bottomed shape to a flat line, then the whole .loader-wrap translates up by -1500px and is set to display: none. Total runtime is roughly 3.5 seconds; the hero <header> slides in from y: 200 overlapping the last 1.5s of the preloader.
HTML structure (minimal)
<div class="loader-wrap">
<svg viewBox="0 0 1000 1000" preserveAspectRatio="none">
<path id="svg" d="M0,1005S175,995,500,995s500,5,500,5V0H0Z"></path>
</svg>
<div class="loader-wrap-heading">
<div class="load-text">
<span>L</span><span>o</span><span>a</span><span>d</span><span>i</span><span>n</span><span>g</span>
</div>
</div>
</div>
Key SCSS tokens
.loader-wrap {
position: fixed;
inset: 0;
z-index: 99999;
background: $main_bg;
display: flex;
align-items: center;
justify-content: center;
svg { position: absolute; inset: 0; width: 100%; height: 100%; fill: $main_bg; }
.load-text { font-size: 80px; font-weight: 600; color: #fff; }
.load-text span { display: inline-block; opacity: .15; transition: opacity .3s; }
}
Animation logic
// components/common/loader.jsx
const interval = setInterval(() => {
if (typeof gsap !== 'undefined') {
clearInterval(interval);
const svg = document.getElementById('svg');
const tl = gsap.timeline();
const curve = 'M0 502S175 272 500 272s500 230 500 230V0H0Z';
const flat = 'M0 2S175 1 500 1s500 1 500 1V0H0Z';
tl.to('.loader-wrap-heading .load-text , .loader-wrap-heading .cont',
{ delay: 1.5, y: -100, opacity: 0 });
tl.to(svg, { duration: 0.5, attr: { d: curve }, ease: 'power2.easeIn' })
.to(svg, { duration: 0.5, attr: { d: flat }, ease: 'power2.easeOut' });
tl.to('.loader-wrap', { y: -1500 });
tl.to('.loader-wrap', { zIndex: -1, display: 'none' });
tl.from('header', { y: 200 }, '-=1.5');
tl.from('header .container', { y: 40, opacity: 0, delay: 0.3 }, '-=1.5');
}
}, 100);
Notable details
- GSAP path-attr tweening (
attr: { d: ... }) animates between two SVG path strings — gives a rubbery curtain reveal you cannot achieve withclip-pathalone. - Polls every 100ms until the global
gsapscript (loadedbeforeInteractive) is onwindow, then runs once. AvoidsuseEffectrace against external Script tags. - Final timeline frames overlap with the hero entrance (
tl.from('header', { y: 200 }, '-=1.5')) so the page feels continuous.
Use when
- Cinematic agency / portfolio sites where a 3-second intro is acceptable.
- When you load GSAP globally as a
<Script beforeInteractive>and want to call it from a 'use client' component.
Caveats
- 3.5s blocking entrance is heavy — disable for users with
prefers-reduced-motion(template doesn't currently). - Once
display: none, screenshot tools at1440x900see only the page underneath; full-page screenshot still captures the intro, however, so the screenshot pipeline should wait at least 4s before capturing. - If
gsapisn't loaded globally, the polling interval runs forever and the loader never hides.