# Animatie in de praktijk

Wanneer beweging helpt en wanneer ze stoort, met de principes van Disney, CSS transition en @keyframes, timing, easing, minder beweging en een korte blik op GSAP

Multimedia & Creatieve Technologie · 2.1 Web production · Les 6 · versie 1.2.0 · 2026-10-27

Tags: css, animation, accessibility, performance

Slides: <https://slides.mctantwerp.be/nl/web-production/05-animatie-praktijk>

Speaker notes: <https://slides.mctantwerp.be/nl/web-production/05-animatie-praktijk.notes.md>

<!-- slide 2 -->

## Agenda

1. Animeren of niet
2. De principes van Disney
3. Transition en keyframes
4. Timing
5. Easing
6. Goed of slecht
7. Minder beweging
8. JavaScript

<!-- slide 3 -->

## Na deze les kan je

- In één zin zeggen waarom een animatie er staat, of waarom ze weg moet
- De principles of animation herkennen op een website
- Een CSS transition en een @keyframes-animatie schrijven
- Een duur en easing kiezen die passen bij wat er beweegt
- Je animaties afstemmen op prefers-reduced-motion
- Een eenvoudige timeline schrijven met GSAP

<!-- slide 4 -->

## 1. Animeren of niet

<!-- slide 5 -->

**Elke animatie heeft een reden nodig**

Kan je die reden niet in één zin zeggen, dan laat je de animatie weg.

<!-- slide 6 -->

### Waar animatie helpt

Feedback

Een knop Bewaar die bij een klik indrukt en groen wordt met een vinkje.

Een knop die indrukt, een vinkje na het bewaren. Je ziet dat je klik aankwam.

Oriëntatie

Een knop Filters die een paneel van rechts laat binnenschuiven. Een klik op het paneel stuurt het terug naar rechts.

Een paneel dat van rechts komt, gaat ook naar rechts weg.

Wachten

Een knop Laad die eerst grijze glanzende balken toont en daarna de inhoud.

Een skeleton terwijl je wacht. Het laden duurt even lang, maar voelt korter.

<!-- slide 7 -->

### Wanneer net niet

- Iets dat je tientallen keren per dag doet: een tab, een dropdown, een lijst
- Snelle toetsenbordacties waarbij beweging het werken vertraagt
- Een animatie waarop je moet wachten voor je verder kan
- Beweging naast tekst die iemand probeert te lezen
- Een animatie die er enkel staat omdat de library het kon

<!-- slide 8 -->

### Hoe vaak zie je het

| Hoe vaak | Animatie | Bijvoorbeeld |
| --- | --- | --- |
| Honderden keren per dag, of met het toetsenbord | Meestal geen | Cmd+K, sneltoetsen, met de pijltjes door een lijst |
| Tientallen keren per dag | Kort en klein | Hover, een dropdown, tabs |
| Af en toe | Mag meer | Een modal, een melding, onboarding |
| Eén keer | Mag opvallen | Een landingspagina, je bestelling is geplaatst |

Naar [Emil Kowalski, You don't need animations](https://emilkowal.ski/ui/you-dont-need-animations)

<!-- slide 9 -->

### Tien keer na elkaar

#### Scale en fade van 300ms

Een command palette die opent en sluit met een scale en fade van 300ms.

#### Geen animatie

Dezelfde command palette, zonder animatie.

<!-- slide 10 -->

### Animeren of niet

**1. Een tab wisselen in een dashboard dat je de hele dag open hebt.**

- Een slide van 400ms
- **Een korte fade, of niets** ✓
- Een bounce op de nieuwe tab

Je doet het tientallen keren per dag. Hou de reactie kort en test meerdere wissels na elkaar. Een bewegende indicator kan helpen om de actieve tab te volgen.

**2. De bevestiging dat je bestelling geplaatst is.**

- Niets, de tekst volstaat
- **Een vinkje dat even popt** ✓
- Confetti over het hele scherm

Je ziet het één keer, en het is feedback die geruststelt. Een vinkje mag opvallen. Confetti is te veel als je net 3 euro aan koffie uitgaf.

**3. Een command palette die je opent met Cmd+K.**

- Een scale en fade van 300ms
- Een fade van 150ms
- **Geen animatie** ✓

Met het toetsenbord, vaak tientallen keren per dag. Direct verschijnen is hier een goed vertrekpunt: je wil meteen kunnen typen.

**4. Een paneel met filters dat je af en toe opent.**

- Het verschijnt gewoon
- **Het schuift van rechts binnen en terug naar rechts weg** ✓
- Het zoomt vanuit het midden

Oriëntatie: de beweging zegt waar het paneel zit en waar het naartoe gaat als je het sluit.

**5. Een formulier met een fout na het verzenden.**

- **Een duidelijke foutmelding bij het veld** ✓
- Enkel het veld laten schudden
- Het hele formulier knippert rood

De tekst zegt wat er mis is en hoe je het oplost. Een korte beweging kan de aandacht ondersteunen, maar is niet nodig om goede feedback te geven. Test ook met minder beweging.

**6. De paragrafen van een blogartikel bij het scrollen.**

- Elke paragraaf fadet in als ze in beeld komt
- **Ze staan er gewoon** ✓
- Ze schuiven om beurten van links en rechts

Wie leest, wil de tekst meteen. Tekst die pas verschijnt als je scrolt, is beweging naast tekst die iemand probeert te lezen.

<!-- slide 11 -->

**Een roltrap die stuk is, blijft een trap**

Valt de animatie weg, door een oude browser, een instelling of een JavaScript-fout, dan moet je pagina nog werken. Content die pas zichtbaar wordt na een animatie, is weg voor wie die animatie niet krijgt.

<!-- slide 12 -->

## 2. De principes van Disney

<!-- slide 13 -->

[Video](https://www.youtube.com/watch?v=yiGY0qiy8fY)

<!-- slide 14 -->

### Twaalf principes

01Squash and stretch amperEen toggle-knop die even uitrekt

02Anticipation somsEen hover-state die de klik aankondigt

03Staging veelEen modal met een gedimde achtergrond

04Straight ahead en pose to pose somsFrame per frame of keyframes

05Follow through en overlapping veelEen stagger, een spring die doorschiet

06Slow in en slow out veelEasing

07Arc amperEen product dat naar je winkelmand vliegt

08Secondary action somsBolletjes rond een like

09Timing veelDe duur

10Exaggeration somsEen veld dat schudt bij een fout

11Solid drawing amperSchaduwen die overal uit dezelfde hoek komen

12Appeal veelDe animatie past bij de rest van je interface

<!-- slide 15 -->

### Staging

Alles rond de actie wordt rustiger, zodat het ene element dat telt de aandacht krijgt.

Op het web: een modal met een gedimde achtergrond.

Een modal verschijnt, één keer gewoon over de pagina en één keer met een gedimde, wazige achtergrond. Zonder en met naast elkaar.

<!-- slide 16 -->

### Follow through en overlapping

Delen bewegen niet allemaal tegelijk, en wat stopt, schiet nog even door.

Op het web: een stagger in een lijst, een paneel dat met een spring een tikje doorschiet.

Vier items schuiven binnen, één keer allemaal tegelijk en één keer telkens 60ms na elkaar, met een kleine overshoot op het einde. Zonder en met naast elkaar.

<!-- slide 17 -->

### Anticipation en secondary action

Anticipation kondigt een actie aan. Secondary action voegt een kleinere tweede beweging toe die de hoofdactie versterkt.

Op het web: een hover-effect voor je klikt, of bolletjes rond een like-knop.

Een hartje wordt rood, één keer enkel van kleur en één keer met een pop en kleine bolletjes errond. Zonder en met naast elkaar.

<!-- slide 18 -->

## 3. Transition en keyframes

<!-- slide 19 -->

### transform

```css
.card {
  transform: translate(0, -4px) rotate(2deg) scale(1.05);
}

/* Hetzelfde, als aparte properties */
.card {
  translate: 0 -4px;
  rotate: 2deg;
  scale: 1.05;
}
```

Vier blokjes die bij hover verschuiven, draaien, vergroten en scheeftrekken: translate, rotate, scale en skew.

- De layout rond het element blijft staan, niets anders schuift mee
- Gebruik je `translate`, zet dan ook `transition: translate` (niet `transform`)

<!-- slide 20 -->

### transition

```css
.button {
  background-color: #cdeb32;
  transition:
    background-color 150ms ease-out,
    color 150ms ease-out;
}

.button:hover {
  background-color: black;
  color: white;
}
```

Twee knoppen Inschrijven: de ene wordt bij hover zwart met een transition van 150ms, de andere springt meteen.

- Staat op het element zelf, niet in :hover, zodat ook de terugweg animeert
- Noem elke property die verandert, nooit transition: all

<!-- slide 21 -->

### @keyframes

```css
@keyframes pulse {
  0%, 100% { scale: 1; }
  50% { scale: 1.15; }
}

.badge--live {
  animation: pulse 800ms ease-in-out 3;
}
```

Een rood badge Nu live dat drie keer pulseert.

- Zoveel tussenstappen als je wil (from/to of percentages)
- Start zonder state change, zodra animation op het element staat
- Kan herhalen met een getal of infinite
- Een weggelaten from of to neemt de huidige waarde van het element

<!-- slide 22 -->

### De animation-properties

```css
.hero__title {
  animation-name: enter;
  animation-duration: 500ms;
  animation-timing-function: ease-out;
  animation-delay: 80ms;
  animation-iteration-count: 1;
  animation-direction: normal;
  animation-fill-mode: backwards;
  animation-play-state: running;

  /* of in één regel */
  animation: enter 500ms ease-out 80ms backwards;
}
```

- `backwards`: toont de eerste keyframe al tijdens de delay
- `forwards`: houdt de laatste keyframe vast na afloop
- `play-state: paused`: zet een animatie stil
- `direction: alternate`: speelt afwisselend heen en terug

<!-- slide 23 -->

### transition of animation

|   | transition | animation (@keyframes) |
| --- | --- | --- |
| Start | Als een waarde verandert (:hover, class) | Zodra de animation op het element staat |
| Stappen | Twee: van oud naar nieuw | Zoveel keyframes als je wil |
| Herhalen | Nee | `animation-iteration-count` |
| Terugweg | Vanzelf als de state terugspringt | Enkel met een tweede animatie of alternate |
| Typisch | Hover, focus, menu open en dicht | Een entree, een skeleton, een pulse |

<!-- slide 24 -->

### Iets dat verschijnt

```css
.menu {
  opacity: 0;
  transition:
    opacity 150ms ease-in,
    display 150ms allow-discrete,
    overlay 150ms allow-discrete;
}

.menu:popover-open {
  opacity: 1;
  transition-duration: 200ms;
}

@starting-style {
  .menu:popover-open { opacity: 0; }
}
```

- Een popover komt van display: none en heeft geen beginwaarde
- @starting-style geeft die beginwaarde
- allow-discrete laat ook het sluiten animeren
- De transition van de state waar je naartoe gaat, telt

<!-- slide 25 -->

## 4. Timing

<!-- slide 26 -->

### Timing

Een kleine app met een dropdown en een drawer die opengaan met een duur naar keuze, van 0 tot 1000ms. Zo voel je hoe dezelfde duur anders aanvoelt voor een kleine en een grote beweging.

<!-- slide 27 -->

### Raad de duur

Raad de duur: een element van een kleine app speelt af, de klas raadt hoe lang het duurde.

1. Een dropdown: 200ms. Juist voor een dropdown. Je ziet de beweging, maar je wacht er niet op.
2. Een drawer: 600ms. Te traag, ook voor een grote afstand. Een drawer zit tussen 300 en 400ms.
3. Een melding: 100ms. Zo kort dat het bijna springt. Een melding die binnenkomt, mag rond 250ms.
4. Een modal: 300ms. Juist voor een modal. Hij komt van ver genoeg en je ziet hem niet tientallen keren per dag.
5. Een dropdown: 1000ms. Een seconde. Na de tweede keer klik je ernaast omdat het menu er nog niet is.

<!-- slide 28 -->

### Hoe lang

| Duur | Waarvoor |
| --- | --- |
| Onder 100ms | Voelt instant. Een kleur bij hover, een knop die indrukt. |
| 150 tot 300ms | De meeste UI: een dropdown, een tooltip, een toggle. |
| 300 tot 500ms | Grote afstanden: een drawer, een modal, een paginaovergang. |
| Langer dan 1s | Voelt traag. Enkel voor iets dat je één keer ziet. |

> [!TIP]
> **Weggaan gaat sneller**
>
> Een dropdown die in 200ms opengaat, mag in 150ms dichtgaan.

<!-- slide 29 -->

### Een vraag in je FAQ klapt open in 600ms, en dicht in 600ms. Wat verander je?

- A. Niets, het is een grote beweging
- B. Allebei naar 80ms, dan voelt het instant
- C. Opengaan rond 250ms, dichtgaan nog iets sneller
- D. Opengaan sneller, dichtgaan mag zo blijven

**Antwoord:** C. Opengaan rond 250ms, dichtgaan nog iets sneller

Een FAQ open je vaak na elkaar, dus 600ms voelt als wachten. Rond 250ms zie je nog waar het antwoord vandaan komt. Dichtgaan mag sneller, want wat weggaat is niet meer belangrijk. Op 80ms zie je de beweging niet meer.

<!-- slide 30 -->

### Oefening 1: Timing

*8 minuten, Alleen*

1. Open de starter in CodePen en bewaar hem
2. Vervang elke all door de properties die echt veranderen
3. Kies een duur voor de knop, het menu, de filters en de melding
4. Test korter sluiten dan openen en leg uit of dat hier helpt
5. Toon één wijziging en leg uit waarom ze beter werkt

Een knop die een nieuwe pen op CodePen opent met de starter "Animatie 1: timing" (HTML en CSS).

<!-- slide 31 -->

## 5. Easing

<!-- slide 32 -->

### Easing

Een bal die over een baan rolt, naast een bal die lineair beweegt, met de curve erbij. Te kiezen: `linear`, `ease-in`, `ease-out`, `ease-in-out`, `cubic-bezier(0.34, 1.56, 0.64, 1)`.

<!-- slide 33 -->

### Welke easing

| Easing | Wanneer | Bijvoorbeeld |
| --- | --- | --- |
| `ease-out` | Iets komt binnen: start snel, remt af | Een dropdown, een modal, een melding |
| `ease-in` | Iets gaat weg: vertrekt traag, versnelt | Een melding die verdwijnt |
| `ease-in-out` | Iets verplaatst zich op het scherm | De indicator onder een tab |
| `linear` | Er verplaatst niets, of het loopt constant | Een kleur, een fade, een spinner |

> [!WARNING]
> **Bounce en elastic**
>
> Gebruik overshoot spaarzaam. Test of herhaling de actie vertraagt of de aandacht afleidt.

<!-- slide 34 -->

### Easing vergelijken

Twee identieke mini-apps naast elkaar met knoppen voor een tab, een menu, een melding en een spinner. Links de gebruikelijke vertrekpunten (ease-in-out, ease-out, ease-in, linear), rechts alternatieven (linear, ease-in, ease-out, ease-in-out) om te vergelijken. Elke knop speelt beide tegelijk af.

<!-- slide 35 -->

### Het bolletje van een toggle schuift van links naar rechts. Welke easing?

- A. ease-out
- B. ease-in
- C. ease-in-out
- D. linear

**Antwoord:** C. ease-in-out

Het bolletje komt niet binnen en gaat niet weg, het verplaatst zich. Dan vertrekt het rustig en komt het rustig aan: ease-in-out. ease-out is de reflex voor alles wat beweegt, linear voelt mechanisch.

<!-- slide 36 -->

### Een cookiebanner schuift weg na een klik op Akkoord. Welke easing?

- A. ease-out
- B. linear
- C. ease-in
- D. Een kleine bounce

**Antwoord:** C. ease-in

De banner gaat weg, dus ease-in: hij vertrekt rustig en versnelt het scherm uit. Met ease-out schiet hij eerst weg en blijft hij dan nog even hangen, net als niemand er nog naar kijkt.

<!-- slide 37 -->

### Oefening 2: Binnen en buiten

*8 minuten, Alleen*

1. Open de starter in CodePen en bewaar hem
2. Begin met ease-out voor binnenkomen en ease-in voor weggaan; vergelijk het resultaat
3. Haal de bounce van de filters
4. Kijk of de knop en de cards nog kloppen
5. Vergelijk één effect met linear en verantwoord je keuze

Een knop die een nieuwe pen op CodePen opent met de starter "Animatie 2: easing" (HTML en CSS).

<!-- slide 39 -->

## 6. Goed of slecht

<!-- slide 40 -->

### Wachten op de tekst

A

Een hero waarvan de vier delen na elkaar binnenkomen, elk 900ms met 700ms tussen elk deel.

B

Dezelfde hero, elk deel in 300ms met 50ms ertussen.

B. In A staat de knop er pas na bijna drie seconden. Een entree mag, zolang ze klaar is voor iemand iets wil doen: hou het hele geheel onder een halve seconde.

<!-- slide 41 -->

### Een hover die duwt

A

Drie cards die bij hover 6px omhoog komen met translate en een schaduw. De rest van de pagina blijft staan.

B

Drie cards die bij hover hun margin en padding veranderen. De tekst eronder verspringt mee.

A. B verandert margin en padding, dus verspringt alles eronder mee. Met `translate` beweegt enkel de card, en dat kost de browser veel minder.

<!-- slide 42 -->

### Een badge die blijft gaan

A

Een productnaam met een badge Nieuw dat eindeloos pulseert, naast een paragraaf tekst.

B

Dezelfde productnaam met een badge Nieuw dat drie keer pulseert en dan stilstaat.

B. Drie keer is genoeg om op te vallen. A blijft je oog trekken terwijl je de tekst ernaast probeert te lezen.

<!-- slide 43 -->

### Een modal met veer

A

Een knop Bestellen die een modal opent met een fade en een kleine scale van 200ms. Sluiten gaat in 150ms.

B

Dezelfde modal, die opent en sluit in 900ms met een grote overshoot.

A. B duurt 900ms en veert ver voorbij zijn maat. Leuk de eerste keer, de twintigste keer wacht je erop.

<!-- slide 44 -->

### Animeer transform en opacity

- translate, scale, rotate en opacity: de browser schuift enkel lagen
- width, height, top, left, margin en padding: elke frame opnieuw layout
- Kleur en schaduw: elke frame opnieuw paint, op één knop geen probleem
- Je laptop is sneller dan de telefoon van je gebruiker

<!-- slide 45 -->

### Cards komen van onder binnen als je scrolt. Wat animeer je?

- A. top van 40px naar 0
- B. margin-top van 40px naar 0
- C. padding-top van 40px naar 0
- D. translate van 0 40px naar 0 0

**Antwoord:** D. translate van 0 40px naar 0 0

Alle vier zie je hetzelfde gebeuren, maar enkel translate schuift een laag die al getekend is. Met top, margin of padding rekent de browser elke frame de layout opnieuw uit, en bij margin en padding schuift alles eronder mee.

<!-- slide 46 -->

### Oefening 3: Zes fouten

*12 minuten, Per 2*

1. Open de starter in CodePen en bewaar hem
2. Verken de zes problemen: klik, hover en lees de CSS
3. Kies de drie die de gebruiker het meest hinderen
4. Noteer per probleem het gevolg en los het op
5. Toon je belangrijkste ingreep; klaar? Pak de andere drie aan

Een knop die een nieuwe pen op CodePen opent met de starter "Animatie 3: zes fouten" (HTML en CSS).

<!-- slide 47 -->

## 7. Minder beweging

<!-- slide 48 -->

**Beweging op een scherm kan je ziek maken**

Bij een vestibulaire aandoening, een probleem met het evenwichtsorgaan, geven parallax, zoom en grote bewegingen over het scherm duizeligheid, misselijkheid of hoofdpijn. Ook bij migraine speelt beweging mee.

<!-- slide 49 -->

### Wat het meest stoort

- Parallax en scroll-jacking
- Grote bewegingen over het hele scherm: zoom, een pagina die wegdraait
- Beweging die blijft lopen: autoplay, carrousels, een achtergrondvideo
- Iets dat meer dan drie keer per seconde flitst

<!-- slide 50 -->

### prefers-reduced-motion

Een melding die binnenkomt. Zonder voorkeur schiet ze van onder in beeld met een draai en een overshoot. Met prefers-reduced-motion: reduce fadet ze enkel in, in 200ms. Eronder staat wat het systeem van de kijker echt vraagt.

<!-- slide 51 -->

### Probeer: Zet het aan

*3 minuten, Alleen*

1. Zet minder beweging aan op je laptop of je telefoon
2. Open een app of site die je elke dag gebruikt: wat verandert er?
3. Zet het ook eens aan in de Rendering-tab van Chrome DevTools

| Systeem | Instelling |
| --- | --- |
| macOS | Toegankelijkheid, Weergave, Beperk beweging |
| Windows 11 | Toegankelijkheid, Visuele effecten, Animatie-effecten |
| iOS | Toegankelijkheid, Beweging, Beperk beweging |
| Android | Toegankelijkheid, Animaties verwijderen |
| Chrome DevTools | Rendering, prefers-reduced-motion |

<!-- slide 52 -->

### Minder, niet niets

#### Override met reduce

```css
.toast {
  animation: slide-in 400ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .toast {
    animation-name: fade-in;
  }
}
```

Handig als de CSS al bestaat.

#### Opt-in met no-preference

```css
.toast {
  animation: fade-in 200ms ease-out;
}

@media (prefers-reduced-motion: no-preference) {
  .toast {
    animation: slide-in 400ms ease-out;
  }
}
```

Beweging zet je erbij. Wie de query niet kent, krijgt de rustige versie.

<!-- slide 53 -->

### Je hero heeft parallax en een titel die infadet. Wat doe je als iemand minder beweging vraagt?

- A. Allebei uit
- B. Parallax uit, de fade mag blijven
- C. De parallax trager maken
- D. Niets, het is maar de hero

**Antwoord:** B. Parallax uit, de fade mag blijven

Parallax is wat het meest stoort: voorgrond en achtergrond bewegen aan een andere snelheid. Een fade verplaatst niets, dus die mag blijven. Minder beweging is niet geen beweging. Trager maken helpt niet, het verschil in snelheid blijft.

<!-- slide 54 -->

### Een carrousel op de homepage wisselt elke 4 seconden, zolang de pagina openstaat. Wat moet er minstens bij?

- A. Een trager interval
- B. Een fade in plaats van een slide
- C. Een knop om hem te pauzeren
- D. Pijltjes om zelf te wisselen

**Antwoord:** C. Een knop om hem te pauzeren

Hij beweegt zolang de pagina openstaat, en alles wat langer dan vijf seconden beweegt moet je kunnen stoppen (WCAG 2.2.2). Trager of met een fade blijft hij bewegen. Pijltjes zijn handig, maar hij blijft verder wisselen.

<!-- slide 55 -->

### Oefening 4: Minder beweging

*10 minuten, Alleen*

1. Open de starter in CodePen en bewaar hem
2. Zet in DevTools, Rendering, prefers-reduced-motion op reduce
3. Laat de hero enkel nog faden, en de filters en de melding ook
4. Zet de pulse en de sprong van de cards uit
5. Zoek wat er nog beweegt als je op Bekijk de kaart klikt
6. Toon beide versies en leg uit welke informatie behouden blijft

Een knop die een nieuwe pen op CodePen opent met de starter "Animatie 4: minder beweging" (HTML en CSS).

Een knop die een nieuwe pen op CodePen opent met de starter "Animatie: alle oefeningen opgelost" (HTML en CSS).

<!-- slide 56 -->

## 8. JavaScript

<!-- slide 57 -->

### CSS of JavaScript

| Je wil | Neem |
| --- | --- |
| Hover, focus, open en dicht | Een CSS transition |
| Een loader of een entree | CSS @keyframes |
| Een reeks stappen na elkaar, die je wil pauzeren of herhalen | Een GSAP timeline |
| Een spring, zonder zelf een duur en easing te kiezen | Motion |

> [!TIP]
> **Eerst CSS**
>
> Een library van 50 kB voor een hover-effect is te veel.

<!-- slide 58 -->

### GSAP

```js
const mm = gsap.matchMedia()

mm.add('(prefers-reduced-motion: no-preference)', () => {
  gsap.from('.hero > *', {
    opacity: 0,
    y: 24,
    duration: 0.5,
    stagger: 0.08,
    ease: 'power2.out',
  })
})
```

Een hero waarvan de vier delen na elkaar binnenkomen met GSAP, met de code van de slide.

- from() eindigt waar je CSS het element zet
- gsap.matchMedia() voor minder beweging

<!-- slide 59 -->

### Een timeline

```js
const tl = gsap.timeline({
  defaults: { duration: 0.5, ease: 'power2.out' },
})

tl.from('.title', { opacity: 0, y: 24 })
  .from('.price', { opacity: 0, y: 24 }, '-=0.2')
  .from('.cta', { opacity: 0, scale: 0.9 }, '-=0.2')
```

- Elke stap start als de vorige klaar is
- `'-=0.2'` laat een stap iets vroeger starten
- `defaults` zet de duur en de easing één keer
- Eén play, pause of restart voor het geheel

<!-- slide 60 -->

### Motion

```js
import { animate, stagger } from 'motion'

const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches

animate(
  '.card',
  { opacity: [0, 1], y: reduce ? 0 : [60, 0] },
  { type: 'spring', bounce: 0.4, delay: stagger(0.1) },
)
```

Drie cards die met een spring na elkaar binnenkomen, met de code van de slide.

- Een spring in plaats van een duur en een easing
- Minder beweging doe je zelf, met matchMedia

<!-- slide 61 -->

### Een promo toont bij het laden een titel, dan een prijs, dan een knop. Eén keer, niets herstart of pauzeert. Wat is genoeg?

- A. Een GSAP timeline
- B. Motion met stagger()
- C. Drie @keyframes met een animation-delay
- D. De Web Animations API met await

**Antwoord:** C. Drie @keyframes met een animation-delay

Drie stappen die één keer spelen, dat kan CSS perfect, zonder library. Een GSAP timeline loont pas als je de reeks wil herstarten, pauzeren of terugspoelen, of als het tien stappen worden en je de delays niet meer zelf wil uitrekenen.

<!-- slide 62 -->

### Oefening 5: Een GSAP timeline

*8 minuten, Alleen*

1. Open de starter in CodePen en bewaar hem
2. Maak een timeline met gsap.timeline()
3. Laat de titel, de prijs en de knop na elkaar binnenkomen met .from()
4. Laat de prijs al starten voor de titel klaar is
5. Met minder beweging blijft enkel een fade

Een knop die een nieuwe pen op CodePen opent met de starter "Animatie 5: een GSAP timeline" (HTML, CSS en JavaScript).

Een knop die een nieuwe pen op CodePen opent met de starter "Animatie 5: de oplossing" (HTML, CSS en JavaScript).

<!-- slide 63 -->

### Sites die veel bewegen

- **[Ray-Ban, Monks](https://www.monks.com/case-studies/reinventing-ray-ban-together)**
- **[A designer's manifesto](https://adesignersmanifesto.webflow.io/)**
- **[Ships Ltd.](https://www.shipsltd.co.jp/pages/sp_50th_anniversary.aspx)**
- **[GTA VI](https://www.rockstargames.com/VI)**
- **[Handwrytten](https://www.handwrytten.com/)**
- **[Peppa Sauce](https://www.peppasauce.love/)**
- **[Apple Vision Pro](https://www.apple.com/apple-vision-pro/)**
- **[scroll-driven-animations.style](https://scroll-driven-animations.style/)**

<!-- slide 64 -->

### Portfolio: Vijf animaties analyseren

1. Zoek 3 goede en 2 slechte voorbeelden van het gebruik van animaties in websites
2. Documenteer in je portfolio per website (met link!) welke animaties je ziet
3. Benoem welke principles of animation je herkent, plus timing, easing en minder beweging
4. Vel een oordeel: zijn ze goed, net niet goed, of wat kan er volgens jou beter?
5. Dien een link naar je portfolio in op Canvas

Indienen op [Canvas](https://canvas.kdg.be/courses/61109/assignments). Een link naar je portfolio volstaat.

<!-- slide 65 -->

### Animaties prompten

#### Vaag

"Maak de animaties op mijn portfolio cooler."

- `transition: all` op alles
- Een bounce die blijft hangen
- Een knop die eeuwig pulseert
- Geen minder beweging

#### Gericht

"Geef de projectkaarten op mijn portfolio een hover en een :focus-visible in CSS.

De kaart schuift 4px omhoog en de schaduw wordt groter, in 200ms. Easing: emphasized decelerate uit Material 3. Animeer enkel transform en box-shadow.

Met prefers-reduced-motion verandert enkel de schaduw.

Het gevoel: rustig, zoals de kaarten op de site die ik meestuur. Leg uit welk principle of animation je gebruikt."

<!-- slide 66 -->

### Wat je meegeeft

- Het element en de trigger: hover, klik, laden, scroll
- De properties, de duur en de easing, in getallen
- Een referentie: een site, een video, een design system
- Wat er gebeurt met minder beweging
- Eerst een beschrijving, dan pas de code

- **[Material 3 motion](https://m3.material.io/styles/motion/overview)**
- **[Apple HIG, Motion](https://developer.apple.com/design/human-interface-guidelines/motion)**
- **[easings.net](https://easings.net/)**
- **[MDN, reduced motion](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion)**

## Begrippen

- **transform**: Verschuift, draait, schaalt of trekt een element scheef zonder de layout rond het element te veranderen. Kan ook met de aparte properties translate, rotate en scale. (slide 19)
- **Transition**: Een animatie van een CSS-property van een oude naar een nieuwe waarde, zodra die waarde verandert: bij :hover, :focus-visible of een nieuwe class. (slide 20)
- **@keyframes**: Een animatie met zoveel tussenstappen als je wil. Ze start zodra de animation op het element staat, zonder state change, en kan herhalen. (slide 21)
- **animation-fill-mode**: Welke waarde een element heeft voor en na zijn animatie. backwards toont de eerste keyframe al tijdens de delay, forwards houdt de laatste vast. (slide 22)
- **@starting-style**: De beginwaarde van een transition voor een element dat net verschijnt, zoals een popover die van display: none komt. (slide 24)
- **Easing**: Hoe de snelheid van een animatie verandert tussen start en einde. ease-out start snel en remt af, ease-in start traag en versnelt, linear blijft even snel. (slide 31)
- **Composite**: De laatste stap van de rendering: de geschilderde lagen op elkaar leggen. transform en opacity kunnen vaak layout en paint overslaan, daarom animeer je die. (slide 44)
- **prefers-reduced-motion**: Media feature die zegt of iemand op het systeem minder beweging vraagt. Met reduce schakel je onnodige beweging uit. Een rustige fade kan blijven als die helpt. (slide 50)
- **Vestibulaire aandoening**: Een probleem met het evenwichtsorgaan. Parallax, zoom en grote bewegingen over het scherm geven dan duizeligheid, misselijkheid of hoofdpijn. (slide 48)

## Links en bronnen

### In de presentatie

- [Emil Kowalski: You don't need animations](https://emilkowal.ski/ui/you-dont-need-animations) (slide 8): Frequentie, toetsenbord en UI onder 300ms
- [Ray-Ban, Monks](https://www.monks.com/case-studies/reinventing-ray-ban-together) (slide 63)
- [A designer's manifesto](https://adesignersmanifesto.webflow.io/) (slide 63)
- [Ships Ltd.](https://www.shipsltd.co.jp/pages/sp_50th_anniversary.aspx) (slide 63)
- [GTA VI](https://www.rockstargames.com/VI) (slide 63)
- [Handwrytten](https://www.handwrytten.com/) (slide 63)
- [Peppa Sauce](https://www.peppasauce.love/) (slide 63)
- [Apple Vision Pro](https://www.apple.com/apple-vision-pro/) (slide 63)
- [scroll-driven-animations.style](https://scroll-driven-animations.style/) (slide 63)
- [Canvas](https://canvas.kdg.be/courses/61109/assignments) (slide 64)
- [Material 3 motion](https://m3.material.io/styles/motion/overview) (slide 66)
- [Apple HIG, Motion](https://developer.apple.com/design/human-interface-guidelines/motion) (slide 66)
- [easings.net](https://easings.net/) (slide 66)
- [MDN, reduced motion](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion) (slide 66)

### Bronnen

- [MDN: Using CSS transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Transitions/Using)
- [MDN: Using CSS animations](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Animations/Using)
- [MDN: easing-function](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/easing-function)
- [MDN: @starting-style](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@starting-style)
- [MDN: prefers-reduced-motion](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/prefers-reduced-motion)
- [The 12 principles of animation according to Disney](https://academyofanimatedart.com/the-12-principles-of-animation-according-to-disney/)
- [The ultimate guide to proper use of animation in UX](https://uxdesign.cc/the-ultimate-guide-to-proper-use-of-animation-in-ux-10bd98614fa9): Duur per toestel, easing, geen bounce
- [web.dev: How to create high-performance CSS animations](https://web.dev/articles/animations-guide)
- [A11y Project: Understanding vestibular disorders](https://www.a11yproject.com/posts/understanding-vestibular-disorders/)
- [W3C: Understanding Pause, Stop, Hide](https://www.w3.org/WAI/WCAG22/Understanding/pause-stop-hide.html)
- [W3C: Understanding Animation from Interactions](https://www.w3.org/WAI/WCAG22/Understanding/animation-from-interactions.html)
- [Webflow makes GSAP 100% free](https://webflow.com/blog/gsap-becomes-free): Sinds april 2025, ook de plugins en commercieel gebruik
- [GSAP: Timeline](https://gsap.com/docs/v3/GSAP/Timeline/)
- [GSAP: gsap.matchMedia()](https://gsap.com/docs/v3/GSAP/gsap.matchMedia()/)
- [Motion: animate()](https://motion.dev/docs/animate)
