Exemple interesante
Text cu mască, scroll reveal în CSS, buton magnetic, contor, secțiune pinned, layoutId, View Transitions.
Actualizat
Exemple care fac un site să pară „premium”
Fiecare exemplu de mai jos e scurt, refolosibil și se bazează pe ce ai învățat în Bazele animației, Motion și GSAP. Toate trebuie să respecte prefers-reduced-motion.
1. Titlu care apare cuvânt cu cuvânt
'use client'
import { motion, stagger } from 'motion/react'
export function RevealTitle({ text }: { text: string }) {
return (
<motion.h1 initial="hidden" animate="show" variants={{ show: { transition: { delayChildren: stagger(0.05) } } }}>
{text.split(' ').map((word, i) => (
<span key={i} className="inline-block overflow-hidden pr-[0.25em]">
<motion.span className="inline-block" variants={{ hidden: { y: '110%' }, show: { y: 0, transition: { ease: [0.22, 1, 0.36, 1], duration: 0.7 } } }}>
{word}
</motion.span>
</span>
))}
</motion.h1>
)
}Trucul: fiecare cuvânt e într-un span cu overflow-hidden; cuvântul urcă din afara lui → efect de „mască”.
2. Carduri care intră la scroll, doar CSS
@supports (animation-timeline: view()) {
.card {
animation: card-in linear both;
animation-timeline: view();
animation-range: entry 10% cover 30%;
}
}
@keyframes card-in {
from { opacity: 0; transform: translateY(40px) scale(0.96); }
}Zero JavaScript — animația e legată direct de poziția elementului în ecran. @supports lasă browserele vechi fără efect, dar cu conținut vizibil.
3. Buton „magnetic”
Butonul se apropie ușor de cursor:
'use client'
import { motion, useSpring } from 'motion/react'
export function MagneticButton({ children }: { children: React.ReactNode }) {
const x = useSpring(0, { stiffness: 200, damping: 15 })
const y = useSpring(0, { stiffness: 200, damping: 15 })
return (
<motion.button
style={{ x, y }}
onPointerMove={e => {
const r = e.currentTarget.getBoundingClientRect()
x.set((e.clientX - (r.left + r.width / 2)) * 0.3) // 30% din distanța față de centru
y.set((e.clientY - (r.top + r.height / 2)) * 0.3)
}}
onPointerLeave={() => (x.set(0), y.set(0))}
>
{children}
</motion.button>
)
}4. Contor care numără până la valoare
'use client'
import { animate, useInView, useMotionValue, useTransform, motion } from 'motion/react'
import { useEffect, useRef } from 'react'
export function Counter({ to }: { to: number }) {
const ref = useRef(null)
const inView = useInView(ref, { once: true })
const value = useMotionValue(0)
const rounded = useTransform(value, v => Math.round(v).toLocaleString('ro-RO'))
useEffect(() => {
if (inView) animate(value, to, { duration: 1.4, ease: 'easeOut' })
}, [inView, to, value])
return <motion.span ref={ref}>{rounded}</motion.span>
}5. Secțiune „pinned” cu GSAP
Imaginea stă fixă, textele se schimbă pe măsură ce dai scroll — clasicul scroll storytelling:
useGSAP(
() => {
const tl = gsap.timeline({ scrollTrigger: { trigger: '.story', start: 'top top', end: '+=2000', scrub: true, pin: true } })
tl.from('.step-1', { opacity: 0, y: 40 })
.to('.step-1', { opacity: 0, y: -40 })
.from('.step-2', { opacity: 0, y: 40 }, '<')
.to('.visual', { scale: 1.15, rotate: 3 }, 0)
},
{ scope: container },
)6. Tranziție listă → detaliu cu layoutId
// în listă
<motion.img layoutId={`photo-${p.id}`} src={p.src} />
// în modalul de detaliu
<motion.img layoutId={`photo-${p.id}`} src={p.src} className="w-full" />Imaginea „zboară” din card în modal. Combinat cu rutele interceptate, modalul are și URL propriu.
7. Tranziții între pagini cu View Transitions
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 250ms;
}
.product-image { view-transition-name: product-hero; }document.startViewTransition(() => updateTheDOM())Browserul face un „snapshot” înainte și după, apoi animă diferența — inclusiv morph între elemente cu același view-transition-name.
Checklist înainte să publici
- Rulează la 60 fps pe un telefon mediu? (DevTools → Performance, CPU throttling 4×)
- Conținutul e vizibil și fără animație (JS dezactivat, reduced motion)?
- Animațiile au un scop, nu doar decor?
- Durata totală a intro-ului e sub ~1.2 s?
Pe scurt
- Text cu mască + stagger, apariții la scroll în CSS pur, buton magnetic, contor, secțiune pinned,
layoutId, View Transitions. - Fiecare exemplu e o combinație de: stări, easing, stagger, scroll.
- Testează pe telefon și cu reduced motion.