Créez le carrousel parfait, 2e partie

Bienvenue dans la série de didacticiels Créer le carrousel parfait. Nous créons un carrousel accessible et agréable utilisant JavaScript et les capacités de physique, d'interpolation et de suivi des entrées de Popmotion..

Dans la première partie de notre didacticiel, nous avons examiné la manière dont Amazon et Netflix ont créé leurs carrousels et évalué les avantages et les inconvénients de leurs approches. Avec nos apprentissages, nous avons choisi une stratégie pour notre carrousel et mis en œuvre le défilement tactile en utilisant la physique..

Dans la deuxième partie, nous allons implémenter le défilement horizontal de la souris. Nous allons également examiner certaines techniques de pagination courantes et en appliquer une. Enfin, nous allons brancher une barre de progression qui indiquera la distance parcourue par le carrousel de l'utilisateur..

Vous pouvez restaurer votre point de sauvegarde en ouvrant ce CodePen, qui reprend là où nous l'avions laissé..

Défilement horizontal de la souris

Il est rare qu'un carrousel JavaScript respecte le défilement horizontal de la souris. C'est une honte: sur les ordinateurs portables et les souris qui implémentent le défilement horizontal basé sur la quantité de mouvement, c'est de loin le moyen le plus rapide de naviguer dans le carrousel. C'est aussi grave que d'obliger les utilisateurs tactiles à naviguer via des boutons plutôt que de glisser.

Heureusement, il peut être implémenté en quelques lignes de code. À la fin de votre carrousel fonction, ajouter un nouvel écouteur d'événement:

container.addEventListener ('wheel', onWheel);

Ci-dessous votre startTouchScroll événement, ajoutez une fonction stub appelée sur roue:

fonction onWheel (e) console.log (e.deltaX)

Maintenant, si vous passez la molette de défilement sur le carrousel et vérifiez le panneau de votre console, vous verrez la distance de la molette sur la sortie de l'axe des x..

Comme avec le toucher, si le mouvement de la roue est principalement vertical, la page doit défiler comme d’habitude. Si c'est horizontal, nous voulons capturer le mouvement de la roue et l'appliquer au carrousel. Donc, dans sur roue, remplace le console.log avec:

angle de const = calc.angle (x: e.deltaX, y: e.deltaY); si (angleIsVertical (angle)) retourne; e.stopPropagation (); e.preventDefault ();

Ce bloc de code arrêtera le défilement de page si le défilement est horizontal. Mettre à jour le décalage x de notre curseur est maintenant juste une question de prendre l'événement deltaX la propriété et l'ajoutant à notre courant sliderX valeur:

const newX = clampXOffset (sliderX.get () + - e.deltaX); sliderX.set (newX);

Nous réutilisons notre précédent clampXOffset fonction pour encapsuler ce calcul et s'assurer que le carrousel ne défile pas au-delà des limites mesurées.

Un événement réservé aux événements de défilement

Tout bon tutoriel traitant des événements d'entrée expliquera à quel point il est important de contrôler ces événements. En effet, les événements de défilement, de souris et de toucher peuvent tous se déclencher plus rapidement que la fréquence d'images du périphérique..

Vous ne voulez pas effectuer un travail inutile et gourmand en ressources, comme rendre le carrousel deux fois dans un cadre, car il s'agit d'un gaspillage de ressources et d'un moyen rapide de créer une interface peu dynamique..

Ce didacticiel n'a pas abordé ce sujet car les rendus fournis par Popmotion implémentent Framesync, un petit planificateur de travaux synchronisé avec des cadres. Cela signifie que vous pouvez appeler (v) => sliderRenderer.set ('x', v) plusieurs fois dans une rangée, et le rendu coûteux ne se produirait qu'une fois, sur l'image suivante.

Pagination

C'est le défilement terminé. Maintenant, nous devons insuffler de la vie dans les boutons de navigation jusqu'alors mal aimés.

Maintenant, ce tutoriel concerne l’interaction, alors n'hésitez pas à concevoir ces boutons comme vous le souhaitez. Personnellement, je trouve les flèches de direction plus intuitives (et totalement internationalisées par défaut!).

Comment devrait fonctionner la pagination?

Lors de la pagination du carrousel, il existe deux stratégies claires: article par article ou premier article obscurci. Il n’ya qu’une stratégie correcte, mais comme j’ai vu l’autre mise en œuvre si souvent, j’ai pensé que cela valait la peine d’expliquer. Pourquoi c'est incorrect.

1. Article par article

Mesurez simplement le décalage x de l'élément suivant dans la liste et animez l'étagère de ce montant. C'est un algorithme très simple qui, je suppose, est choisi pour sa simplicité plutôt que pour sa convivialité..

Le problème est que la plupart des écrans peuvent afficher plusieurs éléments à la fois et que tous les analysent avant d'essayer de naviguer..

Cela semble lent, voire frustrant. La seule situation dans laquelle ce serait un bon choix est si vous savoir les articles de votre carrousel ont la même largeur ou sont légèrement inférieurs à la surface visible.

Cependant, si nous examinons plusieurs éléments, nous ferons mieux d'utiliser la première méthode d'élément masqué:

2. Premier objet masqué

Cette méthode cherche simplement le premier article obscurci dans la direction où nous voulons déplacer le carrousel, prend sa x offset, puis fait défiler jusqu'à.

Ce faisant, nous prenons le nombre maximal de nouveaux éléments en supposant que l'utilisateur a vu tous ceux qui sont actuellement présents..

Etant donné que nous attirons plus d'éléments, le carrousel nécessite moins de clics pour naviguer. Une navigation plus rapide augmentera l'engagement et garantira que vos utilisateurs verront davantage de vos produits..

Auditeurs de l'événement

Premièrement, configurons nos auditeurs d’événements pour pouvoir commencer à jouer avec la pagination.

Nous devons d’abord sélectionner nos boutons précédents et suivants. Au Haut du carrousel fonction, ajouter:

const nextButton = container.querySelector ('. next'); const prevButton = container.querySelector ('. prev');

Puis au bas du carrousel fonction, ajouter les écouteurs d'événement:

nextButton.addEventListener ('click', gotoNext); prevButton.addEventListener ('click', gotoPrev);

Enfin, juste au-dessus de votre bloc d’écouteurs d’événements, ajoutez les fonctions réelles:

fonction goto (delta)  const gotoNext = () => goto (1); const gotoPrev = () => goto (-1);

aller à est la fonction qui va gérer toute la logique de la pagination. Il faut simplement un nombre qui représente la direction du voyage que nous souhaitons paginer. gotoNext et gotoPrev appelez simplement cette fonction avec 1 ou -1, respectivement.

Calculer une "page"

Un utilisateur peut faire défiler ce carrousel librement, et il y a n éléments à l'intérieur, et le carrousel pourrait être redimensionné. Le concept de page traditionnelle n'est donc pas directement applicable ici. Nous ne compterons pas le nombre de pages.

Au lieu de cela, lorsque le aller à fonction est appelée, nous allons regarder dans la direction de delta et trouvez le premier élément partiellement masqué. Cela deviendra le premier élément de notre prochaine "page".

La première étape consiste à obtenir le décalage x actuel de notre curseur et à l'utiliser avec toute la largeur visible du curseur pour calculer un décalage "idéal" vers lequel nous souhaitons faire défiler. Le décalage idéal est ce que nous aurait faites défiler jusqu'à si nous étions naïfs au contenu du curseur. C'est un bon endroit pour commencer à chercher notre premier article..

const currentX = sliderX.get (); laissez targetX = currentX + (- sliderVisibleWidth * delta);

Nous pouvons utiliser une optimisation effrontée ici. En fournissant notre targetX au clampXOffset fonction que nous avons faite dans le tutoriel précédent, nous pouvons voir si sa sortie est différente de targetX. Si c'est le cas, cela signifie que notre targetX est en dehors de nos limites de défilement, donc nous n'avons pas besoin de trouver l'élément le plus proche. Nous venons de faire défiler jusqu'à la fin.

const clampedX = clampXOffset (targetX); targetX = (targetX === clampedX)? findClosestItemOffset (targetX, delta): clampedX;

Trouver l'élément le plus proche

Il est important de noter que le code suivant fonctionne en supposant que tous les articles de votre carrousel ont la même taille. Sous cette hypothèse, nous pouvons faire des optimisations comme ne pas avoir à mesurer la taille de chaque article. Si vos articles sont différentes tailles, cela fera toujours un bon point de départ. 

Au dessus de votre aller à fonction, ajoutez le findClosestItemOffset fonction référencée dans le dernier extrait:

fonction findClosestItem (targetX, delta) 

Premièrement, nous devons savoir quelle est la largeur de nos articles et leur espacement. le Element.getBoundingClientRect () méthode peut fournir toutes les informations dont nous avons besoin. Pour largeur, nous mesurons simplement le premier élément. Pour calculer l’espacement entre les éléments, nous pouvons mesurer la droite décalage du premier élément et la la gauche décalage de la seconde, puis soustrayez la première de la seconde: 

const right, width = items [0] .getBoundingClientRect (); const spacing = items [1] .getBoundingClientRect (). left - right;

Maintenant, avec le targetX et delta variables que nous avons transmises à la fonction, nous avons toutes les données dont nous avons besoin pour calculer rapidement un décalage pour aller à.

Le calcul consiste à diviser l'absolu targetX valeur par le largeur + espacement. Cela nous donnera le nombre exact d'articles que nous pouvons insérer à l'intérieur de cette distance..

const totalItems = Math.abs (targetX) / (largeur + espacement);

Ensuite, arrondissez de haut en bas en fonction du sens de la pagination (notre delta). Cela nous donnera le nombre de Achevée articles que nous pouvons adapter.

const totalCompleteItems = delta === 1? Math.floor (totalItems): Math.ceil (totalItems);

Enfin, multipliez ce nombre par largeur + espacement pour nous donner une couleur offset avec un élément complet.

retourne 0 - totalCompleteItems * (largeur + espacement);

Animer la pagination

Maintenant que nous avons notre targetX calculé, nous pouvons l'animer! Pour cela, nous allons utiliser le cheval de bataille de l'animation Web, le interpolation.

Pour les non-initiés, "tween" est l'abréviation de êtreentre. Une interpolation passe d'une valeur à une autre, sur une durée définie. Si vous avez utilisé des transitions CSS, c'est la même chose. 

L'utilisation de JavaScript sur CSS pour les tweens présente de nombreux avantages (et inconvénients!). Dans ce cas, parce que nous animons aussi sliderX avec la physique et la saisie de l'utilisateur, il nous sera plus facile de rester dans ce flux de travail pendant l'interpolation.

Cela signifie également que plus tard, nous pourrons brancher une barre de progression et que cela fonctionnera naturellement avec toutes nos animations, gratuitement..

Nous voulons d'abord importer interpolation de Popmotion:

const calc, css, assouplissement, physique, pointeur, transformation, interpolation, valeur = window.popmotion;

À la fin de notre aller à fonction, nous pouvons ajouter notre interpolation qui anime de currentX à targetX:

tween (de: currentX, à: targetX, onUpdate: sliderX). start ();

Par défaut, les ensembles Popmotion durée à 300 millisecondes et facilité à easing.easeOut. Celles-ci ont été spécialement sélectionnées pour offrir une sensation de réactivité aux animations répondant aux commentaires des utilisateurs, mais n'hésitez pas à jouer et à voir si vous proposez quelque chose qui correspond mieux à l'ambiance de votre marque..

Indicateur de progression

Il est utile que les utilisateurs sachent où ils se trouvent dans le carrousel. Pour cela, nous pouvons brancher un indicateur de progression.

Votre barre de progression peut être stylée de différentes manières. Pour ce didacticiel, nous avons créé une division colorée de 5 pixels de haut, située entre les boutons précédent et suivant. C’est la façon dont nous relions cela à notre code et animons la barre qui est importante et qui est au centre de ce tutoriel..

Vous n'avez pas encore vu l'indicateur car nous l'avions appelé à l'origine avec transformation: scaleX (0). Nous utilisons un échelle transform pour ajuster la largeur de la barre car, comme expliqué dans la partie 1, les transformations sont plus performantes que les propriétés changeantes telles que la gauche ou, dans ce cas, largeur.

Cela nous permet également d’écrire facilement du code qui définit l’échelle comme pourcentage: la valeur actuelle de sliderX entre minXOffset et maxXOffset.

Commençons par sélectionner notre div.progress-bar après notre précédentButton sélecteur:

const progressBar = container.querySelector ('. progress-bar');

Après avoir défini sliderRenderer, nous pouvons ajouter un moteur de rendu pour barre de progression:

const progressBarRenderer = css (progressBar);

Définissons maintenant une fonction pour mettre à jour le scaleX de la barre de progression.

Nous allons utiliser un calc fonction appelée getProgressFromValue. Cela prend une gamme, dans notre cas min et maxXOffset, et un troisième numéro. Il retourne le le progrès, un nombre entre 0 et 1, de ce troisième nombre dans la plage donnée.

function updateProgressBar (x) const progress = calc.getProgressFromValue (maxXOffset, minXOffset, x); progressBarRenderer.set ('scaleX', progress); 

Nous avons écrit la gamme ici comme maxXOffset, minXOffset quand, intuitivement, il devrait être inversé. Ceci est dû au fait X est une valeur négative, et maxXOffset est aussi une valeur négative alors que minXOffset est 0. le 0 est techniquement le plus grand des deux nombres, mais la plus petite valeur représente en réalité le décalage maximum. Négatifs, hein?

Nous voulons que l’indicateur de progression se mette à jour en même temps que sliderX, alors changeons cette ligne:

const sliderX = valeur (0, (x) => sliderRenderer.set ('x', x));

A cette ligne:

const sliderX = valeur (0, (x) => updateProgressBar (x); sliderRenderer.set ('x', x););

Maintenant, chaque fois sliderX mises à jour, de même que la barre de progression.

Conclusion

Voilà pour cet acompte! Vous pouvez récupérer le dernier code sur ce CodePen. Nous avons introduit avec succès le défilement horizontal des roues, la pagination et une barre de progression.

Le carrousel est en assez bon état jusqu'à présent! Dans la dernière tranche, nous allons aller plus loin. Nous allons rendre le carrousel entièrement accessible au clavier pour que tout le monde puisse l'utiliser. 

Nous allons également ajouter quelques touches agréables à l'aide d'un remorqueur à ressort lorsqu'un utilisateur essaie de faire défiler le carrousel au-delà de ses limites à l'aide d'un défilement tactile ou d'une pagination. 

À plus tard!