Erstellen von Vue.js-Übergängen & Animationen

Avatar of Nicolas Udy
Nicolas Udy am

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

Meine letzten beiden Projekte haben mich in den JAMstack geworfen. SPAs, Headless Content Management, statische Generierung… was auch immer Sie nennen. Wichtiger noch, sie gaben mir die Gelegenheit, Vue.js zu lernen. Mehr als nur "eine To-Do-App bauen", ich durfte echte, produktionsreife Vue-Apps ausliefern.

Die Agentur hinter Snipcart (Spektrum) wollte anfangen, entkoppelte JavaScript-Frameworks für kleine bis mittelgroße Websites zu verwenden. Bevor sie diese jedoch auf Kundenprojekte anwendeten, entschieden sie sich, erst einmal selbst damit zu experimentieren. Nachdem einige meiner Kollegen erfolglose Erfahrungen mit React gemacht hatten, erhielt ich grünes Licht, einige Apps in Vue zu prototypisieren. Dieses Prototyping entwickelte sich zu vollwertigen Vue-Apps für Spektrum, die mit einem Headless CMS verbunden waren. Zuerst verbrachte ich Zeit damit, herauszufinden, wie ich unsere Daten angemessen modellieren und rendern konnte. Dann stürzte ich mich kopfüber in Vue-Transformationen, um unseren beiden Projekten eine dringend benötigte Schicht an Politur zu verleihen.

Ich habe Live-Demos auf CodePen und GitHub-Repos vorbereitet, die zu diesem Artikel gehören.

Dieser Beitrag befasst sich mit Vue.js und den Werkzeugen, die es mit seinem Übergangssystem bietet. Es wird davon ausgegangen, dass Sie bereits mit den Grundlagen von Vue.js und CSS-Übergängen vertraut sind. Der Einfachheit und Klarheit halber werden wir nicht auf die "Logik" eingehen, die in der Demo verwendet wird.

Umgang mit Vue.js-Übergängen & Animationen

Animationen & Übergänge können Ihre Website zum Leben erwecken und Benutzer zum Erkunden anregen. Animationen und Übergänge sind ein integraler Bestandteil des UX- und UI-Designs. Sie sind jedoch leicht falsch zu machen. In komplexen Situationen wie beim Umgang mit Listen kann es fast unmöglich sein, sie zu verstehen, wenn man sich auf natives JavaScript und CSS verlässt. Immer wenn ich Backend-Entwickler frage, warum sie das Frontend so vehement verabscheuen, ist ihre Antwort normalerweise so etwas wie: "… Animationen".

Selbst für diejenigen von uns, die sich vom Feld durch den Drang angezogen fühlen, komplexe Mikrointeraktionen und flüssige Seitenübergänge zu schaffen, ist es keine leichte Arbeit. Wir müssen uns oft aus Performance-Gründen auf CSS verlassen, auch wenn wir uns in einer überwiegend JavaScript-Umgebung befinden, und dieser Bruch in der Umgebung kann schwierig zu handhaben sein.

Hier kommen Frameworks wie Vue.js ins Spiel und nehmen das Rätselraten und die umständlichen Ketten von setTimeout-Funktionen aus den Übergängen.

Der Unterschied zwischen Übergängen und Animationen

Die Begriffe Übergang und Animation werden oft synonym verwendet, sind aber tatsächlich unterschiedliche Dinge.

  • Ein transition ist eine Änderung der Stileigenschaften eines Elements, die in einem einzigen Schritt übergehen soll. Sie werden oft rein über CSS gehandhabt.
  • Eine animation ist komplexer. Sie sind normalerweise mehrstufig und laufen manchmal kontinuierlich. Animationen rufen oft JavaScript auf, um dort anzuknüpfen, wo die Logik von CSS aufhört.

Das kann verwirrend sein, da das Hinzufügen einer Klasse der Auslöser für einen Übergang oder eine Animation sein kann. Dennoch ist es eine wichtige Unterscheidung, wenn man sich in die Welt von Vue begibt, da beide sehr unterschiedliche Ansätze und Werkzeugkästen haben.

Hier ist ein Beispiel für Übergänge auf der Spektrum-Website

Übergänge verwenden

Der einfachste Weg, Übergangseffekte auf Ihrer Seite zu erzielen, ist die <transition>-Komponente von Vue. Sie macht die Dinge so einfach, dass es fast wie schummeln ist. Vue erkennt, ob CSS-Animationen oder Übergänge verwendet werden, und schaltet automatisch Klassen auf dem zu übertragenden Inhalt um, was ein perfekt getimtes Übergangssystem und vollständige Kontrolle ermöglicht.

Der erste Schritt ist die Identifizierung unseres Geltungsbereichs. Wir sagen Vue, dass es die Übergangsklassen mit modal präfixieren soll, indem wir das Attribut name der Komponente setzen. Dann, um einen Übergang auszulösen, müssen Sie nur noch die Sichtbarkeit des Inhalts mit den Attributen v-if oder v-show umschalten. Vue fügt die Klassen entsprechend hinzu/entfernt sie.

Es gibt zwei "Richtungen" für Übergänge: enter (für ein Element, das von unsichtbar zu sichtbar wechselt) und leave (für ein Element, das von sichtbar zu unsichtbar wechselt). Vue bietet dann 3 "Hooks", die verschiedene Zeiträume im Übergang darstellen

  • .modal-enter-active / .modal-leave-active: Diese sind während des gesamten Übergangs vorhanden und sollten verwendet werden, um Ihre CSS-Übergangserklärung anzuwenden. Sie können auch Stile deklarieren, die von Anfang bis Ende angewendet werden müssen.
  • .modal-enter / .modal-leave: Verwenden Sie diese Klassen, um zu definieren, wie Ihr Element vor dem Beginn des Übergangs aussieht.
  • .modal-enter-to / .modal-leave-to: Sie haben es wahrscheinlich bereits erraten, diese bestimmen die Stile, zu denen Sie übergehen möchten, den "vollständigen" Zustand.

Um den gesamten Prozess zu visualisieren, werfen Sie einen Blick auf diese Grafik aus der Vue-Dokumentation

Wie übersetzt sich das in Code? Sagen wir, wir wollen einfach rein und raus blenden, dann würde das Zusammensetzen der Teile so aussehen

<button class="modal__open" @click="modal = true">Help</button>

<transition name="modal">
  <section v-if="modal" class="modal">
    <button class="modal__close" @click="modal = false">&times;</button>
  </section>
</transition>
.modal-enter-active,
.modal-leave-active { transition: opacity 350ms }

.modal-enter,
.modal-leave-to { opacity: 0 }

.modal-leave,
.modal-enter-to { opacity: 1 }

Dies ist wahrscheinlich die grundlegendste Implementierung, auf die Sie stoßen werden. Beachten Sie, dass dieses Übergangssystem auch Inhaltsänderungen verarbeiten kann. Sie könnten beispielsweise auf eine Änderung von Vues dynamischem <component> reagieren.

<transition name="slide">
  <component :is="selectedView" :key="selectedView"/>
</transition>
.slide-enter { transform: translateX(100%) }
.slide-enter-to { transform: translateX(0) }
.slide-enter-active { position: absolute }

.slide-leave { transform: translateX(0) }
.slide-leave-to { transform: translateX(-100%) }

.slide-enter-active,
.slide-leave-active { transition: all 750ms ease-in-out }

Immer wenn sich selectedView ändert, gleitet die alte Komponente nach links hinaus und die neue kommt von rechts herein!

Hier ist eine Demo, die diese Konzepte nutzt

See the Pen VueJS transition & transition-group demo by Nicolas Udy (@udyux) on CodePen.

Übergänge bei Listen

Die Dinge werden interessant, wenn wir mit Listen arbeiten. Sei es eine Aufzählung oder ein Raster von Blogbeiträgen, Vue bietet Ihnen die <transition-group>-Komponente.

Es ist erwähnenswert, dass die <transition>-Komponente zwar kein Element rendert, <transition-group> jedoch schon. Das Standardverhalten ist die Verwendung eines <span>, aber Sie können dies überschreiben, indem Sie das Attribut tag auf der <transition-group> setzen.

Der andere Stolperstein ist, dass alle Listenelemente ein eindeutiges key-Attribut haben müssen. Vue kann dann jedes Element einzeln verfolgen und seine Leistung optimieren. In unserer Demo durchlaufen wir die Liste der Unternehmen, von denen jedes eine eindeutige ID hat. Wir können unsere Liste also wie folgt einrichten

<transition-group name="company" tag="ul" class="content__list">
  <li class="company" v-for="company in list" :key="company.id">
    <!-- ... -->
  </li>
</transition-group>

Das beeindruckendste Merkmal von transition-group ist, wie nahtlos Vue Änderungen in der Reihenfolge der Liste handhabt. Dafür steht eine zusätzliche Übergangsklasse zur Verfügung, .company-move (ähnlich den active-Klassen für das Ein- und Ausblenden), die auf Listenelemente angewendet wird, die sich bewegen, aber sichtbar bleiben.

In der Demo habe ich es etwas detaillierter aufgeschlüsselt, um zu zeigen, wie man verschiedene Zustände nutzt, um ein saubereres Endergebnis zu erzielen. Hier ist eine vereinfachte und übersichtliche Version der Stile

/* base */
.company {
  backface-visibility: hidden;
  z-index: 1;
}

/* moving */
.company-move {
  transition: all 600ms ease-in-out 50ms;
}

/* appearing */
.company-enter-active {
  transition: all 300ms ease-out;
}

/* disappearing */
.company-leave-active {
  transition: all 200ms ease-in;
  position: absolute;
  z-index: 0;
}

/* appear at / disappear to */
.company-enter,
.company-leave-to {
  opacity: 0;
}

Die Verwendung von backface-visibility: hidden auf einem Element, selbst in Abwesenheit von 3D-Transformationen, sorgt für seidige 60fps-Übergänge und vermeidet unscharfe Textdarstellung während Transformationen, indem der Browser dazu gebracht wird, Hardwarebeschleunigung zu nutzen.

Im obigen Snippet habe ich den Basisstil auf z-index: 1 gesetzt. Dies stellt sicher, dass Elemente, die auf der Seite bleiben, immer über Elementen erscheinen, die ausgeblendet werden. Ich wende auch eine absolute-Positionierung auf Elemente an, die ausgeblendet werden, um sie aus dem natürlichen Fluss zu entfernen und für die restlichen Elemente den move-Übergang auszulösen.

Das ist alles, was wir brauchen! Das Ergebnis ist, ehrlich gesagt, fast magisch.

Animationen verwenden

Die Möglichkeiten und Ansätze für Animationen in Vue sind praktisch endlos, daher habe ich eine meiner Lieblingstechniken gewählt, um zu zeigen, wie Sie Ihre Daten animieren könnten.

Wir werden die TweenLite-Bibliothek von GSAP verwenden, um Easing-Funktionen auf die Änderungen unseres Zustands anzuwenden, und Vues blitzschnelle Reaktivität dies im DOM widerspiegeln lassen. Vue ist genauso komfortabel mit Inline-SVG wie mit HTML.

Wir werden ein Liniendiagramm mit 5 Punkten erstellen, die gleichmäßig entlang der X-Achse verteilt sind und deren Y-Achse einen Prozentsatz darstellt. Sie können sich hier das Ergebnis ansehen.

See the Pen SVG path animation with VueJS & TweenLite by Nicolas Udy (@udyux) on CodePen.

Beginnen wir mit der Logik unserer Komponente.

new Vue({
  el: '#app',
  // this is the data-set that will be animated
  data() {
    return {
      points: { a: -1, b: -1, c: -1, d: -1, e: -1 }
    }
  },

  // this computed property builds an array of coordinates that
  // can be used as is in our path
  computed: {
    path() {
      return Object.keys(this.points)
        // we need to filter the array to remove any
        // properties TweenLite has added
        .filter(key => ~'abcde'.indexOf(key))
        // calculate X coordinate for 5 points evenly spread
        // then reverse the data-point, a higher % should
        // move up but Y coordinates increase downwards
        .map((key, i) => [i * 100, 100 - this.points[key]])
    }
  },

  methods: {
    // our randomly generated destination values
    // could be replaced by an array.unshift process
    setPoint(key) {
      let duration = this.random(3, 5)
      let destination = this.random(0, 100)
      this.animatePoint({ key, duration, destination })
    },
    // start the tween on this given object key and call setPoint
    // once complete to start over again, passing back the key
    animatePoint({ key, duration, destination }) {
      TweenLite.to(this.points, duration, {
        [key]: destination,
        ease: Sine.easeInOut,
        onComplete: this.setPoint,
        onCompleteParams: [key]
      })
    },
    random(min, max) {
      return ((Math.random() * (max - min)) + min).toFixed(2)
    }
  },

  // finally, trigger the whole process when ready
  mounted() {
    Object.keys(this.points).forEach(key => {
      this.setPoint(key)
    })
  }
});

Nun zur Vorlage.

<main id="app" class="chart">
  <figure class="chart__content">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="-20 -25 440 125">
      <path class="chart__path" :d="`M${path}`"
        fill="none" stroke="rgba(255, 255, 255, 0.3)"
        stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>

      <text v-for="([ x, y ]) in path" :x="x - 10" :y="y - 7.5"
        font-size="10" font-weight="200" fill="currentColor">
        {{ 100 - (y | 0) + '%' }}
      </text>
    </svg>
  </figure>
</main>

Beachten Sie, wie wir unsere berechnete Eigenschaft path an das d-Attribut des Pfadelements binden. Etwas Ähnliches tun wir mit den Textknoten, die den aktuellen Wert für diesen Punkt ausgeben. Wenn TweenLite die Daten aktualisiert, reagiert Vue sofort und hält das DOM synchron.

Das ist im Grunde alles! Natürlich wurden zusätzliche Stile angewendet, um die Dinge hübsch zu machen, was Sie zu diesem Zeitpunkt erkennen könnten, ist mehr Arbeit als die Animation selbst!

Live-Demos (CodePen) & GitHub-Repo

Gehen Sie ruhig die Live-Demos durch oder analysieren/verwenden Sie den Code in unserem Open-Source-Repo!

Fazit

Ich war schon immer ein Fan von Animationen und Übergängen im Web, aber ich bin auch ein Verfechter der Leistung. Daher bin ich immer sehr vorsichtig, wenn es darum geht, sich auf JavaScript zu verlassen. Wenn man jedoch die blitzschnelle und kostengünstige Reaktivität von Vue mit seiner Fähigkeit, reine CSS-Übergänge zu verwalten, kombiniert, müsste man schon sehr über die Stränge schlagen, um Leistungsprobleme zu haben.

Es ist beeindruckend, dass ein so leistungsfähiges Framework eine so einfache, aber beherrschbare API bieten kann. Die Animationsdemo, einschließlich der Stile, wurde in nur 45 Minuten erstellt. Und wenn man die Zeit für die Einrichtung der Mock-Daten für den Listenübergang abzieht, ist sie in unter 2 Stunden machbar. Ich will mir den Migräne-induzierenden Prozess, ähnliche Setups ohne Vue zu erstellen, gar nicht erst vorstellen, geschweige denn, wie viel Zeit das dauern würde!

Jetzt legen Sie los und werden Sie kreativ! Die Anwendungsfälle gehen weit über das hinaus, was wir in diesem Beitrag gesehen haben: die einzigste wirkliche Begrenzung ist Ihre Vorstellungskraft. Vergessen Sie nicht, den Abschnitt über Übergänge und Animationen in der Dokumentation von Vue.js für weitere Informationen und Inspiration zu konsultieren.


Dieser Beitrag erschien ursprünglich auf Snipcarts Blog. Haben Sie Kommentare, Fragen? Fügen Sie sie unten hinzu!