Animationshöhe - der richtige Weg

Lass uns ehrlich sein. Animationshöhe kann ein großer Schmerz sein. Für mich war es ein ständiger Kampf zwischen dem Wunsch, schöne Animationen zu haben und nicht bereit zu sein, die enormen Leistungskosten zu bezahlen, die mit der Höhe der Animation verbunden sind. Nun - das ist alles erledigt.

Alles begann, als ich an meinem Nebenprojekt arbeitete - einem Lebenslaufersteller, in dem Sie Links zu Ihrem Lebenslauf freigeben können, die nur für einen bestimmten Zeitraum aktiv sind.

Ich wollte eine schöne Animation für alle Abschnitte im Lebenslauf haben und habe eine Reaktionskomponente erstellt, die die Animation ausführt. Ich stellte jedoch bald fest, dass diese Komponente die Leistung auf Geräten der unteren Preisklasse und in einigen Browsern absolut zerstörte. Zur Hölle, sogar mein High-End-Macbook-Profi hatte Mühe, die Animation reibungslos zu gestalten.

Der Grund dafür ist natürlich, dass das Animieren der Eigenschaft height in CSS dazu führt, dass der Browser teure Layout- und Malvorgänge für jeden Frame ausführt. Es gibt einen fantastischen Abschnitt zum Rendern der Leistung bei Google Web Fundamentals. Ich empfehle Ihnen, ihn sich anzusehen.

Kurz gesagt: Wenn Sie eine geometrische Eigenschaft in CSS ändern, muss der Browser die Auswirkungen dieser Änderung auf das Layout auf der Seite anpassen und berechnen. Anschließend muss die Seite in einem Schritt namens Paint neu gerendert werden.

Warum sollten wir uns überhaupt um Leistung kümmern?

Es kann verlockend sein, die Leistung zu ignorieren. Es ist nicht weise, aber es kann verlockend sein. Aus geschäftlicher Sicht sparen Sie viel Zeit, die Sie sonst für die Erstellung neuer Funktionen benötigen.

Die Leistung kann sich jedoch direkt auf Ihr Endergebnis auswirken. Was nützt es, viele Funktionen zu erstellen, wenn sie von niemandem verwendet werden? Mehrere von Amazon und Google durchgeführte Studien zeigen, dass dies der Fall ist. Die Leistung hängt direkt mit der Anwendungsnutzung und dem Umsatz zusammen.

Die andere Seite der Leistung ist ebenso wichtig. Wir als Entwickler sind dafür verantwortlich, dass das Web für alle zugänglich bleibt - wir tun dies, weil es richtig ist. Weil das Internet nicht nur für Sie und mich ist, ist es für alle.

Wie aus Addy Osmanis ausgezeichnetem Artikel hervorgeht, dauert das Parsen und Ausführen von Javascript bei Low-End-Geräten im Vergleich zu ihren High-End-Geräten erheblich länger.

Um eine Klassenunterschiede im Internet zu vermeiden, müssen wir unermüdlich nach Leistung streben. Für mich bedeutete dies, kreativ zu sein und ein anderes Mittel zu finden, um meine Animationen zu erzielen, ohne die Leistung zu beeinträchtigen.

Höhe richtig animieren

Wenn Sie sich nicht für das Wie interessieren und nur ein Live-Beispiel sehen möchten, lesen Sie bitte die folgenden Links für die Demo-Site, Beispiele und ein npm-Paket, um zu reagieren:

  • Demo-Site unter Verwendung der Technik
  • Live-Beispiel in Vanille JS
  • Einfaches Beispiel in reagieren
  • NPM-Paket und Dokumentation zum Reagieren

Die Frage, die ich mir stellte, war, wie ich die Leistungskosten vermeiden kann, die durch das Animieren der Höhe entstehen. Einfache Antwort - das kannst du nicht.

Stattdessen musste ich mit anderen CSS-Eigenschaften kreativ werden, für die diese Kosten nicht anfallen. Verwandelt sich nämlich.

Da Transformationen keine Möglichkeit haben, die Höhe zu beeinflussen. Wir können nicht einfach eine einfache Eigenschaft auf ein Element anwenden und fertig sein. Wir müssen klüger sein.

Die Art und Weise, wie wir eine performante Animation der Höhe erzielen, besteht darin, sie mit transform: scaleY vorzutäuschen. Die Animation erfolgt in mehreren Schritten:

Hier ist ein Beispiel:

 ${this.markup} `
  • Zuerst müssen wir die Anfangshöhe des Elementcontainers ermitteln. Dann setzen wir die Höhe des äußeren Containers explizit auf diese Höhe. Dies führt dazu, dass sich ändernde Inhalte über den Container laufen und stattdessen den übergeordneten Container erweitern.
  • Im äußeren Container befindet sich ein weiteres Div, das absolut positioniert ist, um die gesamte Breite und Höhe des Div zu überspannen. Dies ist unser Hintergrund und wird skaliert, sobald wir die Transformation umschalten.
  • Wir haben auch einen inneren Behälter. Der innere Container enthält das Markup und ändert seine Höhe entsprechend dem darin enthaltenen Inhalt.
  • Hier ist der Trick:  Sobald wir ein Ereignis umschalten, das das Markup ändert, nehmen wir die neue Höhe des inneren Containers und berechnen den Betrag, den der Hintergrund skalieren muss, um die neue Höhe aufzunehmen. Dann setzen wir den Hintergrund um den neuen Betrag auf scaleY.
  • In Vanille-Javascript bedeutet dies einige Tricks mit zwei Renderings. Einmal, um die Höhe des inneren Behälters zu erhalten, um die Skala zu berechnen. Wenden Sie dann die Skala erneut auf das Hintergrund-Div an, damit die Transformation ausgeführt wird.

Sie können ein Live-Beispiel hier in Vanilla JS sehen.

Zu diesem Zeitpunkt wurde unser Hintergrund richtig skaliert, um die Illusion von Höhe zu erzeugen. Aber was ist mit dem umgebenden Inhalt? Da wir das Layout nicht mehr anpassen, ist der umgebende Inhalt nicht von den Änderungen betroffen.

Um den umgebenden Inhalt zu verschieben. Wir müssen den Inhalt mit transformY anpassen. Der Trick besteht darin, den Betrag zu nehmen, um den der Inhalt erweitert wurde, und diesen zu verwenden, um den umgebenden Inhalt mit Transformationen zu verschieben. Siehe das obige Beispiel.

Animationshöhe in React

Wie bereits erwähnt, habe ich diese Methode bei der Arbeit an einem persönlichen Projekt in React entwickelt. Diese Demo-Site verwendet diese Methode ausschließlich in allen „Höhenanimationen“. Schauen Sie sich hier die Demo-Site an.

Nachdem ich dies erfolgreich implementiert hatte, nahm ich mir die Zeit, diese Komponente und einige unterstützende Komponenten zu einer kleinen Animationsbibliothek hinzuzufügen, die ich in React erstellt habe. Bei Interesse finden Sie hier die relevanten Informationen:

  • Sehen Sie sich hier die Bibliothek auf NPM an
  • Dokumentation finden Sie hier.

Die wichtigsten Komponenten in dieser Bibliothek sind AnimateHeight und AnimateHeightContainer. Untersuchen wir sie:

// Inside a React component // handleAnimateHeight is called inside AnimateHeight and is passed the // transitionAmount and optionally selectedId if you pass that as a prop to // AnimateHeight. This means that you can use the transitionAmount to // transition your surrounding content.const handleAnimateHeight = (transitionAmount, selectedId) => { this.setState({ transitionAmount, selectedId }); }; // Takes a style prop, a shouldchange prop and a callback. shouldChange // determines when the AnimateHeight component should trigger, which is // whenever the prop changes. The same prop is used to control which // content to show.  {this.state.open && } {!this.state.open && } 
  • Beispiel mit dem Verschieben von umgebenden Inhalten

Die obigen Beispiele zeigen Ihnen, wie Sie AnimateHeight verwenden und den umgebenden Inhalt manuell zum Anpassen auslösen. Aber was ist, wenn Sie viel Inhalt haben und diesen Vorgang nicht manuell ausführen möchten? In diesem Fall können Sie AnimateHeight in Verbindung mit AnimateHeightContainer verwenden.

Um AnimateHeightContainer verwenden zu können, müssen Sie allen Kindern der obersten Ebene eine Requisite namens animateHeightId zur Verfügung stellen, die auch an Ihre AnimateHeight-Komponenten übergeben werden muss:

// Inside React Component const handleAnimateHeight = (transitionAmount, selectedId) => { this.setState({ transitionAmount, selectedId }); }; // AnimateHeight receives the transitionAmount and the active id of the AnimateHeight that fired. {this.state.open &&  {!this.state.open && }  // When AnimateHeight is triggered by state change // this content will move because the animateHeightId // is greater than the id of the AnimateHeight component above I will move I will also move 

Wie Sie in diesem Beispiel sehen können, erhält AnimateHeight die Informationen, die zum Anpassen des Inhalts erforderlich sind, wenn die AnimateHeight-Komponente durch Ändern des Status ausgelöst wird.

In diesem Fall löst die AnimateHeight-Komponente den Rückruf aus und setzt die Eigenschaften in den Status. In AnimateHeight sieht es ungefähr so ​​aus (vereinfacht):

// Inside AnimateHeight componentDidUpdate() { if (update) { doUpdate() callback(transitionAmount, this.props.animateHeightId) } } // Equivalent to calling this function: const handleAnimateHeight = (transitionAmount, selectedId) => { this.setState({ transitionAmount, selectedId }); }; handleAnimateHeight(transitionAmount, this.props.animateHeight)

Jetzt geben Sie den Betrag an, um den der Inhalt in Pixel übergegangen ist, und die ID der ausgelösten AnimateHeight-Komponente. Sobald Sie diese Werte an den AnimateHeightContainer übergeben haben, werden sie übernommen und die anderen Komponenten in sich selbst übertragen, vorausgesetzt, Sie haben inkrementelle animateHeightIds für die untergeordneten Elemente der obersten Ebene eingerichtet.

Fortgeschrittenere Beispiele:

  • Verschieben von umgebenden Inhalten mit AnimateHeightContainer
  • Akkordeon Beispiel

HINWEIS: Wenn Sie diese Methode verwenden, um die Höhe zu animieren und den umgebenden Inhalt zu verschieben, müssen Sie den Übergangsbetrag zur Höhe Ihrer Seite hinzufügen.

Fazit

Möglicherweise haben Sie in diesem Artikel bemerkt, dass wir die Höhe nicht wirklich animieren - und es als falsch bezeichnen. Sie haben natürlich absolut Recht. Ich bin jedoch fest davon überzeugt, dass das, was wir es nennen, keine Rolle spielt. Entscheidend ist, dass wir den gewünschten Effekt mit möglichst geringen Leistungskosten erzielen.

Obwohl ich denke, dass ich einen Weg gefunden habe, der besser ist, als die Eigenschaft height direkt zu animieren, erhebe ich keinen Anspruch darauf, etwas erfunden oder auf andere Weise an etwas gedacht zu haben, das zuvor noch nicht entdeckt wurde. Ich urteile auch nicht. Vielleicht funktioniert das Animieren der Höhe in Ihrem Szenario für Sie - kein Problem.

All I want is to enable and simplify effects that we all need to do, but sometimes incur costs that are difficult to bear. At the very least, I want to spark a discussion that is worth having. How can we improve the internet for everyone?