Verwenden wir React, styled-components und react-flip-toolkit, um unsere eigene Version des animierten Navigationsmenüs auf der Stripe-Homepage zu erstellen. Es ist ein beeindruckendes Menü mit einigen schicken Animationseffekten, und die Kombination dieser drei Tools kann die Nachbildung relativ einfach machen.
Dies ist eine Anleitung für Fortgeschrittene, die Vertrautheit mit React und grundlegenden Animationskonzepten voraussetzt. Unser React-Leitfaden ist ein guter Ausgangspunkt.
Hier ist, was wir erstellen wollen
Sehen Sie den Pen React Stripe Menu von Alex (@aholachek) auf CodePen.
Zerlegung der Animation
Zerlegen wir zuerst die Animation in verschiedene Teile, damit wir sie leichter reproduzieren können. Möglicherweise möchten Sie das fertige Produkt in Zeitlupe überprüfen (verwenden Sie die Schalter), um alle Details zu erfassen.
- Der weiße Dropdown-Container aktualisiert sowohl seine Größe als auch seine Position.
- Der graue Hintergrund in der unteren Hälfte des Dropdown-Containers verändert seine Höhe.
- Während sich der Dropdown-Container bewegt, blenden die vorherigen Inhalte aus und verschieben sich leicht in die entgegengesetzte Richtung, als ob der Dropdown sie hinter sich lässt, während die neuen Inhalte ins Bild gleiten.
Es gibt einige nützliche Leitlinien, die man im Hinterkopf behalten sollte, wenn wir diese Animation in React nachbilden. Wo immer möglich, halten wir die Dinge einfach, indem wir den Browser das Layout verwalten lassen. Dies tun wir, indem wir Elemente im normalen DOM-Fluss belassen, anstatt absolute Positionierung und manuelle Berechnungen zu verwenden. Anstatt eine einzelne Dropdown-Komponente zu haben, die wir jedes Mal neu positionieren müssen, wenn sich die Mausposition eines Benutzers ändert, rendern wir einen einzelnen Dropdown im entsprechenden Navigationsabschnitt.
Wir verwenden die FLIP-Technik, um die Illusion zu erzeugen, dass die drei separaten Dropdown-Komponenten tatsächlich eine einzige, sich bewegende Komponente sind.
Grundgerüst der Benutzeroberfläche mit styled-components
Zunächst erstellen wir eine nicht animierte Navbar-Komponente, die einfach ein Konfigurationsobjekt aus Titeln und Dropdown-Komponenten entgegennimmt und ein Navbar-Menü rendert. Die Komponente zeigt und verbirgt das relevante Dropdown beim Mouseover.
Wir erstellen die UI-Komponenten mit styled-components. Sie sind nicht nur eine praktische Möglichkeit, eine modulare Benutzeroberfläche zu erstellen, sondern verfügen auch über eine großartige API zum Hinzufügen konfigurierbarer CSS-Keyframe-Animationen. Es stellt sich heraus, dass CSS-Animationen und React sehr gut zusammenarbeiten, daher werden wir CSS-Keyframes verwenden, um viele der Animationen später hinzuzufügen.
Mit den zusammengefügten Komponenten ohne Animationen haben wir etwas geschaffen, das so aussieht:
Sehen Sie den Pen React Stripe Menu Before Animation von Alex (@aholachek) auf CodePen.
Beachten Sie, dass der graue Hintergrund am unteren Rand des Menüs fehlt. Es ist das einzige Element, das wir aus dem normalen DOM-Fluss herausnehmen und absolut positionieren müssen, also ignorieren wir es vorerst.
Animation unseres Dropdowns mit der FLIP-Technik
Wir werden die Bibliothek react-flip-toolkit verwenden, um die Größe und Position des Dropdowns zu animieren. Dies ist eine Bibliothek, die ich entwickelt habe, um fortgeschrittene und komplexe Übergänge einfacher und konfigurierbarer zu machen.
Sie stellt uns zwei Komponenten zur Verfügung: eine übergeordnete <Flipper/>-Komponente und eine <Flipped/>-Komponente, um alle Kinder zu umschließen, die wir animieren möchten.
Zuerst richten wir die Flipper-Wrapper-Komponente in der Render-Funktion von AnimatedNavbar ein.
// currentIndex is the index of the hovered dropdown
<Flipper flipKey={currentIndex}>
<Navbar>
{navbarConfig.map((n, index) => {
// render navbar items here
})}
</Navbar>
</Flipper>
Als nächstes umschließen wir in unserer DropdownContainer-Komponente Elemente, die animiert werden müssen, in ihre eigenen Flipped-Komponenten und geben ihnen jeweils einen eindeutigen flipdId-Prop.
<DropdownRoot>
<Flipped flipId="dropdown-caret">
<Caret />
</Flipped>
<Flipped flipId="dropdown">
<DropdownBackground>
{children}
</DropdownBackground>
</Flipped>
</DropdownRoot>
Wir animieren die <Caret/>-Komponente und die <DropdownBackground/>-Komponente separat, damit der Stil overflow:hidden der <DropdownBackground/>-Komponente die Darstellung der <Caret/>-Komponente nicht beeinträchtigt.
Jetzt haben wir eine funktionierende FLIP-Animation, aber es gibt immer noch ein Problem: Die Inhalte des Dropdowns erscheinen im Laufe der Animation seltsam verzerrt.
Sehen Sie den Pen React Stripe Menu — Fehler #1: keine Skalierung von Alex (@aholachek) auf CodePen.
Dieser unerwünschte Effekt tritt auf, weil Skalierungstransformationen auf Kinder angewendet werden. Wenn Sie scaleY(2) auf ein Div mit Text anwenden, wird der Text skaliert und verzerrt.
Dieses Problem können wir lösen, indem wir die Kinder in eine Flipped-Komponente mit einer inverseFlipId verpacken, die sich auf die flipId der übergeordneten Komponente (in diesem Fall "dropdown") bezieht, um zu fordern, dass übergeordnete Transformationen für Kinder aufgehoben werden. Da wir möchten, dass Translate-Transformationen die Kinder weiterhin beeinflussen, übergeben wir auch den scale-Prop, um die Aufhebung auf Skalierungsänderungen zu beschränken.
<DropdownRoot>
<Flipped flipId="dropdown-caret">
<Caret />
</Flipped>
<Flipped flipId="dropdown">
<DropdownBackground>
<Flipped inverseFlipId="dropdown" scale>
{children}
</Flipped>
</DropdownBackground>
</Flipped>
</DropdownRoot>
Puh. All diese Arbeit und wir haben etwas geschaffen, das so aussieht:
Sehen Sie den Pen React Stripe Menu — Einfaches FLIP von Alex (@aholachek) auf CodePen.
Alles im Detail
Es wird besser, aber wir müssen uns noch um die kleinen Details kümmern, die die Animation großartig aussehen lassen: die subtile Rotationsanimation, wenn der Dropdown erscheint und verschwindet, das Überblenden der vorherigen und aktuellen Dropdown-Elemente und die seidig-glatte Höhenänderung des grauen Hintergrunds.
Konfigurierbare CSS-Keyframe-Animationen mit styled-components
Styled-components, das wir zum Aufbau der Benutzeroberfläche für diese Demo verwendet haben, bietet eine äußerst praktische Möglichkeit, konfigurierbare Keyframe-Animationen zu erstellen. Wir werden diese Funktionalität sowohl für die Enter-Animation des Dropdowns als auch für das Überblenden der Inhalte verwenden. Wir können einige grundlegende Informationen über die gewünschte Animation übergeben – ob die Inhalte ein- oder ausgeblendet werden und in welche Richtung sich die Maus des Benutzers bewegt hat – und erhalten automatisch die entsprechende Animation. Hier ist zum Beispiel der Code für die Crossfade-Animation in der <FadeContents>-Komponente.
const getFadeContainerKeyFrame = ({ animatingOut, direction }) => {
if (!direction) return;
return keyframes`
from {
transform: translateX(${
animatingOut ? 0 : direction === "left" ? 20 : -20
}px);
}
to {
transform: translateX(${
!animatingOut ? 0 : direction === "left" ? -20 : 20
}px);
opacity: ${animatingOut ? 0 : 1};
}
`;
};
const FadeContainer = styled.div`
animation-name: ${getFadeContainerKeyFrame};
animation-duration: ${props => props.duration * 0.5}ms;
animation-fill-mode: forwards;
position: ${props => (props.animatingOut ? "absolute" : "relative")};
opacity: ${props => (props.direction && !props.animatingOut ? 0 : 1)};
animation-timing-function: linear;
top: 0;
left: 0;
`;
Jedes Mal, wenn der Benutzer über einen neuen Artikel fährt, stellen wir dem DropdownContainer-Komponente nicht nur das aktuelle Dropdown, sondern auch das vorherige Dropdown als Kinder zur Verfügung, zusammen mit Informationen darüber, in welche Richtung sich die Maus des Benutzers bewegt hat. Die DropdownContainer-Komponente verpackt dann beide Kinder in eine neue Komponente, FadeContents, die den obigen Keyframe-Animationscode verwendet, um den entsprechenden Übergang hinzuzufügen.
Hier ist ein Link zum vollständigen Code für die FadeContents-Komponente.
Die Enter/Exit-Animation des Dropdowns funktioniert sehr ähnlich.
Der letzte Schliff: Eine flüssige Hintergrundanimation
Zum Schluss fügen wir die Animation des grauen Hintergrunds hinzu. Um diese Animation scharf zu halten, müssen wir von unserer bisherigen Strategie, normale DOM-Verschachtelung beizubehalten und den Browser das Layout verwalten zu lassen, abweichen und stattdessen einige manuelle Positionsberechnungen durchführen. Wir müssen auch direkt mit dem DOM interagieren. Kurz gesagt, es wird ein wenig unübersichtlich.
Hier ist eine visuelle Darstellung unseres grundlegenden Ansatzes
Sehen Sie den Pen React Stripe Menu — Animierter Hintergrund von Alex (@aholachek) auf CodePen.
Wir positionieren ein graues Div absolut am oberen Rand des DropdownContainer. In der Lebenszyklusfunktion componentDidMount von DropdownContainer aktualisieren wir die translateY-Transformation des grauen Hintergrunds. Wenn die DropdownContainer-Komponente nur ein Kind hat (was bedeutet, dass der Benutzer bisher nur einen Dropdown überfahren hat), setzen wir das translateY des grauen Divs auf die Höhe des ersten Dropdown-Abschnitts. Wenn zwei Kinder vorhanden sind, einschließlich eines vorherigen Dropdowns, setzen wir stattdessen das anfängliche translateY auf die Höhe des ersten Abschnitts des vorherigen Dropdowns und animieren dann das translateY auf die Höhe des ersten Abschnitts des aktuellen Dropdowns. Hier ist die Funktion, die in componentDidMount aufgerufen wird.
const updateAltBackground = ({
altBackground,
prevDropdown,
currentDropdown
}) => {
const prevHeight = getFirstDropdownSectionHeight(prevDropdown)
const currentHeight = getFirstDropdownSectionHeight(currentDropdown)
// we'll use this function when we want a change
// to happen immediately, without CSS transitions
const immediateSetTranslateY = (el, translateY) => {
el.style.transform = `translateY(${translateY}px)`
el.style.transition = "transform 0s"
requestAnimationFrame(() => (el.style.transitionDuration = ""))
}
if (prevHeight) {
// transition the grey ("alt") background from its previous height
// to its current height
immediateSetTranslateY(altBackground, prevHeight)
requestAnimationFrame(() => {
altBackground.style.transform = `translateY(${currentHeight}px)`
})
} else {
// immediately set the background to the appropriate height
// since we don't have a stored value
immediateSetTranslateY(altBackground, currentHeight)
}
}
Dieser Ansatz erfordert, dass DropdownContainer ein ref verwendet und in seine Kinder hineingreift, um DOM-Messungen in der Funktion getFirstDropdownSectionHeight durchzuführen, was sich schlampig anfühlt. Wenn Sie Ideen für alternative Implementierungen haben, lassen Sie es mich bitte in den Kommentaren wissen!
Zusammenfassung
Hoffentlich hat dieser Artikel einige Techniken verdeutlicht, die Sie verwenden können, wenn Sie das nächste Mal eine Animation in React erstellen. Normalerweise gibt es mehrere Möglichkeiten, einen Effekt zu erzielen, aber oft ist es sinnvoll, mit der einfachsten möglichen Implementierung zu beginnen – grundlegende Komponenten mit einigen CSS-Übergängen oder Keyframe-Animationen – und die Komplexität von dort aus zu skalieren, wenn nötig. In unserem Fall bedeutete dies die Einbeziehung einer zusätzlichen Bibliothek, react-flip-toolkit, damit wir uns nicht um die manuelle Übergang der Position der Dropdown-Komponente über den Bildschirm kümmern mussten. Um die Animation vollständig nachzubilden, mussten wir eine beträchtliche Menge an Code schreiben. Aber indem wir diese Animation in separate Teile zerlegten und sie nacheinander angehen, konnten wir einen ziemlich coolen UI-Effekt in React nachbilden.
großartig~
Gute Arbeit. Aber ist es nicht übertrieben, React zu verwenden? Das hätte mit deutlich weniger Codezeilen in reinem JavaScript gemacht werden können.
Es ist definitiv übertrieben. Dieser Artikel ist schon eine Weile alt: https://codyhouse.co/gem/stripe-navigation
Ich denke, der Punkt des Autors war, speziell zu zeigen, wie man dies in React erreicht. Die Verwendung von React war Teil des Tutorials und nicht nur eine Werkzeugwahl. So wie Tutorials zum Erstellen von To-Do-Apps mit React oder Vue verwendet werden, um das Framework vorzustellen, ist dies ein Artikel, der komplexe UI-Animationen mit React zeigt. Es ist überhaupt keine übertriebene Situation.
Schöner Artikel! Würde mir auch so etwas für VueJS wünschen...
Ja, es wäre sehr cool, das in VueJS zu sehen. Ich überlege, es von Grund auf neu zu machen oder eine externe Komponente zu verwenden.
Ein großartiger Beitrag! React ist eines der besten Werkzeuge für diese Zwecke.