Bildfragmentierungseffekt mit CSS-Masken und benutzerdefinierten Eigenschaften

Avatar of Temani Afif
Temani Afif am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Geoff teilte diese Idee eines Schachbrettmusters, bei dem die Kacheln verschwinden, um ein Bild freizulegen. Darin hat ein Element ein Hintergrundbild, dann hält ein CSS-Grid-Layout die „Kacheln“, die von einer gefüllten Hintergrundfarbe zu transparent wechseln und das Bild freilegen. Eine leichte Berührung von SCSS versetzt die Animation in einen Zeitversatz.

Ich habe eine ähnliche Idee, aber mit einem anderen Ansatz. Anstatt das Bild freizulegen, beginnen wir damit, dass es vollständig freigelegt ist, und lassen es dann eine Kachel nach der anderen verschwinden, als ob es in winzigen Fragmenten davonschwebt.

Hier ist eine funktionierende Demo des Ergebnisses. Keine JavaScript-Handhabung, keine SVG-Tricks. Nur ein einzelnes <img> und etwas SCSS-Magie.

Cool, oder? Sicher, aber hier ist der Haken. Sie müssen dies in Chrome, Edge oder Opera anzeigen, da dies derzeit die einzigen Browser sind, die @property unterstützen, und dies eine Schlüsselkomponente dieser Idee ist. Wir werden uns davon nicht aufhalten lassen, denn dies ist eine großartige Gelegenheit, uns mit coolen CSS-Funktionen wie Masken und dem Animieren von linearen Verläufen mit Hilfe von @property vertraut zu machen.

Maskieren von Dingen

Maskierung ist manchmal schwer zu konzeptualisieren und wird oft mit Clipping verwechselt. Die Quintessenz: Masken sind Bilder. Wenn ein Bild als Maske auf ein Element angewendet wird, lassen uns transparente Teile des Bildes direkt durch das Element sehen. Jeder opake Teil lässt das Element vollständig sichtbar erscheinen.

Masken funktionieren genauso wie Opazität, aber auf unterschiedlichen Teilen desselben Elements. Das unterscheidet sich von Clipping, das ein Pfad ist, bei dem alles außerhalb des Pfads einfach ausgeblendet wird. Der Vorteil von Masken ist, dass wir beliebig viele Maskenebenen auf demselben Element haben können – ähnlich wie wir mehrere Bilder über background-image verketten können.

Und da Masken Bilder sind, können wir CSS-Verläufe verwenden, um sie zu erstellen. Nehmen wir ein einfaches Beispiel, um den Trick besser zu verstehen.

img {
  mask:
    linear-gradient(rgba(0,0,0,0.8) 0 0) left,  /* 1 */
    linear-gradient(rgba(0,0,0,0.5) 0 0) right; /* 2 */
  mask-size: 50% 100%;
  mask-repeat: no-repeat;
}

Hier definieren wir zwei Maskenebenen für ein Bild. Sie sind beide eine Volltonfarbe, aber die Alpha-Transparenzwerte sind unterschiedlich. Die obige Syntax mag seltsam aussehen, ist aber eine vereinfachte Schreibweise für linear-gradient(rgba(0,0,0,0.8), rgba(0,0,0,0.8)).

Es ist erwähnenswert, dass die verwendete Farbe irrelevant ist, da der Standardwert für mask-mode alpha ist. Der Alpha-Wert ist das einzig relevante Ding. Unser Verlauf kann linear-gradient(rgba(X,Y,Z,0.8) 0 0) sein, wobei X, Y und Z zufällige Werte sind.

Jede Maskenebene entspricht 50% 100% (oder halbe Breite und volle Höhe des Bildes). Eine Maske bedeckt die linke Seite und die andere die rechte Seite. Am Ende haben wir zwei **nicht überlappende** Masken, die die gesamte Fläche des Bildes abdecken, und wie wir zuvor besprochen haben, hat jede von ihnen einen unterschiedlich definierten Alpha-Transparenzwert.

Wir betrachten zwei Maskenebenen, die mit zwei linearen Verläufen erstellt wurden. Der erste Verlauf, links, hat einen Alpha-Wert von 0.8. Der zweite Verlauf, rechts, hat einen Alpha-Wert von 0.5. Der erste Verlauf ist opaker, was bedeutet, dass mehr vom Bild durchscheint. Der zweite Verlauf ist transparenter, was bedeutet, dass mehr vom Hintergrund durchscheint.

Animieren von linearen Verläufen

Was wir tun wollen, ist, eine Animation auf die linearen Verlaufs-Alpha-Werte unserer Maske anzuwenden, um eine Transparenzanimation zu erzeugen. Später werden wir daraus asynchrone Animationen machen, die den Fragmentierungseffekt erzeugen.

Das Animieren von Verläufen ist etwas, das wir bisher in CSS nicht konnten. Das heißt, bis wir eine eingeschränkte Unterstützung für @property erhielten. Jhey Tompkins hat sich eingehend mit den fantastischen Animationskräften von @property befasst und gezeigt, wie es zum Übergang von Verläufen verwendet werden kann. Auch hier sollten Sie dies in Chrome oder einem anderen Blink-basierten Browser anzeigen.

Kurz gesagt, @property ermöglicht es uns, benutzerdefinierte CSS-Eigenschaften zu erstellen, bei denen wir die Syntax definieren können, indem wir einen Typ angeben. Erstellen wir zwei Eigenschaften, --c-0 und --c-1, die eine Zahl mit einem Anfangswert von 1 aufnehmen.

@property --c-0 {
   syntax: "<number>";
   initial-value: 1;
   inherits: false;
}
@property --c-1 {
   syntax: "<number>";
   initial-value: 1;
   inherits: false;
}

Diese Eigenschaften werden die Alpha-Werte in unserer CSS-Maske darstellen. Und da beide standardmäßig vollständig opak sind (d. h. 1), scheint das gesamte Bild durch die Maske. Hier können wir die Maske mit den benutzerdefinierten Eigenschaften neu schreiben.

/* Omitting the @property blocks above for brevity */

img {
  mask:
    linear-gradient(rgba(0,0,0,var(--c-0)) 0 0) left,  /* 1 */
    linear-gradient(rgba(0,0,0,var(--c-1)) 0 0) right; /* 2 */
  mask-size: 50% 100%;
  mask-repeat: no-repeat;
  transition: --c-0 0.5s, --c-1 0.3s 0.4s;
}

img:hover {
  --c-0:0;
  --c-1:0;
}

Alles, was wir hier tun, ist, eine andere Übergangsdauer und Verzögerung für jede benutzerdefinierte Variable anzuwenden. Fahren Sie mit der Maus über das Bild. Der erste Verlauf der Maske wird zu einem Alpha-Wert von 0 ausgeblendet, um das Bild vollständig durchsichtig zu machen, gefolgt vom zweiten Verlauf.

Mehr Maskierung!

Bisher haben wir nur mit zwei linearen Verläufen auf unserer Maske und zwei benutzerdefinierten Eigenschaften gearbeitet. Um einen Kachel- oder Fragmentierungseffekt zu erzielen, benötigen wir viel mehr Kacheln, und das bedeutet viel mehr Verläufe und viele benutzerdefinierte Eigenschaften!

SCSS macht dies zu einer ziemlich trivialen Aufgabe, daher werden wir von nun an Stile schreiben. Wie wir im ersten Beispiel gesehen haben, haben wir eine Art Matrix von Kacheln. Wir können diese als Zeilen und Spalten betrachten, also definieren wir zwei SCSS-Variablen, $x und $y, um sie darzustellen.

Benutzerdefinierte Eigenschaften

Wir benötigen @property-Definitionen für jede einzelne. Niemand möchte all diese von Hand schreiben, also lassen wir SCSS die schwere Arbeit für uns erledigen, indem wir unsere Eigenschaften durch eine Schleife laufen lassen.

@for $i from 0 through ($x - 1) {
  @for $j from 0 through ($y - 1) {
    @property --c-#{$i}-#{$j} {
      syntax: "<number>";
      initial-value: 1;
      inherits: false;
    }
  }
}

Dann lassen wir sie alle beim Überfahren mit der Maus zu 0 werden.

img:hover {
  @for $i from 0 through ($x - 1) {
    @for $j from 0 through ($y - 1) {
      --c-#{$i}-#{$j}: 0;
    }
  }
}

Verläufe

Wir schreiben ein @mixin, das sie für uns generiert.

@mixin image() {
  $all_t: (); // Transition
  $all_m: (); // Mask
  @for $i from 0 through ($x - 1) {
    @for $j from 0 through ($y - 1) {
      $all_t: append($all_t, --c-#{$i}-#{$j} transition($i,$j), comma);
      $all_m: append($all_m, linear-gradient(rgba(0,0,0,var(--c-#{$i}-#{$j})) 0 0) calc(#{$i}*100%/(#{$x} - 1)) calc(#{$j}*100%/(#{$y} - 1)), comma);
    }
  }
  transition: $all_t;
  mask: $all_m;
}

Alle unsere Maskenebenen sind gleich groß, daher benötigen wir nur eine Eigenschaft dafür, die sich auf die Variablen $x und $y sowie calc() stützt.

mask-size: calc(100%/#{$x}) calc(100%/#{$y})

Sie haben vielleicht auch diese Zeile bemerkt.

$all_t: append($all_t, --c-#{$i}-#{$j} transition($i,$j), comma);

Innerhalb desselben Mixins generieren wir auch die transition-Eigenschaft, die alle zuvor definierten benutzerdefinierten Eigenschaften enthält.

Schließlich generieren wir dank der Funktion random() in SCSS eine andere Dauer/Verzögerung für jede Eigenschaft.

@function transition($i,$j) {
  @return $s*random()+s $s*random()+s;
}

Jetzt müssen wir nur noch die Variablen $x und $y anpassen, um die Granularität unserer Fragmentierung zu steuern.

Mit den Animationen spielen

Wir können auch die Zufallskonfiguration ändern, um verschiedene Arten von Animationen zu berücksichtigen.

Im obigen Code habe ich die Funktion transition() wie folgt definiert:

// Uncomment one to use it
@function transition($i,$j) {
  // @return (($s*($i+$j))/($x+$y))+s (($s*($i+$j))/($x+$y))+s; /* diagonal */
  // @return (($s*$i)/$x)+s (($s*$j)/$y)+s; /* left to right */
  // @return (($s*$j)/$y)+s (($s*$i)/$x)+s; /* top to bottom */
  // @return  ($s*random())+s (($s*$j)/$y)+s; /* top to bottom random */
  @return  ($s*random())+s (($s*$i)/$y)+s; /* left to right random */
  // @return  ($s*random())+s (($s*($i+$j))/($x+$y))+s; /* diagonal random */
  // @return ($s*random())+s ($s*random())+s; /* full random*/
}

Durch Anpassung der Formel können wir verschiedene Arten von Animationen erzielen. Kommentieren Sie einfach diejenige aus, die Sie verwenden möchten. Diese Liste ist nicht erschöpfend – wir können jede Kombination erstellen, indem wir mehr Formeln berücksichtigen. (Ich überlasse es Ihrer Vorstellungskraft, was möglich ist, wenn wir erweiterte mathematische Funktionen wie sin(), sqrt() usw. hinzufügen.)

Mit den Verläufen spielen

Wir können immer noch mit unserem Code herumspielen, indem wir den Verlauf anpassen, so dass wir anstatt des Alpha-Werts die Farb-Stops animieren. Unser Verlauf würde so aussehen:

linear-gradient(white var(--c-#{$i}-#{$j}),transparent 0)

Dann animieren wir die Variable von 100% auf 0%. Und hey, wir müssen uns nicht an lineare Verläufe halten. Warum nicht radiale?

Wie beim Übergang können wir jede Art von Verlauf definieren, die wir wollen – die Kombinationen sind unendlich!

Mit der Überlappung spielen

Führen wir eine weitere Variable ein, um die Überlappung zwischen unseren Verlaufsmasken zu steuern. Diese Variable setzt die mask-size wie folgt:

calc(#{$o}*100%/#{$x}) calc(#{$o}*100%/#{$y})

Wenn sie gleich 1 ist, gibt es keine Überlappung. Wenn sie größer ist, gibt es eine Überlappung. Dies ermöglicht es uns, noch mehr Arten von Animationen zu erstellen.

Das war's!

Alles, was wir tun müssen, ist, die perfekte Kombination aus Variablen und Formeln zu finden, um erstaunliche und verrückte Bildfragmentierungseffekte zu erzielen.