Maison > interface Web > tutoriel CSS > Aller «Meta GSAP»: la quête d'un défilement infini «parfait»

Aller «Meta GSAP»: la quête d'un défilement infini «parfait»

Joseph Gordon-Levitt
Libérer: 2025-03-25 10:23:10
original
224 Les gens l'ont consulté

Aller «Meta GSAP»: la quête d'un défilement infini «parfait»

Je ne sais pas comment celui-ci est né. Mais c'est une histoire. Cet article concerne davantage Grokking a Concept, celui qui va vous aider à penser à vos animations d'une manière différente. Il se trouve que cet exemple particulier présente un défilement infini - en particulier le parchemin infini «parfait» pour un jeu de cartes sans reproduire aucun d'entre eux.

Pourquoi suis-je ici? Eh bien, tout a commencé à partir d'un tweet. Un tweet qui m'a fait réfléchir sur les dispositions et le contenu de défilement latéral.

J'ai pris ce concept et je l'ai utilisé sur mon site. Et il est toujours là en action au moment de la rédaction.

Ensuite, j'ai pu réfléchir davantage aux vues des galeries et aux concepts de défilement latéral. Nous avons sauté sur un flux en direct et avons décidé d'essayer de faire quelque chose comme l'ancien modèle de «flux de couverture» de pomme. Vous vous en souvenez?

Mes premières pensées pour faire en sorte que cela supposais que je le ferais pour que cela fonctionne sans JavaScript, comme il le fait dans la démo ci-dessus, d'une manière qui utilise une «amélioration progressive». J'ai attrapé Greensock et Scrolltrigger, et nous sommes partis. Je suis sorti de ce travail assez déçu. J'avais quelque chose mais je ne pouvais pas vraiment faire de défilement infini pour fonctionner comme je le voulais. Les boutons «suivants» et «précédents» ne voulaient pas jouer au ballon. Vous pouvez le voir ici, et cela nécessite un défilement horizontal.

J'ai donc ouvert un nouveau fil sur le forum Greensock. Je ne savais pas que j'étais sur le point de m'ouvrir à un apprentissage sérieux! Nous avons résolu le problème avec les boutons. Mais, étant moi, j'ai dû me demander si quelque chose d'autre était possible. Y avait-il une façon «propre» de faire du défilement infini? J'avais essayé quelque chose sur Stream mais n'avait pas de chance. J'étais curieux. J'avais essayé une technique comme celle utilisée dans ce stylo que j'ai créé pour la version ScrollTrigger.

La réponse initiale a été que c'est un peu difficile à faire:

La partie difficile des choses infinies sur Scroll est que la barre de défilement est limitée tandis que l'effet que vous voulez n'est pas. Vous devez donc faire bouclez la position de défilement comme cette démo (trouvée dans la section démos de défilement) ou accrochez directement aux événements de navigation liés au défilement (comme l'événement de roue) au lieu d'utiliser réellement la position de défilement réelle.

J'ai pensé que c'était le cas et j'étais heureux de le laisser «tel quel». Quelques jours se sont écoulés et Jack a laissé tomber une réponse qui m'a un peu époustouflé lorsque j'ai commencé à y creuser. Et maintenant, après un tas de traverser, je suis ici pour partager la technique avec vous.

Animer n'importe quoi

Une chose qui est souvent négligée avec GSAP, c'est que vous pouvez animer presque tout. C'est souvent parce que les choses visuelles vont à l'esprit lorsque l'on pense à l'animation - le mouvement physique réel de quelque chose. Notre première pensée ne consiste pas à prendre ce processus à un niveau de méta et à l'animation à partir d'un pas en arrière.

Mais, pensez au travail d'animation à plus grande échelle, puis décomposez-le en couches. Par exemple, vous jouez un dessin animé. Le dessin animé est une collection de compositions. Chaque composition est une scène. Et puis vous avez le pouvoir de frotter cette collection de compositions avec une télécommande, que ce soit sur YouTube, en utilisant votre télécommande TV ou autre. Il y a presque trois niveaux à ce qui se passe.

Et c'est l'astuce dont nous avons besoin pour créer différents types de boucles infinies . C'est le concept principal ici. Nous animons la position de la tête de jeu d'une chronologie avec une chronologie. Et puis nous pouvons frotter cette chronologie avec notre position de défilement.

Ne vous inquiétez pas si cela semble déroutant. Nous allons le décomposer.

Aller "Meta"

Commençons par un exemple. Nous allons créer un tween qui déplace certaines boîtes de gauche à droite. C'est ici.

Dix boîtes qui continuent de gauche à droite. C'est assez simple avec Greensock. Ici, nous utilisons depuis et répétons pour maintenir l'animation. Mais, nous avons un écart au début de chaque itération. Nous utilisons également Stagger pour espacer le mouvement et c'est quelque chose qui jouera un rôle important à mesure que nous continuerons.

 gsap.fromto ('. Box', {
  xpercent: 100
}, {
  xpercent: -200,
  Stagger: 0,5,
  Durée: 1,
  répéter: -1,
  facilité: «aucun»,
})
Copier après la connexion

Vient maintenant la partie amusante. Arrêtons la tween et attribuons-le à une variable. Créons ensuite un tween qui le joue. Nous pouvons le faire en tweening le temps total de la Tween, ce qui nous permet d'obtenir ou de définir la Tween Tween de la tête de jeu, tout en considérant les répétitions et les retards de répétition.

 const shift = gsap.fromto ('. Box', {
  xpercent: 100
}, {
  pause: vrai,
  xpercent: -200,
  Stagger: 0,5,
  Durée: 1,
  répéter: -1,
  facilité: «aucun»,
})

const Durée = Shift.Duration ()

gsap.to (shift, {
  Total temps: durée,
  répéter: -1,
  Durée: durée,
  facilité: «aucun»,
})
Copier après la connexion

Ceci est notre premier tween «méta». Il est exactement la même chose, mais nous ajoutons un autre niveau de contrôle. Nous pouvons changer les choses sur cette couche sans affecter la couche d'origine. Par exemple, nous pourrions changer la facilité de Tween en Power4.in. Cela change complètement l'animation mais sans affecter l'animation sous-jacente. Nous nous protégeons un peu avec un repli.

Non seulement cela, nous pourrions choisir de répéter une certaine partie de la chronologie. Nous pourrions faire ça avec un autre de de la part, comme ceci:

Le code pour cela serait quelque chose comme ça.

 gsap.fromto (shift, {
  Total temps: 2,
}, {
  Total temps: durée - 1,
  répéter: -1,
  Durée: durée,
  facilité: 'Aucun'
})
Copier après la connexion

Voyez-vous où cela va? Regardez ce tween. Bien qu'il continue à boucler, les chiffres retournent à chaque répétition. Mais, les boîtes sont dans la bonne position.

Atteindre la boucle «parfaite»

Si nous revenons à notre exemple d'origine, il y a un écart notable entre chaque répétition.

Voici l'astuce. La partie qui déverrouille tout. Nous devons construire une boucle parfaite.

Commençons par répéter le changement trois fois. Il est égal à l'utilisation de la répétition: 3. Remarquez comment nous avons supprimé la répétition: -1 de la Tween.

 const getShift = () => gsap.fromto ('. Box', {
  xpercent: 100
}, {
  xpercent: -200,
  Stagger: 0,5,
  Durée: 1,
  facilité: «aucun»,
})

const Loop = gsap.Timeline ()
  .add (getShift ())
  .add (getShift ())
  .add (getShift ())
Copier après la connexion

Nous avons transformé le Tween initial en une fonction qui renvoie le Tween et nous l'ajoutons trois fois à une nouvelle chronologie. Et cela nous donne ce qui suit.

D'ACCORD. Mais, il y a encore un écart. Maintenant, nous pouvons apporter le paramètre de position pour ajouter et positionner ces préadolescents. Nous voulons que ce soit transparent. Cela signifie insérer chaque ensemble de préadolescents avant la fin de la précédente. C'est une valeur basée sur le Stagger et la quantité d'éléments.

 const Stagger = 0,5 // utilisé dans notre tween changeant
const Boxes = gsap.utils.toarray ('. Box')
const Loop = gsap.Timeline ({
  répéter: -1
})
  .add (getShift (), 0)
  .add (getShift (), boxes.length * Stagger)
  .add (getShift (), boxes.length * Stagger * 2)
Copier après la connexion

Si nous mettons à jour notre chronologie pour le répéter et le regarder (tout en ajustant le Stagger pour voir comment cela affecte les choses)…

Vous remarquerez qu'il y a une fenêtre au milieu qui crée une boucle «transparente». Rappelez-vous ces compétences de plus tôt où nous avons manipulé le temps? C'est ce que nous devons faire ici: boucle la fenêtre du temps où la boucle est «sans couture».

Nous pourrions essayer de faire du temps total à travers cette fenêtre de la boucle.

 const Loop = gsap.Timeline ({
  pause: vrai,
  répéter: -1,
})
.add (getShift (), 0)
.add (getShift (), boxes.length * Stagger)
.add (getShift (), boxes.length * Stagger * 2)

gsap.fromto (boucle, {
  Total temps: 4,75,
},
{
  Total temps: '= 5',
  Durée: 10,
  facilité: «aucun»,
  répéter: -1,
})
Copier après la connexion

Ici, nous disons que le temps total de 4,75 et ajoutez la durée d'un cycle à cela. La longueur d'un cycle est de 5. Et c'est la fenêtre centrale de la chronologie. Nous pouvons utiliser Nifty = GSAP pour le faire, ce qui nous donne ceci:

Prenez un moment pour digérer ce qui se passe là-bas. Cela pourrait être la partie la plus délicate pour envelopper votre tête. Nous calculons des fenêtres de temps dans notre chronologie. C'est un peu difficile à visualiser mais j'ai essayé.

Il s'agit d'une démo d'une montre qui prend 12 secondes pour que les mains tournent une fois. Il est en boucle à l'infini avec la répétition: -1, puis nous utilisons depuis pour animer une fenêtre de temps spécifique avec une durée donnée. Si vous, réduisez la fenêtre temporelle pour dire 2 et 6, puis modifiez la durée en 1, les mains passeront de 2 heures à 6 heures à répéter. Mais, nous n'avons jamais changé l'animation sous-jacente.

Essayez de configurer les valeurs pour voir comment cela affecte les choses.

À ce stade, c'est une bonne idée de préparer une formule pour notre position de fenêtre. Nous pourrions également utiliser une variable pour la durée nécessaire à chaque case pour passer.

 Durée const = 1
const Cycle_Duration = Boxes.Length * Stagger
const start_time = cycle_duration (durée * 0,5)
const end_time = start_time cycle_duration
Copier après la connexion

Au lieu d'utiliser trois chronologies empilées, nous pourrions faire bouclez nos éléments trois fois où nous bénéficions de ne pas avoir besoin de calculer les positions. Visualiser cela comme trois chronologies empilées est une façon intéressante de grok le concept, cependant, et une belle façon d'aider à comprendre l'idée principale.

Changeons notre implémentation pour créer une grande chronologie dès le début.

 const Stagger = 0,5
const Boxes = gsap.utils.toarray ('. Box')

const Loop = gsap.Timeline ({
  pause: vrai,
  répéter: -1,
})

const Shifts = [... Boîtes, ... Boîtes, ... Boîtes]

Shifts.ForEach ((box, index) => {
  LOOP.FRAMO (Box, {
    xpercent: 100
  }, {
    xpercent: -200,
    Durée: 1,
    facilité: «aucun»,
  }, index * Stagger)
})
Copier après la connexion

Ceci est plus facile à assembler et nous donne la même fenêtre. Mais, nous n'avons pas besoin de penser aux mathématiques. Maintenant, nous parcourons trois ensembles de boîtes et positionnons chaque animation selon The Stagger.

À quoi cela pourrait-il ressembler si nous ajustons le Stagger? Il écrasera les boîtes plus près les unes des autres.

Mais, il est cassé la fenêtre parce que maintenant le temps total est sorti. Nous devons recalculer la fenêtre. C'est le bon moment pour brancher la formule que nous avons calculée plus tôt.

 Durée const = 1
const Cycle_Duration = Stagger * Boxes.Length
const start_time = cycle_duration (durée * 0,5)
const end_time = start_time cycle_duration

gsap.fromto (boucle, {
  TotalTime: start_time,
},
{
  TotalTime: end_time,
  Durée: 10,
  facilité: «aucun»,
  répéter: -1,
})
Copier après la connexion

Fixé!

Nous pourrions même introduire un «décalage» si nous voulions modifier la position de départ.

 const Stagger = 0,5
const offset = 5 * Stagger
const start_time = (cycle_duration (stunger * 0,5)) décalage
Copier après la connexion

Maintenant, notre fenêtre part à partir d'une position différente.

Mais encore, ce n'est pas génial car cela nous donne ces piles maladroites à chaque extrémité. Pour nous débarrasser de cet effet, nous devons réfléchir à une fenêtre «physique» pour nos boîtes. Ou pensez à la façon dont ils entrent et quittent la scène.

Nous allons utiliser document.body comme fenêtre pour notre exemple. Mettons à jour les Tweens de la boîte pour être des délais individuels où les cases augmentent en entrée et en descente à la sortie. Nous pouvons utiliser Yoyo et répéter: 1 pour réaliser l'entrée et la sortie.

 Shifts.ForEach ((box, index) => {
  const box_tl = gsap
    .chronologie()
    .fromto (
      BOÎTE,
      {
        Xpercent: 100,
      },
      {
        xpercent: -200,
        Durée: 1,
        facilité: «aucun»,
      }, 0
    )
    .fromto (
      BOÎTE,
      {
        Échelle: 0,
      },
      {
        Échelle: 1,
        répéter: 1,
        yoyo: vrai,
        facilité: «aucun»,
        Durée: 0,5,
      },
      0
    )
  Loop.add (box_tl, index * Stagger)
})
Copier après la connexion

Pourquoi utilisons-nous une durée de chronologie de 1? Cela rend les choses plus faciles à suivre. Nous savons que le temps est de 0,5 lorsque la boîte est au milieu. Il convient de noter que l'assouplissement n'aura pas l'effet à laquelle nous pensons habituellement ici. En fait, l'assouplissement jouera en fait un rôle dans la façon dont les boîtes se positionnent. Par exemple, une facilité-in enroulerait les boîtes à droite avant de passer à travers.

Le code ci-dessus nous donne cela.

Presque. Mais, nos boîtes disparaissent pendant un certain temps au milieu. Pour résoudre ce problème, introduisons la propriété immédiatement. Il agit comme un mode d'animation: aucun dans CSS. Nous disons à GSAP que nous ne voulons pas conserver ou pré-enregistrer les styles qui sont définis sur une boîte.

 Shifts.ForEach ((box, index) => {
  const box_tl = gsap
    .chronologie()
    .fromto (
      BOÎTE,
      {
        Xpercent: 100,
      },
      {
        xpercent: -200,
        Durée: 1,
        facilité: «aucun»,
        immédiatementRender: faux,
      }, 0
    )
    .fromto (
      BOÎTE,
      {
        Échelle: 0,
      },
      {
        Échelle: 1,
        répéter: 1,
        Zindex: Boxes.Length 1,
        yoyo: vrai,
        facilité: «aucun»,
        Durée: 0,5,
        immédiatementRender: faux,
      },
      0
    )
  Loop.add (box_tl, index * Stagger)
})
Copier après la connexion

Ce petit changement répare les choses pour nous! Notez comment nous avons également inclus Z-index: Boxes.Length. Cela devrait nous sauvegarder contre les problèmes d'index z.

Là, nous l'avons! Notre première boucle infinie sans couture. Pas d'éléments en double et de continuation parfaite. Nous plions du temps! Pat-toi dans le dos si tu es allé jusqu'ici! ?

Si nous voulons voir plus de boîtes à la fois, nous pouvons bricoler le timing, l'allumage et la facilité. Ici, nous avons une échelle de 0,2 et nous avons également introduit l'opacité dans le mélange.

La partie clé ici est que nous pouvons utiliser RepealDelay afin que la transition d'opacité soit plus rapide que l'échelle. S'estomper en plus de 0,25 seconde. Attendez 0,5 seconde. Fade reculer sur 0,25 seconde.

 .fromto (
  BOÎTE, {
    Opacité: 0,
  }, {
    Opacité: 1,
    Durée: 0,25,
    répéter: 1,
    Repeatdelay: 0,5,
    immédiatementRender: faux,
    facilité: «aucun»,
    yoyo: vrai,
  }, 0)
Copier après la connexion

Cool! Nous pourrions faire ce que nous voulons avec les transitions dans et hors des transitions. L'essentiel ici est que nous avons notre fenêtre de temps qui nous donne la boucle infinie.

Accrocher cela pour faire défiler

Maintenant que nous avons une boucle transparente, attachons-la à faire défiler. Pour cela, nous pouvons utiliser Scrolltrigger de GSAP. Cela nécessite un tween supplémentaire pour frotter notre fenêtre en boucle. Notez comment nous avons également défini la boucle maintenant.

 const loop_head = gsap.fromto (Loop, {
  TotalTime: start_time,
},
{
  TotalTime: end_time,
  Durée: 10,
  facilité: «aucun»,
  répéter: -1,
  pause: vrai,
})

const smouch = gsap.to (loop_head, {{
  Total temps: 0,
  pause: vrai,
  Durée: 1,
  facilité: «aucun»,
})
Copier après la connexion

L'astuce ici est d'utiliser Scrolltrigger pour frotter la tête de jeu de la boucle en mettant à jour le temps total du gommage. Il existe différentes façons de mettre en place ce parchemin. Nous pourrions l'avoir horizontal ou lié à un conteneur. Mais ce que nous allons faire, c'est envelopper nos boîtes dans un élément .boxes et épingler à la fenêtre. (Cela corrige sa position dans la fenêtre.) Nous resterons également avec le défilement vertical. Vérifiez la démo pour voir le style des cases qui définissent les choses sur la taille de la fenêtre.

 Importez ScrollTrigger à partir de 'https://cdn.skypack.dev/gsap/scrolltrigger'
gsap.registerPlugin (Scrolltrigger)

Scrolltrigger.create ({
  Démarrer: 0,
  fin: '= 2000',
  horizontal: faux,
  Pin: «.bodées»,
  onupdate: self => {
    Scrud.vars.totaltime = loop_head.Duration () * self.progress
    Scrub.invalidate (). Restart ()
  }
})
Copier après la connexion

La partie importante est à l'intérieur de l'onupdate. C'est là que nous avons réglé le temps total des Tween en fonction des progrès de défilement. L'appel invalidate rince toutes les positions enregistrées en interne pour le gommage. Le redémarrage définit ensuite la position sur le nouveau temps total que nous avons défini.

Essayez-le! Nous pouvons faire des allers-retours dans la chronologie et mettre à jour la position.

À quel point est-ce cool? Nous pouvons faire défiler pour frotter une chronologie qui frotte une chronologie qui est une fenêtre d'une chronologie. Digérez cela une seconde parce que c'est ce qui se passe ici.

Voyage dans le temps pour le défilement infini

Jusqu'à présent, nous avons manipulé le temps. Maintenant, nous allons voyager dans le temps!

Pour ce faire, nous allons utiliser d'autres utilitaires GSAP et nous ne frotterons plus le temps total de Loop_head. Au lieu de cela, nous allons le mettre à jour via Proxy. Ceci est un autre excellent exemple de la «méta» GSAP.

Commençons par un objet proxy qui marque la position de la tête de jeu.

 const de playhead = {position: 0}
Copier après la connexion

Nous pouvons maintenant mettre à jour notre gommage pour mettre à jour le poste. Dans le même temps, nous pouvons utiliser l'utilitaire WRAP de GSAP, qui enveloppe la valeur de position autour de la durée LOOP_HEAD. Par exemple, si la durée est 10 et que nous fournissons la valeur 11, nous récupérerons 1.

 const position_wrap = gsap.utils.wrap (0, loop_head.Duration ())

const smouch = gsap.to (playhead, {{
  Position: 0,
  onupdate: () => {
    Loop_head.totaltime (position_wrap (playhead.position)))
  },
  pause: vrai,
  Durée: 1,
  facilité: «aucun»,
})
Copier après la connexion

Dernier point, mais non le moindre, nous devons réviser ScrollTrigger afin qu'il mette à jour la variable correcte sur le gommage. C'est la position, au lieu de TotalTime.

 Scrolltrigger.create ({
  Démarrer: 0,
  fin: '= 2000',
  horizontal: faux,
  Pin: «.bodées»,
  onupdate: self => {
    Scrud.vars.position = Loop_head.Duration () * self.progress
    Scrub.invalidate (). Restart ()
  }
})
Copier après la connexion

À ce stade, nous sommes passés à un proxy et nous ne verrons aucun changement.

Nous voulons une boucle infinie lorsque nous faisons défiler. Notre première pensée pourrait être de faire défiler jusqu'au début lorsque nous terminons les progrès de défilement. Et cela ferait exactement cela, faites défiler vers l'arrière. Bien que ce soit ce que nous voulons faire, nous ne voulons pas que la tête de jeu frappe à l'envers. C'est là que le temps total entre en jeu. Rappelez-vous? Il obtient ou définit la position de la tête de jeu en fonction du totalDuration qui comprend toutes les répétitions et les retards de répétition.

Par exemple, disons que la durée de la tête de boucle était de 5 et que nous y sommes arrivés, nous ne rentrerons pas à 0. Au lieu de cela, nous continuerons à nettoyer la tête de boucle à 10. Si nous continuons, cela ira à 15, etc. En attendant, nous garderons une trace d'une variable d'itération car cela nous dit où nous sommes dans le gommage. Nous nous assurerons également de mettre à jour l'itération uniquement lorsque nous atteindrons les seuils de progression.

Commençons par une variable d'itération:

 Laissez itération = 0
Copier après la connexion

Mettons à jour notre implémentation ScrollTrigger:

 const-trigger = scrolltrigger.create ({{
  Démarrer: 0,
  fin: '= 2000',
  horizontal: faux,
  Pin: «.bodées»,
  onupdate: self => {
    const Scroll = self.scroll ()
    if (Scroll> self.end - 1) {
      // avance à temps
      Envelopper (1, 1)
    } else if (Scroll <p>Remarquez comment nous sommes maintenant en train de prendre en compte l'itération dans le calcul de la position. N'oubliez pas que cela est enveloppé avec l'épurateur. Nous détectons également lorsque nous atteignons les limites de notre parchemin, et c'est le point où nous enroulons. Cette fonction définit la valeur d'itération appropriée et définit la nouvelle position de défilement.</p><pre rel="JavaScript" data-line=""> const wrap = (itérationdelta, scrollto) => {
  itération = itérationdelta
  Trigger.scroll (ScrolltO)
  Trigger.update ()
}
Copier après la connexion

Nous avons un défilement infini! Si vous avez une de ces souris fantaisistes avec la roue de défilement sur laquelle vous pouvez lâcher, essayez-le! C'est amusant!

Voici une démo qui affiche l'itération et les progrès actuels:

Scroll Snapping

Nous sommes là. Mais, il y a toujours «agréable à faire des nantis» lorsque vous travaillez sur une fonctionnalité comme celle-ci. Commençons par faire un claquement de défilement. GSAP le rend facile, car nous pouvons utiliser gsap.utils.snap sans autre dépendance. Cela gère la claquement à un moment où nous fournissons les points. Nous déclarons l'étape entre 0 et 1 et nous avons 10 boîtes dans notre exemple. Cela signifie qu'un cliché de 0,1 fonctionnerait pour nous.

 const snap = gsap.utils.snap (1 / boxes.length)
Copier après la connexion

Et cela renvoie une fonction que nous pouvons utiliser pour casser notre valeur de position.

Nous ne voulons que se casser une fois que le parchemin s'est terminé. Pour cela, nous pouvons utiliser un écouteur d'événements sur ScrollTrigger. Lorsque le défilement se termine, nous allons faire défiler vers une certaine position.

 Scrolltrigger.addeventListener ('scrollend', () => {
  ScrollToPosition (Scrub.vars.Sposition)
})
Copier après la connexion

Et voici ScrollToposition:

 const ScrollToPosition = position => {
  const snap_pos = snap (position)
  const progress =
    (Snap_pos - loop_head.duration () * itération) / loop_head.Duration ()
  const Scroll = ProgressToscroll (Progress)
  Trigger.scroll (Scroll)
}
Copier après la connexion

Que faisons-nous ici?

  1. Calculer le point dans le temps pour se casser à
  2. Calculer les progrès actuels. Disons que LOOP_HEAD.Duration () est 1 et que nous avons passé à 2,5. Cela nous donne un progrès de 0,5 entraînant une itération de 2, où 2,5 - 1 * 2/1 === 0,5. Nous calculons les progrès afin qu'il soit toujours compris entre 1 et 0.
  3. Calcul de la destination de défilement. Il s'agit d'une fraction de la distance que notre défilement peut couvrir. Dans notre exemple, nous avons fixé une distance de 2000 et nous en voulons une fraction. Nous créons une nouvelle fonction ProgressToscroll pour le calculer.
 const progressToscroll = progress =>
  gsap.utils.clamp (1, Trigger.end - 1, gsap.utils.wrap (0, 1, progress) * Trigger.end)
Copier après la connexion

Cette fonction prend la valeur de progression et la mappe à la plus grande distance de défilement. Mais nous utilisons une pince pour nous assurer que la valeur ne peut jamais être 0 ou 2000. C'est important. Nous nous protégeons contre la prise de ces valeurs car cela nous mettrait dans une boucle infinie.

Il y a un peu à prendre là-dedans. Consultez cette démo qui affiche les valeurs mises à jour sur chaque instantané.

Pourquoi les choses sont-elles beaucoup plus vives? La durée et la facilité de frottement ont été modifiées. Une durée plus petite et une facilité plus punch nous donnent le snap.

 const smouch = gsap.to (playhead, {{
  Position: 0,
  onupdate: () => {
    Loop_head.totaltime (position_wrap (playhead.position)))
  },
  pause: vrai,
  Durée: 0,25,
  Facilité: «Power3»,
})
Copier après la connexion

Mais, si vous avez joué avec cette démo, vous remarquerez qu'il y a un problème. Parfois, lorsque nous nous enroulons à l'intérieur du cliché, la tête de jeu saute. Nous devons tenir compte de cela en nous assurant de s'enrouler lorsque nous nous cassons - mais seulement lorsque cela est nécessaire.

 const ScrollToPosition = position => {
  const snap_pos = snap (position)
  const progress =
    (Snap_pos - loop_head.duration () * itération) / loop_head.Duration ()
  const Scroll = ProgressToscroll (Progress)
  if (progress> = 1 || progress <p> Et maintenant, nous avons un défilement infini avec un claquement!</p><h3> Quoi de suivi?</h3><p> Nous avons terminé les bases d'un Solid Infinite Scroller. Nous pouvons en tirer parti pour ajouter des choses, comme les contrôles ou les fonctionnalités du clavier. Par exemple, cela pourrait être un moyen de connecter des boutons et des commandes de clavier «suivants» et «précédents». Tout ce que nous avons à faire est de manipuler le temps, non?</p><pre rel="JavaScript" data-line=""> const next = () => scrolltoposition (scrud.vars.position - (1 / boxes.length)))
const prev = () => scrolltoposition (scrud.vars.position (1 / boxes.length)))

// Flèche gauche et droite plus A et D
document.addeventListener ('keydown', événement => {
  if (event.KeyCode === 37 || event.KeyCode === 65) next ()
  if (event.KeyCode === 39 || event.KeyCode === 68) PREV ()
})

document.QuerySelect
document.QuerySelect
Copier après la connexion

Cela pourrait nous donner quelque chose comme ça.

Nous pouvons tirer parti de notre fonction ScrollToPosition et augmenter la valeur dont nous avons besoin.

C'est ça!

Vous voyez ça? GSAP peut animer plus que les éléments! Ici, nous avons plié et manipulé le temps pour créer un curseur infini presque parfait. Pas d'éléments en double, pas de gâchis et une bonne flexibilité.

Récapitulons ce que nous avons couvert:

  • Nous pouvons animer une animation. ?
  • Nous pouvons considérer le timing comme des outils de positionnement lorsque nous manipulons le temps.
  • Comment utiliser Scrolltrigger pour frotter une animation via le proxy.
  • Comment utiliser certains des utilitaires impressionnants de GSAP pour gérer la logique pour nous.

Vous pouvez maintenant manipuler le temps! ?

Ce concept de «méta» GSAP ouvre une variété de possibilités. Que pourriez-vous animer d'autre? Audio? Vidéo? Quant à la démo «couvre-housse», voici où cela est allé!

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal