React App: Flexbox mit gleicher Spaltenbreite durch flex-basis: 100-Prozent
Gleichmäßige Spaltenbreiten lassen sich mit display:flex beim Parent-Element und flex-basis: 100% beim child-Element einfach und intuitiv umsetzen. Auch dann noch, wenn sich die Anzahl der Spalten dynamisch ändert. Eine React-App zeigt die optische Wirkung.
Gleichmäßige Spaltenbreiten lassen sich mit display:flex
beim Parent-Element und flex-basis: 100%
beim child-Element einfach und intuitiv umsetzen. Auch dann noch, wenn sich die Anzahl der Spalten dynamisch ändert. Eine React-App zeigt die optische Wirkung.
Github Repository des Codes
CodePen-Demo
Für gleichmäßig breite Spalten beim Design einer Website gibt es mehrere Möglichkeiten, mit Flexbox gibt es aber eine besonders einfache und intuitive1. Setzt man das Parent-Element auf display: flex
und die Child-Elemente auf flex-basis: 100%
werden die Spalten gleichmäßig breit dargestellt. Wenn eine Spalte dynamisch hinzugefügt oder entfernt wird, entfällt das aufwendige und fehleranfällige Berechnen der einzelnen Spalten mit Pixeln oder Prozentangaben.
Dabei ist man übrigens gar nicht auf gleichmäßig breite Spalten festgelegt (oder bei flex-direction: column
auf Layoutzeilen), sondern kann bei den Child-Elementen auch Verhältnisangaben mühelos und eingängig festlegen (z. B. zwei Drittel – ein Drittel).
React-App mit Animationen
Mit einer kleinen React-App habe ich diesen Effekt optisch umgesetzt, sodass der Nutzer die Anzahl der Spalten und die flex-basis-Prozentangaben der Spaltenelemente frei festlegen kann. Die React-App wird durch Animationen aufgewertet und besteht aus den Komponenten App.js
, Card.js
, ColumnDisplay.js
und DisplayInterval.js
.
App.js
umfasst die Hauptkomponente, die die vom Nutzer änderbare Anzahl der Childkomponenten als Cards per Mapping ausgibt.
<ul className="mb-4 card-container" style={{display: 'flex', flexDirection: 'row',}}>
{
cardArray.map((cardItem, index) => <Card {...cardItem} key={idArr.current[index]}/>)
}
</ul>
Die Card-Komponente (die jeweils eine Spalte repräsentieren soll) umfasst die animierte Darstellung der Prozentangaben durch die Komponente DisplayInterval
sowie das Handling der drei Buttons. Die Komponente ColumnDisplay
zeigt die Anzahl der Cards mit Symbolen an.
Technisch aufwendiger als die bloße Darstellung der Spaltenanzahl mit Symbolen ist die Animation der hinzukommenden oder abgehenden Karten auf der rechten Bildschirmseite. Weil React beim Entfernen einer Karte die Darstellung des Karten-Containers sofort neu rendern würde, wird die Änderung des States durch eine setTimeout
verzögert und mit die jeweiligen Cards mit CSS-Hilfsklassen entry
und exit
versehen, die die e@keyframes
-Animationen steuern.
Browser-Animation mit requestAnimationFrame
Die Komponente DisplayInterval
steuert die Anzeige der Prozentzahlen, die ein animiertes Hoch- und Runterzählen der Prozentangaben umsetzt. Ich nutze dabei die Browser-API-Funktion requestAnimationFrame
, der eine Callback-Funktion (in diesem Fall animate
) als Argument übergeben wird, die vor dem nächsten Repaint des Browsers aufgerufen wird. Diese Animation ist ressourcenschonender als die alten Javascript-Techniken setInterval
und setTimeout
, die auch dann noch arbeiten, wenn das Tab gerade nicht aktiv ist.
Der Animationsmethode animate
muss man sich dabei anders annähern, als man das von den anderen Methoden gewohnt ist. Der springende Punkt ist, zu einer bestimmten Zeit (hierfür wird die vergangene Zeit seit Start der Methode in der Variablen deltaTime
berechnet), den Zustand einer Animation zu berechnen. In meinem Fall musste also berechnet werden, welche Prozentzahl zu einer bestimmten Zeit dargestellt werden soll, wenn die Animation zum Beispiel 200 Millisekunden dauern soll.
const updatedNumber = Math.min(prevNumberRef.current + sign * Math.round(deltaTime / (duration / Math.abs(numberGap))), newNumber);
setCurrentNumber(updatedNumber);
Diese Berechnung übernimmt im oben dargestellten Beispiel die Variable updatedNumber
.
Die Variablen und ihre Bedeutung:
updatedNumber
: Die Berechnung der zu einer bestimmten Zeit darzustellenden Zahlsign
: Gibt das Vorzeichen plus eins oder minus eins an(Multiplikator 1 oder -1), die darüber entscheidet, ob die Zahl rauf oder runter gehtdeltaTime
: Die seit dem Start der Animation vergangene Zeit in MillisekundennumberGap
: Die Differenz zwischen altem und neuen ProzentwertnewNumber
: Der Zielwert der Animation, die am Schluss der Animation darzustellende Zahl
Um Rundungsfehler zu vermeiden, wird mit der Math.min
-Funktion innerhalb der Animation sichergestellt, dass die berechnete Zahl updatedNumber
nie höher liegt als der Zielwert newNumber
.
return () => cancelAnimationFrame(requestRef.current);
Diese Callback-Funktion innerhalb des useEffect
-Hooks, der nach dem Rendern und nur beim Ändern der newNumber
-Prop aufgerufen wird, räumt die Animation als Cleanup-Funktion auf und gibt den Speicherplatz für die Garbage Collection frei. Enjoy!
- ↑Auf seinem Youtube-Kanal hat Kevin Powell diese Möglichkeit ausführlich dargestellt.