Flux

Acht Effekte aus echten Projekten. Mit Code.

Inhalt

Inhalt

ContentsIndex.vue
// Hover any row in the index above
gsap.to(title, {
  duration: 0.7,
  overwrite: true,
  scrambleText: {
    text: title.dataset.text,
    chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZ#%&*",
    revealDelay: 0.1,
  },
});

SplitText + ScrollTrigger

Zeile für Zeile.

Jede Zeile kriegt ihre eigene Maske. Gedruckt wird mit der Scrollbar.

SplitText schaut nach, wo der Browser deine Zeilen tatsächlich umbricht, steckt jede in eine Maske und teilt neu, sobald Fonts nachladen oder sich das Fenster ändert. Jede Headline hier läuft so. Der Absatz rechts hängt am Scroll. Scroll ruhig wieder hoch und schau zu, wie er sich zurückdruckt.

Gute Bewegung funktioniert wie guter Satz. Sie hat Rhythmus, sie weiß, wo die Zeile bricht, und sie lässt dich nie auf die Pointe warten.

LinesReveal.vue
SplitText.create(paragraph, {
  type: "lines,words",
  mask: "lines",        // each line clips itself
  autoSplit: true,      // re-split on resize & font load
  onSplit(self) {
    return gsap.timeline({
      scrollTrigger: { trigger: paragraph, start: "top 80%", end: "bottom 35%", scrub: 1 },
    })
      .from(self.lines, { yPercent: 100, stagger: 0.1 })
      .fromTo(self.words, { opacity: 0.12 }, { opacity: 1, stagger: 0.02 }, 0);
  },
});

Flip

Möbel rücken.

Klasse tauschen, Faden behalten. Flip merkt sich, wo alles stand.

Du animierst nie auf ein Layout hin. Du tauschst die Klasse, und Flip rechnet aus, wie jede Kachel von ihrem alten Platz zum neuen kommt. Grid, Liste und Stapel sind drei schlichte CSS-Regeln. Die Bewegung gibt's umsonst.

ANº 01
BNº 02
CNº 03
DNº 04
ENº 05
FNº 06
FlipGrid.vue
// 1. record where everything is
state = Flip.getState(".flip__tile");
// 2. let Vue change the class
layout.value = "stack";

// 3. after the DOM updates, animate from old → new
await nextTick();
Flip.from(state, {
  duration: 0.9,
  ease: "power3.inOut",
  stagger: 0.04,
  absolute: true,
});

Draggable + InertiaPlugin

Wirf's.

Bei Oberflächen zum Anfassen zählt vor allem das Loslassen.

Draggable kümmert sich um Maus, Touch und Ränder. InertiaPlugin misst, wie schnell du loslässt, und lässt jeden Sticker ausgleiten, mit Abprall an den Kanten. Ein bisschen Schräglage unterwegs, ein kleines Nachwackeln bei der Landung, und auf einmal ist es Papier.

Packen und werfen

NEW!
Fragile, handle with care
Nº 01
Approved
★
Print me
ThrowDesk.vue
Draggable.create(".sticker", {
  bounds: desk,
  inertia: true,          // InertiaPlugin: momentum on release
  edgeResistance: 0.7,
  onDrag() {
    // tilt into the direction of travel
    gsap.to(this.target, { rotation: this.deltaX * 1.6 });
  },
  onThrowComplete() {
    gsap.to(this.target, { rotation: gsap.utils.random(-12, 12), ease: "elastic.out(1, 0.5)" });
  },
});

Raw WebGL

Tinte ohne Drucker.

Kein three.js, keine Texturen. Ein Fragment-Shader und drei Farben.

Rauschen, das in sich selbst verbogen wird, ergibt diese langsamen, tintigen Wellen. Ich drück das Ganze auf Papier, Schwarz und Rot runter und streu Korn drüber, das sich jeden Frame ändert. Dann flimmert es wie ein echter Risodruck. Deine Maus schiebt die Tinte herum. Gefällt's dir, macht die Export-Seite ein Poster draus.

Mach ein Poster draus
Beweg die Maus und schieb die Tinte herum
utils/riso.ts
// fragment shader: domain-warped fbm
vec2 q = vec2(fbm(uv * 1.6 + t), fbm(uv * 1.6 + vec2(5.2, 1.3) - t));
float pull = exp(-length(uv - mouse) * 3.5);
vec2 r = vec2(fbm(uv * 1.6 + 4.0 * q + pull), fbm(uv * 1.6 + 4.0 * q - pull));
float f = fbm(uv * 1.6 + 4.0 * r) + grain * 0.07;

// posterize into three spot inks
vec3 col = f < 0.37 ? ink : f < 0.47 ? accent : paper;

ScrollTrigger · containerAnimation

Seitwärts.

Du scrollst runter. Die Geschichte läuft nach links.

Die Section unten bleibt stehen und schiebt vier Panels durchs Bild, während du weiterscrollst. Jedes Riesenwort hat einen eigenen Trigger, gemessen an der Seitwärtsbewegung statt an der Seite. Deshalb driftet und kippt es beim Durchlaufen.

Sideways.vue
const slide = gsap.to(track, {
  x: () => -(track.scrollWidth - innerWidth),
  ease: "none",
  scrollTrigger: { trigger: section, pin: true, scrub: 1, invalidateOnRefresh: true },
});

// nested triggers ride the horizontal tween
gsap.fromTo(word, { xPercent: 20 }, {
  xPercent: -20,
  scrollTrigger: { trigger: word, containerAnimation: slide, scrub: true },
});
01 / 04

Pin

Die Section bleibt stehen, die Seite scrollt darunter weiter.

02 / 04

Scrub

Der Abspielkopf der Timeline hängt direkt an der Scrollbar.

03 / 04

Nest

Kind-Trigger messen an der Seitwärtsbewegung, nicht an der Seite.

04 / 04

Release

Pin lösen, Scroll zurückgeben, weiter zum Nächsten.

gsap.ticker · utils.wrap

Tempo lesen.

Ein Laufband mit Tacho.

Ein einziger Ticker-Callback läuft deinem Scrolltempo hinterher, lässt jede Zeile bei halber Breite umlaufen und legt die Schrift schräg, je schneller du bist. Hörst du auf, trudelt alles langsam aus. Das Band unter dem Cover der Startseite ist genau diese Komponente.

VelocityMarquee.vue
gsap.ticker.add((_t, delta) => {
  velocity += (scrollY - lastY - velocity) * 0.12;
  lastY = scrollY;

  offset += dir * (0.6 + Math.abs(velocity) * 0.5) * (delta / 16.67);
  offset = gsap.utils.wrap(-row.scrollWidth / 2, 0, offset);

  row.style.translate = `${offset}px 0`;
  setSkew(gsap.utils.clamp(-14, 14, -velocity * 0.4));
});

Variable fonts · CSS custom properties

Fett, bis du hinschaust.

Bricolage Grotesque hat eine Gewichts- und eine Breitenachse. Beide hängen am Abstand.

Jeder Buchstabe trägt zwei Custom Properties, die in font-variation-settings landen. GSAP blendet sie Buchstabe für Buchstabe über, je nachdem, wie weit deine Maus weg ist. Die Schrift wird dünn und schmal, wo du hinschaust. Der große Name auf der Startseite ist dieselbe Komponente.

Komm näher.

ProximityText.vue
const weight = gsap.utils.pipe(
  gsap.utils.clamp(0, radius),
  gsap.utils.mapRange(0, radius, 200, 800),
);

// .px-char { font-variation-settings: "wght" var(--wght), "wdth" var(--wdth) }
for (const c of chars) {
  const d = distance(pointer, center(c));
  gsap.to(c, { "--wght": weight(d), "--wdth": width(d), overwrite: "auto" });
}

Canvas 2D · lenticular

Beide Seiten.

Das Wackelbild aus der Cornflakes-Packung, nachgebaut.

Beide Motive werden unsichtbar vorgemalt und dann jeden Frame als ineinander verschränkte Sieben-Pixel-Streifen neu gezeichnet. Jeder Streifen sucht sich seine Seite aus Blickwinkel und eigener Position. Deshalb kippt das Bild in einem Wisch und nicht auf einmal. Ein Rillenmuster und ein wandernder Glanz machen den Rest.

Fahr über den Druck

Lenticular.vue
for (let x = 0; x < w; x += strip) {
  // each lens flips a little later than its neighbour → a sweep
  const local = angle + (x / w - 0.5) * 0.6;
  const t = clamp(0, 1, (local + 0.15) / 0.3);

  ctx.globalAlpha = 1;
  ctx.drawImage(sideA, x, 0, strip, h, x, 0, strip, h);
  ctx.globalAlpha = smoothstep(t);
  ctx.drawImage(sideB, x, 0, strip, h, x, 0, strip, h);
}

// ridges: a strip-wide gradient repeated as a pattern
ctx.fillStyle = ctx.createPattern(lens, "repeat");
ctx.fillRect(0, 0, w, h);

Library

Nimm es mit.

Fünf davon in einem Script mit 4 KB.

flux.js holen

Schrift

Bricolage Grotesque, Instrument Serif, JetBrains Mono

Motion

GSAP 3: SplitText, ScrambleText, Flip, Draggable, Inertia, ScrollTrigger · Canvas 2D · WebGL

Scroll

Lenis, synchron zum GSAP-Ticker

Gebaut mit

Nuxt 3 · Vue 3 · Tailwind