Animierte Rahmen

Avatar of Stephen Shaw
Stephen Shaw am

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

Der Übergang des border für einen Hover-Zustand. Einfach, oder? Sie werden vielleicht unangenehm überrascht sein.

Die Herausforderung

Die Herausforderung ist einfach: Erstellen Sie eine Schaltfläche mit einem sich erweiternden Rahmen beim Hovern.

Dieser Artikel konzentriert sich auf echte CSS-Tricks, die einfach in jedes Projekt integriert werden können, ohne das DOM zu berühren oder JavaScript zu verwenden. Die hier behandelten Methoden folgen diesen Regeln:

  • Einzelnes Element (keine Hilfs-Divs, aber Pseudoelemente sind erlaubt)
  • Nur CSS (kein JavaScript)
  • Funktioniert für jede Größe (nicht beschränkt auf eine bestimmte Breite, Höhe oder ein bestimmtes Seitenverhältnis)
  • Unterstützt transparente Hintergründe
  • Sanfter und performanter Übergang

Ich habe diese Herausforderung im Animation at Work Slack und erneut auf Twitter vorgeschlagen. Obwohl es keinen Konsens über den besten Ansatz gab, erhielt ich einige wirklich clevere Ideen von phänomenalen Entwicklern.

Methode 1: Animierender border

Der einfachste Weg, einen Rahmen zu animieren, ist ... nun ja, indem man den border animiert.

.border-button {
  border: solid 5px #FC5185;
  transition: border-width 0.6s linear;
}

.border-button:hover { border-width: 10px; }

Siehe den Pen von Shaw (@shshaw) auf CodePen.

Schön und einfach, aber es gibt einige große Leistungsprobleme.

Da border Platz im Layout des Dokuments einnimmt, löst die Änderung von border-width ein Layout aus. Benachbarte Elemente verschieben sich aufgrund der neuen Rahmenbreite, wodurch der Browser diese Elemente bei jeder AnimationFrame neu positioniert, es sei denn, Sie legen eine explizite Größe für den Button fest.

Als ob das Auslösen eines Layouts nicht schlimm genug wäre, fühlt sich die Animation selbst "abgehackt" an. Ich werde im nächsten Beispiel zeigen, warum.

Methode 2: Besserer border mit outline

Wie können wir den Rahmen ändern, ohne ein Layout auszulösen? Indem wir stattdessen outline verwenden! Sie sind wahrscheinlich am vertrautesten mit outline aus dem Entfernen bei :focus Stilen (obwohl Sie es nicht tun sollten), aber outline ist eine äußere Linie, die die Größe oder Position eines Elements im Layout nicht verändert.

.border-button {
  outline: solid 5px #FC5185;
  transition: outline 0.6s linear;
  margin: 0.5em; /* Increased margin since the outline expands outside the element */
}

.border-button:hover { outline-width: 10px; }

Siehe den Pen von Shaw (@shshaw) auf CodePen.

Eine schnelle Überprüfung im Performance-Tab der Entwicklertools zeigt, dass der outline-Übergang kein Layout auslöst. Ungeachtet dessen wirkt die Bewegung immer noch abgehackt, da Browser die Werte von border-width und outline-width runden, sodass Sie keine Subpixel-Darstellung zwischen 5 und 6 erhalten oder sanfte Übergänge von 5.4 zu 5.5.

Siehe den Pen von Shaw (@shshaw) auf CodePen.

Seltsamerweise rendert Safari oft den outline-Übergang nicht und hinterlässt gelegentlich verrückte Artefakte.

border artifact in safari

Methode 3: Zuschneiden mit clip-path

Zuerst implementiert von Steve Gardner, diese Methode verwendet clip-path mit calc, um den Rahmen zuzuschneiden, sodass wir bei einem Hover zum vollständigen Rahmen übergehen können.

.border-button {  
  /* Full width border and a clip-path visually cutting it down to the starting size */
  border: solid 10px #FC5185; 
  clip-path: polygon( 
    calc(0% + 5px) calc(0% + 5px), /* top left */
    calc(100% - 5px) calc(0% + 5px), /* top right */
    calc(100% - 5px) calc(100% - 5px), /* bottom right */
    calc(0% + 5px) calc(100% - 5px) /* bottom left */
  );
  transition: clip-path 0.6s linear;
}

.border-button:hover {
  /* Clip-path spanning the entire box so it's no longer hiding the full-width border. */
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}

Siehe den Pen von Shaw (@shshaw) auf CodePen.

Die clip-path-Technik ist bisher die sanfteste und performanteste Methode, birgt aber einige Vorbehalte. Rundungsfehler können zu geringfügigen Ungenauigkeiten führen, abhängig von der genauen Größe. Der Rahmen muss auch von Anfang an vollflächig sein, was die exakte Positionierung schwierig machen kann.

Leider gibt es noch keine Unterstützung in IE/Edge, obwohl sie sich in Entwicklung zu befinden scheint. Sie können und sollten das Microsoft-Team ermutigen, diese Funktionen zu implementieren, indem Sie für die Hinzufügung von Masken/Clip-Path abstimmen.

Methode 4: linear-gradient Hintergrund

Wir können einen Rahmen simulieren, indem wir eine clevere Kombination aus mehreren linear-gradient-Hintergründen verwenden, die richtig dimensioniert sind. Insgesamt haben wir vier separate Verläufe, einen für jede Seite. Die Eigenschaften background-position und background-size bringen jeden Verlauf an die richtige Stelle und in die richtige Größe, die dann animiert werden können, um den Rahmen erweitern zu lassen.

.border-button {
  background-repeat: no-repeat;
  
  /* background-size values will repeat so we only need to declare them once */
  background-size: 
    calc(100% - 10px) 5px, /* top & bottom */
    5px calc(100% - 10px); /* right & left */
  
  background-position: 
    5px 5px, /* top */
    calc(100% - 5px) 5px, /* right */
    5px calc(100% - 5px), /* bottom */
    5px 5px; /* left */
  
  /* Since we're sizing and positioning with the above properties, we only need to set up a simple solid-color gradients for each side */
  background-image: 
    linear-gradient(0deg, #FC5185, #FC5185),
    linear-gradient(0deg, #FC5185, #FC5185),
    linear-gradient(0deg, #FC5185, #FC5185),
    linear-gradient(0deg, #FC5185, #FC5185);
  
  transition: all 0.6s linear;
  transition-property: background-size, background-position;
}

.border-button:hover {
  background-position: 0 0, 100% 0, 0 100%, 0 0;
  background-size: 100% 10px, 10px 100%, 100% 10px, 10px 100%;
}

Siehe den Pen von Shaw (@shshaw) auf CodePen.

Diese Methode ist sehr schwierig einzurichten und weist viele browserübergreifende Unterschiede auf. Firefox und Safari animieren den Faux-Rahmen sanft, genau der Effekt, den wir suchen. Die Animation von Chrome ist ruckelig und noch abgehackter als die outline- und border-Übergänge. IE und Edge weigern sich, den background überhaupt zu animieren, aber sie geben den richtigen Rahmenexpansionseffekt.

Methode 5: Vortäuschen mit box-shadow

Versteckt in den Spezifikationen von box-shadow gibt es einen vierten Wert für den spread-radius. Setzen Sie alle anderen Längenwerte auf 0px und verwenden Sie den Spread-Radius, um Ihre border-Alternative zu erstellen, die wie outline das Layout nicht beeinflusst.

.border-button {
  box-shadow: 0px 0px 0px 5px #FC5185;
  transition: box-shadow 0.6s linear;
  margin: 0.5em; /* Increased margin since the box-shado expands outside the element, like outline */
}

.border-button:hover { box-shadow: 0px 0px 0px 10px #FC5185; }

Siehe den Pen von Shaw (@shshaw) auf CodePen.

Der Übergang mit box-shadow ist ausreichend performant und fühlt sich viel sanfter an, außer in Safari, wo er während des Übergangs auf ganze Werte springt, wie bei border und outline.

Pseudoelemente

Mehrere dieser Techniken können modifiziert werden, um stattdessen ein Pseudoelement zu verwenden, aber Pseudoelemente verursachten in meinen Tests einige zusätzliche Leistungsprobleme.

Bei der box-shadow-Methode löste der Übergang gelegentlich ein Painting in einem viel größeren Bereich als nötig aus. Reinier Kaper wies darauf hin, dass ein Pseudoelement helfen kann, das Painting auf einen spezifischeren Bereich zu isolieren. Bei weiteren Tests verursachte box-shadow keine großen Bereiche des Dokuments mehr und die Komplexität des Pseudoelements war letztendlich weniger performant. Die Änderung des Paintings und der Leistung könnte auf ein Chrome-Update zurückzuführen sein, testen Sie es also gerne selbst.

Ich konnte auch keine Möglichkeit finden, Pseudoelemente so zu nutzen, dass eine transform-basierte Animation möglich wäre.

Warum nicht transform: scale?

Sie schalten vielleicht Twitter ein, um hilfreich vorzuschlagen, transform: scale dafür zu verwenden. Da transform und opacity die besten Stileigenschaften für Leistung sind, warum nicht ein Pseudoelement verwenden und den Rahmen hoch- und runterskalieren lassen?

.border-button {
  position: relative;
  margin: 0.5em;
  border: solid 5px transparent;
  background: #3E4377;
}

.border-button:after {
  content: '';
  display: block;
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  border: solid 10px #FC5185;
  margin: -15px;
  z-index: -1;
  transition: transform 0.6s linear;
  transform: scale(0.97, 0.93);
}

.border-button:hover::after { transform: scale(1,1); }

Siehe den Pen von Shaw (@shshaw) auf CodePen.

Es gibt ein paar Probleme.

  1. Der Rahmen wird durch einen transparenten Button hindurchscheinen. Ich habe einen Hintergrund auf den Button gezwungen, um zu zeigen, wie der Rahmen hinter dem Button versteckt ist. Wenn Ihr Design Buttons mit einem vollständigen Hintergrund vorsieht, könnte dies funktionieren.
  2. Sie können den Rahmen nicht auf spezifische Größen skalieren. Da die Abmessungen des Buttons mit dem Text variieren, gibt es keine Möglichkeit, den Rahmen nur mit CSS von genau 5px auf 10px zu animieren. In diesem Beispiel habe ich einige Magiezahlen bei der scale verwendet, um es richtig aussehen zu lassen, aber das wird nicht universell sein.
  3. Der Rahmen animiert ungleichmäßig, da das Seitenverhältnis des Buttons nicht 1:1 ist. Dies bedeutet normalerweise, dass links/rechts größer erscheint als oben/unten, bis die Animation abgeschlossen ist. Dies ist möglicherweise kein Problem, abhängig davon, wie schnell Ihr Übergang ist, dem Seitenverhältnis des Buttons und der Größe Ihres Rahmens.

Wenn Ihr Button festgelegte Abmessungen hat, hat Cher eine clevere Möglichkeit zur Berechnung der benötigten Skalierungen aufgezeigt, obwohl diese Rundungsfehlern unterliegen kann.

Jenseits von CSS

Wenn wir unsere Regeln etwas lockern, gibt es viele interessante Möglichkeiten, Rahmen zu animieren. Codrops leistet in diesem Bereich konstant hervorragende Arbeit und nutzt meist SVGs und JavaScript. Die Endergebnisse sind sehr zufriedenstellend, können aber etwas komplex zu implementieren sein. Hier sind einige, die sich zu überprüfen lohnen:

Fazit

Es steckt mehr in Rahmen als nur border, aber wenn Sie einen Rahmen animieren möchten, haben Sie möglicherweise einige Schwierigkeiten. Die hier behandelten Methoden helfen, obwohl keine davon eine perfekte Lösung ist. Welche Sie wählen, hängt von den Anforderungen Ihres Projekts ab, daher habe ich eine Vergleichstabelle erstellt, die Ihnen bei der Entscheidung hilft.

Siehe den Pen von Shaw (@shshaw) auf CodePen.

Meine Empfehlung wäre, box-shadow zu verwenden, da es die beste Gesamtbalance zwischen einfacher Implementierung, Animationseffekt, Leistung und Browserunterstützung bietet.

Haben Sie eine andere Möglichkeit, einen animierten Rahmen zu erstellen? Vielleicht eine clevere Art, Transformationen für die Bewegung eines Rahmens zu nutzen? Kommentieren Sie unten oder erreichen Sie mich auf Twitter, um Ihre Lösung für die Herausforderung zu teilen.

Besonderer Dank an Martin Pitt, Steve Gardner, Cher, Reinier Kaper, Joseph Rex, David Khourshid und die Animation at Work Community.