Créer le carrousel parfait, première partie

Les carrousels sont un aliment de base des sites de streaming et de commerce électronique. Amazon et Netflix les utilisent tous deux comme outils de navigation de premier plan. Dans ce didacticiel, nous évaluerons la conception des interactions entre les deux et utiliserons nos résultats pour mettre en œuvre le carrousel parfait..

Dans cette série de didacticiels, nous allons également apprendre quelques fonctions de Popmotion, un moteur de mouvement JavaScript. Il propose des outils d'animation tels que les tweens (utiles pour la pagination), le suivi du pointeur (pour le défilement) et la physique du printemps (pour nos ravissantes touches finales).

La première partie évaluera comment Amazon et Netflix ont implémenté le défilement. Nous allons ensuite mettre en place un carrousel qui peut être fait défiler au toucher.

À la fin de cette série, nous aurons implémenté le défilement des roues et du pavé tactile, la pagination, les barres de progression, la navigation au clavier et quelques petites manipulations utilisant la physique des ressorts. Nous aurons également été exposés à une composition fonctionnelle de base.

Parfait?

Que faut-il pour qu'un carrousel soit "parfait"? Il doit être accessible par:

  • Souris: Il devrait offrir des boutons précédents et suivants faciles à cliquer et ne masquant pas le contenu..
  • Toucher: Il faut suivre le doigt, puis faire défiler avec le même élan que lorsque le doigt se lève de l'écran.
  • Molette: Souvent négligés, la souris Apple Magic Mouse et de nombreux trackpads pour ordinateurs portables offrent un défilement horizontal en douceur. Nous devrions utiliser ces capacités!
  • Clavier: De nombreux utilisateurs préfèrent ne pas utiliser ou ne peuvent pas utiliser une souris pour la navigation. Il est important de rendre notre carrousel accessible pour que les utilisateurs puissent également utiliser notre produit..

Enfin, nous allons faire avancer les choses encore plus loin et en faire un UX confiant et délicieux en faisant en sorte que le carrousel réponde clairement et viscéralement à la physique du printemps lorsque le curseur aura atteint la fin..

Installer

Premièrement, obtenons le code HTML et CSS nécessaire pour construire un carrousel rudimentaire en créant ce CodePen. 

Le stylo est configuré avec Sass pour le pré-traitement CSS et Babel pour la transcription de JavaScript ES6. J'ai également inclus Popmotion, qui peut être consulté avec window.popmotion.

Vous pouvez copier le code dans un projet local si vous préférez, mais vous devez vous assurer que votre environnement prend en charge Sass et ES6. Vous aurez également besoin d’installer Popmotion avec npm installer popmotion.

Créer un nouveau carrousel

Sur une page donnée, nous pourrions avoir de nombreux carrousels. Nous avons donc besoin d’une méthode pour encapsuler l’état et les fonctionnalités de chaque.

Je vais utiliser un fonction d'usine Plutôt qu'un classe. Les fonctions d’usine évitent d’utiliser des fonctions souvent source de confusion ce mot-clé et simplifiera le code pour les besoins de ce tutoriel.

Dans votre éditeur JavaScript, ajoutez cette fonction simple:

fonction carrousel (conteneur)  carrousel (document.querySelector ('. conteneur'));

Nous allons ajouter notre code spécifique au carrousel à l'intérieur de cette carrousel une fonction.

Le comment et le pourquoi du défilement

Notre première tâche consiste à faire défiler le carrousel. Il y a deux façons de s'y prendre:

Défilement du navigateur natif

La solution évidente serait de définir overflow-x: faire défiler sur le curseur. Cela permettrait le défilement natif sur tous les navigateurs, y compris les périphériques tactiles et horizontaux..

Cette approche présente toutefois des inconvénients:

  • Le contenu en dehors du conteneur ne serait pas visible, ce qui peut être restrictif pour notre conception.
  • Cela limite également la façon dont nous pouvons utiliser des animations pour indiquer que nous avons atteint la fin..
  • Les navigateurs de bureau auront une barre de défilement horizontale laide (bien que accessible!).

Alternativement:

Animer translateX

Nous pourrions aussi animer le carrousel translateX propriété. Ce serait très polyvalent car nous serions en mesure de mettre en œuvre exactement le design que nous aimons. translateX est également très performant, contrairement au CSS la gauche propriété, il peut être géré par le GPU du périphérique.

En revanche, il faudrait réimplémenter la fonctionnalité de défilement à l'aide de JavaScript. C'est plus de travail, plus de code.

Comment le défilement des approches Amazon et Netflix?

Les carrousels Amazon et Netflix font des compromis différents en abordant ce problème.

Amazone anime le carrousel la gauche propriété en mode "bureau". Animer la gauche est un choix incroyablement pauvre, car son changement déclenche un nouveau calcul de la mise en page. Cela nécessite beaucoup de temps processeur, et les anciennes machines auront du mal à atteindre 60 images par seconde.

Celui qui a pris la décision d'animer la gauche au lieu de translateX doit être un vrai idiot (becquet: c'était moi, en 2012. Nous n'étions pas aussi éclairés à l'époque.)

Lorsqu'il détecte un périphérique tactile, le carrousel utilise le défilement natif du navigateur. Le problème avec l'activation de ce mode en mode "mobile" est que les utilisateurs de bureau dotés d'une molette de défilement horizontale ne sont pas pris en compte. Cela signifie également que tout contenu situé en dehors du carrousel devra être coupé visuellement:

Netflix anime correctement le carrousel translateX propriété, et il le fait sur tous les appareils. Cela leur permet d'avoir un design qui saigne à l'extérieur du carrousel:

Cela leur permet à leur tour de créer un design de fantaisie où les éléments sont agrandis en dehors des bords x et y du carrousel et où les éléments environnants sortent de leur chemin:

Malheureusement, la réimplémentation du défilement par Netflix pour les appareils tactiles n’est pas satisfaisante: elle utilise un système de pagination basé sur les gestes qui semble lent et lourd. Il n'y a également aucune considération pour les roues de défilement horizontales.

Nous pouvons faire mieux. Codons!

Faire défiler comme un pro

Notre premier geste est de saisir le .curseur nœud. Pendant que nous y sommes, prenons les éléments qu'il contient pour déterminer la dimension du curseur..

fonction carrousel (conteneur) const slider = container.querySelector ('. slider'); const items = slider.querySelectorAll ('. item'); 

Mesurer le carrousel

Nous pouvons déterminer la zone visible du curseur en mesurant sa largeur:

const sliderVisibleWidth = slider.offsetWidth;

Nous voudrons également connaître la largeur totale de tous les éléments contenus à l'intérieur. Pour garder notre carrousel fonctionner relativement propre, mettons ce calcul dans une fonction séparée en haut de notre fichier.

En utilisant getBoundingClientRect pour mesurer le la gauche offset de notre premier article et la droite offset de notre dernier article, nous pouvons utiliser la différence entre eux pour trouver la largeur totale de tous les articles.

function getTotalItemsWidth (items) const left = items [0] .getBoundingClientRect (); const right = items [items.length - 1] .getBoundingClientRect (); retournez droite - gauche; 

Après notre sliderVisibleWidth mesure, écrivez:

const totalItemsWidth = getTotalItemsWidth (items);

Nous pouvons maintenant déterminer la distance maximale que notre carrousel devrait pouvoir parcourir. C'est la largeur totale de tous nos articles, moins une pleine largeur de notre curseur visible. Cela fournit un nombre qui permet à l'élément le plus à droite de s'aligner sur la droite de notre curseur:

const maxXOffset = 0; const minXOffset = - (totalItemsWidth - sliderVisibleWidth);

Avec ces mesures en place, nous sommes prêts à commencer à faire défiler notre carrousel..

Réglage translateX

Popmotion est livré avec un moteur de rendu CSS pour un paramétrage simple et performant des propriétés CSS. Il est également livré avec une fonction value qui peut être utilisée pour suivre les nombres et, surtout (comme nous le verrons bientôt), pour interroger leur vitesse..

En haut de votre fichier JavaScript, importez-les comme suit:

const css, valeur = window.popmotion;

Ensuite, sur la ligne après avoir défini minXOffset, créer un moteur de rendu CSS pour notre curseur:

const sliderRenderer = css (slider);

Et créer un valeur pour suivre le décalage de notre curseur et mettre à jour le curseur translateX propriété quand il change:

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

Maintenant, déplacer le curseur horizontalement est aussi simple que d’écrire:

sliderX.set (-100);

L'essayer!

Touch Scroll

Nous voulons que notre carrousel commence à défiler quand un utilisateurfait glisser le curseur horizontalement et pour arrêter le défilement lorsqu'un utilisateur cesse de toucher l'écran. Nos gestionnaires d'événements ressembleront à ceci:

laisser l'action; function stopTouchScroll () document.removeEventListener ('touchend', stopTouchScroll);  function startTouchScroll (e) document.addEventListener ('touchend', stopTouchScroll);  slider.addEventListener ('touchstart', startTouchScroll, passif: false);

Dans notre startTouchScroll fonction, nous voulons:

  • Arrêtez toute autre action alimentant sliderX.
  • Trouver le point de contact d'origine.
  • Ecoute le prochain toucher événement pour voir si l'utilisateur fait glisser verticalement ou horizontalement.

Après document.addEventListener, ajouter:

if (action) action.stop ();

Cela arrêtera toutes les autres actions (comme le défilement de momentum physique que nous allons implémenter dans stopTouchScroll) de déplacer le curseur. Cela permettra à l'utilisateur de "rattraper" immédiatement le curseur s'il fait défiler un élément ou un titre sur lequel il souhaite cliquer..

Ensuite, nous devons stocker le point de contact d'origine. Cela nous permettra de voir où l’utilisateur déplacera ensuite son doigt. S'il s'agit d'un mouvement vertical, nous allons permettre le défilement de la page comme d'habitude. Si c'est un mouvement horizontal, nous allons faire défiler le curseur.

Nous voulons partager ceci TouchOrigin entre les gestionnaires d'événements. Donc après laisser l'action; ajouter:

laissez touchOrigin = ;

De retour dans notre startTouchScroll gestionnaire, ajouter:

const touch = e.touches [0]; touchOrigin = x: touch.pageX, y: touch.pageY;

Nous pouvons maintenant ajouter un toucher écouteur d'événement à la document pour déterminer la direction de traînée basée sur cette TouchOrigin:

document.addEventListener ('touchmove', DetermineDragDirection);

Notre DetermineDragDirection function va mesurer le prochain emplacement tactile, vérifier qu'il a bien été déplacé et, le cas échéant, mesurer l'angle pour déterminer s'il est vertical ou horizontal:

fonction DetermDragDirection (e) const touch = e.changedTouches [0]; const touchLocation = x: touch.pageX, y: touch.pageY; 

Popmotion inclut des calculatrices utiles pour mesurer des éléments tels que la distance entre deux coordonnées x / y. Nous pouvons importer ceux-ci comme ceci:

const calc, css, valeur = window.popmotion;

Ensuite, mesurer la distance entre les deux points consiste à utiliser le distance calculatrice:

const distance = calc.distance (touchOrigin, touchLocation);

Maintenant, si le toucher a été déplacé, nous pouvons désactiver cet écouteur d'événement.

si (! distance) retourne; document.removeEventListener ('touchmove', DetermineDragDirection);

Mesurer l’angle entre les deux points avec le angle calculatrice:

const angle = calc.angle (touchOrigin, touchLocation);

Nous pouvons utiliser cela pour déterminer si cet angle est un angle horizontal ou vertical, en le passant à la fonction suivante. Ajoutez cette fonction tout en haut de notre fichier:

fonction angleIsVertical (angle) const isUp = (angle <= -90 + 45 && angle >= -90 - 45); const isDown = (angle <= 90 + 45 && angle >= 90 - 45); return (isUp || isDown); 

Cette fonction retourne vrai si l'angle fourni est compris entre -90 +/- 45 degrés (tout en haut) ou 90 +/- 45 degrés (tout en bas), nous pouvons donc en ajouter un autre revenir clause si cette fonction retourne vrai.

si (angleIsVertical (angle)) retourne;

Suivi du pointeur

Maintenant que nous savons que l'utilisateur essaie de faire défiler le carrousel, nous pouvons commencer à suivre son doigt. Popmotion propose une action de pointeur qui générera les coordonnées x / y d'un pointeur de souris ou tactile.

Tout d'abord, importer aiguille:

const calc, css, pointeur, valeur = window.popmotion;

Pour suivre la saisie tactile, fournissez l'événement d'origine à aiguille:

action = pointeur (e) .start ();

Nous voulons mesurer l'initiale X position de notre pointeur et appliquer n'importe quel mouvement au curseur. Pour cela, nous pouvons utiliser un transformateur appelé applyOffset.

Les transformateurs sont des fonctions pures qui prennent une valeur et renvoient it-yes-transform. Par exemple: const double = (v) => v * 2.

const calc, css, pointeur, transformation, valeur = window.popmotion; const applyOffset = transformer;

applyOffset est une fonction au curry. Cela signifie que lorsque nous l'appelons, cela crée une nouvelle fonction qui peut ensuite recevoir une valeur. Nous appelons d’abord un nombre avec lequel nous voulons mesurer le décalage, dans ce cas la valeur actuelle de action.x, et un nombre auquel appliquer ce décalage. Dans ce cas, c’est notre sliderX.

Donc notre applyOffset la fonction ressemblera à ceci:

const applyPointerMovement = applyOffset (action.x.get (), sliderX.get ());

Nous pouvons maintenant utiliser cette fonction dans le pointeur sortie callback pour appliquer un mouvement de pointeur au curseur.

action.output ((x) => slider.set (applyPointerMovement (x)));

S'arrêter, avec style

Le carrousel est maintenant déplaçable au toucher! Vous pouvez tester cela en utilisant une émulation de périphérique dans les outils de développement de Chrome..

Il se sent un peu janky, non? Vous avez peut-être déjà rencontré un défilement semblable à celui-ci: vous levez le doigt et le défilement cesse de fonctionner. Ou le défilement s'arrête net, puis une petite animation prend le relais pour simuler une continuation du défilement.

On ne va pas faire ça. Nous pouvons utiliser l'action physique de Popmotion pour prendre la vitesse réelle de sliderX et appliquer un frottement sur une durée.

Tout d'abord, ajoutez-le à notre liste sans cesse croissante d'importations:

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

Puis, à la fin de notre stopTouchScroll fonction, ajouter:

if (action) action.stop (); action = physique (from: sliderX.get (), vélocité: sliderX.getVelocity (), frottement: 0,2) .output ((v) => sliderX.set (v)) .start ();

Ici, de et rapidité sont définis avec la valeur actuelle et la vitesse de sliderX. Cela garantit que notre simulation physique a les mêmes conditions initiales de démarrage que le mouvement de glissement de l'utilisateur.

friction est défini comme 0,2. La friction est définie comme une valeur de 0 à 1, avec 0 être pas de friction du tout et 1 être friction absolue. Essayez de jouer avec cette valeur pour voir le changement que cela apporte au "sentiment" du carrousel lorsqu'un utilisateur cesse de faire glisser.

Un nombre plus petit donnera l'impression d'être plus léger et un nombre plus élevé rendra le mouvement plus lourd. Pour un mouvement de défilement, je me sens 0,2 frappe un bon équilibre entre erratique et lente.

Limites

Mais il y a un problème! Si vous avez joué avec votre nouveau carrousel tactile, c'est évident. Nous n'avons pas limité le mouvement, ce qui permet de jeter votre carrousel littéralement!

Il y a un autre transformateur pour ce travail, serrer. C’est aussi une fonction au curry, ce qui signifie que si on l’appelle avec une valeur minimale et maximale, 0 et 1, il retournera une nouvelle fonction. Dans cet exemple, la nouvelle fonction limitera le nombre attribué à celle-ci entre 0 et 1:

pince (0, 1) (5); // retourne 1

Tout d'abord, importer serrer:

const applyOffset, clamp = transformer;

Nous voulons utiliser cette fonction de serrage sur notre carrousel, alors ajoutez cette ligne après avoir défini minXOffset:

const clampXOffset = clamp (minXOffset, maxXOffset);

Nous allons modifier les deux sortie nous avons mis sur nos actions en utilisant une composition fonctionnelle légère avec le tuyau transformateur.

Tuyau

Lorsque nous appelons une fonction, nous l'écrivons comme ceci:

foo (0);

Si nous voulons donner le résultat de cette fonction à une autre fonction, nous pourrions écrire cela comme ceci:

barre (foo (0));

Cela devient un peu difficile à lire, et cela ne fait qu'empirer à mesure que nous ajoutons de plus en plus de fonctions.

Avec tuyau, nous pouvons composer une nouvelle fonction sur foo et bar que nous pouvons réutiliser:

const foobar = pipe (foo, bar); foobar (0);

C'est également écrit dans un ordre de départ naturel -> ordre d'arrivée, ce qui le rend plus facile à suivre. Nous pouvons l'utiliser pour composer applyOffset et serrer en une seule fonction. Importation tuyau:

const applyOffset, clamp, pipe = transformer;

Remplace le sortie rappel de notre aiguille avec:

tuyau ((x) => x, applyOffset (action.x.get (), sliderX.get ()), clampXOffset, (v) => sliderX.set (v))

Et remplace le sortie rappel de la physique avec:

pipe (clampXOffset, (v) => sliderX.set (v))

Ce type de composition fonctionnelle peut très facilement créer des processus descriptifs, étape par étape, à partir de fonctions réutilisables plus petites..

Maintenant, quand vous faites glisser le carrousel, il ne bougera pas en dehors de ses limites.

L'arrêt brutal n'est pas très satisfaisant. Mais c'est un problème pour une partie ultérieure!

Conclusion

C'est tout pour la partie 1. Jusqu'à présent, nous avons examiné les carrousels existants pour voir les forces et les faiblesses de différentes approches du défilement. Nous avons utilisé le suivi des entrées et la physique de Popmotion pour animer de manière performante ceux de notre carrousel translateX avec défilement tactile. Nous avons également été initiés à la composition fonctionnelle et aux fonctions au curry.

Vous pouvez récupérer une version commentée de "l'histoire jusqu'à présent" sur ce CodePen.

Dans les prochains versements, nous examinerons:

  • défilement avec une molette de la souris
  • remesurer le carrousel lorsque la fenêtre se redimensionne
  • pagination, avec accessibilité clavier et souris
  • touches délicieuses, avec l'aide de la physique de printemps

Au plaisir de vous y voir!