⏱️ Leestijd: ca. 7 min.

CSS Animaties & Transform

Waar je met transities enkel een vloeiende overgang maakt tussen twee toestanden (zoals een :hover), laten CSS Transforms en CSS Animaties je de wetten van de fysica op het web herschrijven.

Met transform kun je elementen vervormen, verplaatsen, vergroten en zelfs in 3D-ruimte laten ronddraaien. In combinatie met @keyframes en de animation-eigenschappen kun je ingewikkelde, meerstaps animaties bouwen die automatisch afspelen, oneindig herhalen of reageren op gebruikersinteractie.

In deze geavanceerde les leer je hoe je statische ontwerpen omzet in dynamische, bewegende ervaringen!


1. 2D Transformations (transform)

De eigenschap transform past de visuele weergave van een HTML-element aan zonder de normale lay-out van de pagina (de HTML-stroom) te verstoren.

De vier belangrijkste 2D-functies zijn:

.box {
    /* 1. Verplaatsen (X-as, Y-as) */
    transform: translate(20px, -10px);

    /* 2. Schalen (vergroten/verkleinen) */
    transform: scale(1.2); /* 120% van de originele grootte */

    /* 3. Roteren (draaien in graden) */
    transform: rotate(45deg);

    /* 4. Schuintrekken (vervormen) */
    transform: skew(10deg, 5deg);
}

A. Verplaatsen: translate(x, y)

Met translate() schuif je een element over de horizontale ($X$) en verticale ($Y$) as.


B. Schalen: scale(x, y)

Met scale() vergroot of verklein je een element.


C. Roteren: rotate(deg)

Draait een element met de klok mee (positieve waarde) of tegen de klok in (negatieve waarde).


D. Schuintrekken: skew(x-deg, y-deg)

Vervormt een element door de zijkanten schuin te trekken, wat een dynamisch 'snelheidseffect' of een parallellogram kan creëren.

.actie-banner {
    /* Trekt het element 15 graden schuin over de X-as */
    transform: skewX(-15deg);
}

E. Transform Origin: Het scharnierpunt aanpassen

Standaard vindt elke transformatie plaats vanuit het exacte middelpunt van het element (transform-origin: center center; of 50% 50%). Je kunt dit scharnierpunt verplaatsen!

.deur {
    /* Draait het element vanuit de linker bovenhoek, als een scharnier */
    transform-origin: top left;
    transform: rotate(30deg);
}

2. 3D Transformations & Perspectief

Wil je diepte creëren op een plat scherm? Dan stappen we over naar 3D Transforms. In 3D werken we niet alleen met de $X$-as (breedte) en $Y$-as (hoogte), maar ook met de $Z$-as (diepte, naar de bezoeker toe of van de bezoeker af).

       Y (Verticale as)
       │
       │   Z (Diepte - naar je toe)
       │  /
       │ /
───────┼─────── X (Horizontale as)
      /│
     / │

Diepte toevoegen met perspective

Om 3D-effecten echt te laten overkomen, moet de ouder-container weten hoe ver de virtuele 'camera' van het scherm staat. Dit stellen we in met perspective.

/* Het hoofdelement dient als de '3D-ruimte' */
.card-container {
    perspective: 1000px; /* Kleinere waarde = heftiger 3D-effect; grotere waarde = subtieler */
}

/* Het kind-element roteert in 3D */
.card-3d {
    transform: rotateY(45deg) rotateX(10px);
    transition: transform 0.5s ease;
}

.card-container:hover .card-3d {
    transform: rotateY(180deg); /* Kaart omdraaien! */
}
💡 Praktijktip: backface-visibility
Wanneer je een element 180 graden draait in 3D, zie je normaal gesproken de achterkant in spiegelbeeld. Wil je dat de achterkant onzichtbaar wordt bij het omdraaien (bijvoorbeeld bij een memorykaart-spel)?
Gebruik dan: backface-visibility: hidden;.

3. Keyframe Animaties (@keyframes)

Met transition animeer je van status A naar status B. Met Keyframes kun je een animatie opbouwen uit tientallen tussenstappen!

Je definieert de animatie eerst met een naam via @keyframes. Daarna koppel je die naam aan een HTML-element.

Syntaxis 1: Eenvoudig (from / to)

@keyframes binnen-zweven {
    from {
        opacity: 0;
        transform: translateY(50px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

Syntaxis 2: Geavanceerd met Procenten (%)

Met procenten geef je exact aan wat er moet gebeuren op $0\%$ (start), $50\%$ (halverwege) en $100\%$ (einde) van de totale tijdsduur.

@keyframes stuitermagie {
    0% {
        transform: translateY(0) scale(1);
    }
    30% {
        transform: translateY(-30px) scale(1.1);
    }
    50% {
        transform: translateY(0) scale(0.95); /* Vlakt licht af bij impact */
    }
    100% {
        transform: translateY(-100px) scale(1);
        opacity: 0;
    }
}

4. De animation Eigenschappen

Zodra een @keyframes animatie is aangemaakt, koppel je deze aan een element via de animation-eigenschappen.

De losse eigenschappen:

.loader {
    /* 1. Welke keyframe animatie gebruiken we? */
    animation-name: draaiSpinner;

    /* 2. Hoe lang duurt één cyclus? */
    animation-duration: 1.5s;

    /* 3. Snelheidsverloop (linear, ease, ease-in-out, etc.) */
    animation-timing-function: linear;

    /* 4. Hoe vaak herhalen? (bijv. 3, of infinite) */
    animation-iteration-count: infinite;

    /* 5. Richting (normal, reverse, alternate, alternate-reverse) */
    animation-direction: alternate;

    /* 6. Wat gebeurt er VOOR of NA de animatie? */
    animation-fill-mode: forwards;

    /* 7. Vertraging voor de start */
    animation-delay: 0.5s;
}

Wat doet animation-fill-mode?

Dit is een van de meest gemaakte fouten bij beginners! Zodra een animatie stopt, springt het element normaal direct terug naar zijn oorspronkelijke CSS-staat.


De Verkorte Eigenschap (Shorthand Syntax)

Net als bij transities gebruiken we meestal de shorthand:

/* Shorthand: [name] [duration] [timing-function] [delay] [iteration-count] [direction] [fill-mode] */
.puls-knop {
    animation: pulseren 2s ease-in-out infinite alternate;
}

5. Performance & GPU Acceleratie

Animaties moeten supersoepel draaien ($60\text{ fps}$ ofwel frames per seconde). Als een animatie hapert, schrikt dat bezoekers af.

Gouden Regel voor Animaties:
Aanbranden van de processor voorkomen? Animeer uitsluitend de eigenschappen transform en opacity.
De browser kan deze eigenschappen rechtstreeks verwerken via de videokaart (GPU-acceleratie), waardoor ze vloeiend lopen zonder de lay-out te herberekenen!

6. Compleet Praktijkvoorbeeld: 3D Flip-Card & Laad-Animatie

In dit praktijkvoorbeeld bouwen we twee veelgebruikte elementen: een 3D Flip-Card die omdraait bij hover, en een Pulse Loader die oneindig geanimeerd wordt.

HTML (index.html)

<!DOCTYPE html>
<html lang="nl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3D Transforms & Animaties</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>

    <section class="demo-container">
        <!-- 1. 3D FLIP CARD -->
        <div class="card-scene">
            <div class="card">
                <div class="card__face card__face--front">
                    <h3>Pas Muis Over Mij!</h3>
                    <p>3D Rotate Showcase</p>
                </div>
                <div class="card__face card__face--back">
                    <h3>Achterkant!</h3>
                    <p>Gebouwd met CSS 3D Transforms</p>
                </div>
            </div>
        </div>

        <!-- 2. INFINITE PULSE LOADER -->
        <div class="loader-container">
            <div class="pulse-ring"></div>
            <div class="pulse-core"></div>
        </div>
    </section>

</body>
</html>

CSS (style.css)

/* Reset & Algemene Lay-out */
*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

body {
    font-family: 'Inter', system-ui, sans-serif;
    background-color: #0f172a;
    color: #ffffff;
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
}

.demo-container {
    display: flex;
    flex-wrap: wrap;
    gap: 60px;
    align-items: center;
    justify-content: center;
}

/* ====================================================== */
/* 1. 3D FLIP CARD STYLING                                */
/* ====================================================== */

/* De '3D Ruimte' (Perspective instellen) */
.card-scene {
    width: 260px;
    height: 340px;
    perspective: 1000px; /* Creëert diepte */
}

.card {
    width: 100%;
    height: 100%;
    position: relative;
    transform-style: preserve-3d; /* Zorgt dat kinderen hun 3D-positie behouden */
    transition: transform 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    cursor: pointer;
}

/* Bij hover op de scèneruimte draait de kaart 180 graden */
.card-scene:hover .card {
    transform: rotateY(180deg);
}

.card__face {
    position: absolute;
    width: 100%;
    height: 100%;
    border-radius: 16px;
    padding: 24px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    text-align: center;
    /* Verberg de achterkant wanneer hij weggedraaid is! */
    backface-visibility: hidden;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}

/* Voorkant styling */
.card__face--front {
    background: linear-gradient(135deg, #2563eb, #1d4ed8);
    color: #ffffff;
}

/* Achterkant styling */
.card__face--back {
    background: linear-gradient(135deg, #059669, #047857);
    color: #ffffff;
    /* De achterkant staat van zichzelf al 180 graden gedraaid */
    transform: rotateY(180deg);
}

/* ====================================================== */
/* 2. INFINITE PULSE LOADER (@keyframes)                  */
/* ====================================================== */

.loader-container {
    position: relative;
    width: 80px;
    height: 80px;
    display: flex;
    justify-content: center;
    align-items: center;
}

.pulse-core {
    width: 24px;
    height: 24px;
    background-color: #38bdf8;
    border-radius: 50%;
}

.pulse-ring {
    position: absolute;
    width: 100%;
    height: 100%;
    border: 4px solid #38bdf8;
    border-radius: 50%;
    
    /* Koppel de geavanceerde keyframe animatie */
    animation: pulseren 1.6s ease-out infinite;
}

/* DEFINEER DE KEYFRAMES */
@keyframes pulseren {
    0% {
        transform: scale(0.3);
        opacity: 1;
    }
    80%, 100% {
        transform: scale(1.5);
        opacity: 0; /* Vervliegt geleidelijk */
    }
}

Samenvatting