Les écrans tactiles sont disponibles sur de plus en plus d'appareils, des téléphones aux écrans d'ordinateurs. Votre application doit répondre à son toucher de manière intuitive et esthétique.
Les écrans tactiles sont disponibles sur de plus en plus d'appareils, des téléphones aux écrans d'ordinateur. Lorsque vos utilisateurs choisissent d'interagir avec votre UI, votre application doit répondre à leur toucher de manière intuitive.
Répondre aux états des éléments
Avez-vous déjà appuyé ou cliqué sur un élément d'une page Web et demandé si le site l'avait réellement détecté ?
Il suffit de modifier la couleur d'un élément lorsque les utilisateurs touchent ou interagissent avec des parties de votre interface utilisateur pour leur donner l'assurance de base que votre site fonctionne. Cela permet non seulement de réduire la frustration, mais aussi de donner une impression de réactivité et d'agilité.
Les éléments DOM peuvent hériter de l'un des états suivants : par défaut, en surbrillance, en survol et actif. Pour modifier l'UI pour chacun de ces états, nous devons appliquer des styles aux pseudo-classes :hover
, :focus
et :active
, comme indiqué ci-dessous:
.btn {
background-color: #4285f4;
}
.btn:hover {
background-color: #296cdb;
}
.btn:focus {
background-color: #0f52c1;
/* The outline parameter suppresses the border
color / outline when focused */
outline: 0;
}
.btn:active {
background-color: #0039a8;
}
Dans la plupart des navigateurs mobiles, les états de pointage et/ou de ciblage s'appliquent à un élément une fois que l'utilisateur a appuyé dessus.
Réfléchissez attentivement aux styles que vous définissez et à leur apparence pour l'utilisateur une fois qu'il a terminé son geste.
Supprimer les styles de navigateur par défaut
Une fois que vous avez ajouté des styles pour les différents états, vous remarquerez que la plupart des navigateurs implémentent leurs propres styles en réponse à la pression de l'utilisateur. En effet, lors du lancement initial des appareils mobiles, un certain nombre de sites n'avaient pas de style pour l'état :active
. Par conséquent, de nombreux navigateurs ont ajouté une couleur ou un style de surbrillance supplémentaires pour fournir des commentaires à l'utilisateur.
La plupart des navigateurs utilisent la propriété CSS outline
pour afficher un anneau autour d'un élément lorsqu'il est sélectionné. Vous pouvez le supprimer avec:
.btn:focus {
outline: 0;
/* Add replacement focus styling here (i.e. border) */
}
Safari et Chrome ajoutent une couleur de surbrillance en cas de pression, que vous pouvez éviter avec la propriété CSS -webkit-tap-highlight-color
:
/* Webkit / Chrome Specific CSS to remove tap
highlight color */
.btn {
-webkit-tap-highlight-color: transparent;
}
Internet Explorer sur Windows Phone présente un comportement similaire, mais il est supprimé via une balise méta :
<meta name="msapplication-tap-highlight" content="no">
Firefox doit gérer deux effets secondaires.
La pseudo-classe -moz-focus-inner
, qui ajoute un contour aux éléments tactiles, peut être supprimée en définissant border: 0
.
Si vous utilisez un élément <button>
sur Firefox, un dégradé est appliqué, que vous pouvez supprimer en définissant background-image: none
.
/* Firefox Specific CSS to remove button
differences and focus ring */
.btn {
background-image: none;
}
.btn::-moz-focus-inner {
border: 0;
}
Désactivation de la sélection de l'utilisateur
Lorsque vous créez votre interface utilisateur, il peut arriver que vous souhaitiez que les utilisateurs interagissent avec vos éléments, mais que vous vouliez supprimer le comportement par défaut consistant à sélectionner du texte lors d'un appui prolongé ou en faisant glisser la souris sur votre interface utilisateur.
Vous pouvez le faire avec la propriété CSS user-select
, mais sachez que cela peut extrêmement agacer les utilisateurs s'ils souhaitent sélectionner le texte dans l'élément.
Veillez donc à l'utiliser avec précaution et avec parcimonie.
/* Example: Disable selecting text on a paragraph element: */
p.disable-text-selection {
user-select: none;
}
Implémenter des gestes personnalisés
Si vous avez une idée d'interactions et de gestes personnalisés pour votre site, tenez compte de deux points :
- Prendre en charge tous les navigateurs
- Maintenir une fréquence d'images élevée
Dans cet article, nous allons examiner précisément ces sujets, en couvrant les API que nous devons prendre en charge pour toucher tous les navigateurs, puis en expliquant comment utiliser ces événements efficacement.
Selon ce que vous souhaitez que votre geste fasse, vous souhaitez probablement que l'utilisateur interagisse avec un élément à la fois ou qu'il puisse interagir avec plusieurs éléments en même temps.
Dans cet article, nous allons examiner deux exemples, qui montrent comment prendre en charge tous les navigateurs et comment maintenir la fréquence d'images élevée.
Le premier exemple permet à l'utilisateur d'interagir avec un seul élément. Dans ce cas, vous pouvez souhaiter que tous les événements tactiles soient attribués à cet élément, à condition que le geste ait commencé sur l'élément lui-même. Par exemple, vous pouvez toujours contrôler l'élément en déplaçant un doigt hors de celui-ci.
Cela est utile, car cela offre une grande flexibilité à l'utilisateur, mais impose une restriction sur la façon dont l'utilisateur peut interagir avec votre UI.
Toutefois, si vous prévoyez que les utilisateurs interagiront avec plusieurs éléments en même temps (à l'aide de la technologie multipoint), vous devez limiter le toucher à l'élément spécifique.
Cette approche est plus flexible pour les utilisateurs, mais elle complique la logique de manipulation de l'UI et est moins résiliente aux erreurs des utilisateurs.
Ajouter des écouteurs d'événements
Dans Chrome (version 55 et ultérieure), Internet Explorer et Edge, PointerEvents
est l'approche recommandée pour implémenter des gestes personnalisés.
Dans d'autres navigateurs, TouchEvents
et MouseEvents
sont la bonne approche.
PointerEvents
présente l'avantage de fusionner plusieurs types d'entrées, y compris les événements de souris, d'écran tactile et de stylet, en un seul ensemble de rappels. Les événements à écouter sont pointerdown
, pointermove
, pointerup
et pointercancel
.
Dans d'autres navigateurs, les équivalents sont touchstart
, touchmove
, touchend
et touchcancel
pour les événements tactiles. Si vous souhaitez implémenter le même geste pour la saisie à la souris, vous devez implémenter mousedown
, mousemove
et mouseup
.
Si vous avez des questions sur les événements à utiliser, consultez ce tableau des événements tactiles, de souris et de pointeur.
L'utilisation de ces événements nécessite d'appeler la méthode addEventListener()
sur un élément DOM, ainsi que le nom d'un événement, une fonction de rappel et une valeur booléenne.
La valeur booléenne détermine si vous devez intercepter l'événement avant ou après que d'autres éléments ont eu la possibilité de l'intercepter et de l'interpréter. (true
signifie que vous souhaitez que l'événement soit placé avant les autres éléments.)
Voici un exemple d'écoute du début d'une interaction.
// Check if pointer events are supported.
if (window.PointerEvent) {
// Add Pointer Event Listener
swipeFrontElement.addEventListener('pointerdown', this.handleGestureStart, true);
swipeFrontElement.addEventListener('pointermove', this.handleGestureMove, true);
swipeFrontElement.addEventListener('pointerup', this.handleGestureEnd, true);
swipeFrontElement.addEventListener('pointercancel', this.handleGestureEnd, true);
} else {
// Add Touch Listener
swipeFrontElement.addEventListener('touchstart', this.handleGestureStart, true);
swipeFrontElement.addEventListener('touchmove', this.handleGestureMove, true);
swipeFrontElement.addEventListener('touchend', this.handleGestureEnd, true);
swipeFrontElement.addEventListener('touchcancel', this.handleGestureEnd, true);
// Add Mouse Listener
swipeFrontElement.addEventListener('mousedown', this.handleGestureStart, true);
}
Gérer les interactions avec un seul élément
Dans le court extrait de code ci-dessus, nous n'avons ajouté que l'écouteur d'événements de démarrage pour les événements de souris. En effet, les événements de souris ne se déclenchent que lorsque le curseur passe la souris sur l'élément auquel l'écouteur d'événements est ajouté.
TouchEvents
suit un geste après son démarrage, quel que soit l'endroit où le toucher se produit, et PointerEvents
suit les événements quel que soit l'endroit où le toucher se produit après avoir appelé setPointerCapture
sur un élément DOM.
Pour les événements de déplacement et de fin de la souris, nous ajoutons les écouteurs d'événements dans la méthode de début du geste et les ajoutons au document. Cela signifie qu'il peut suivre le curseur jusqu'à la fin du geste.
Pour ce faire, procédez comme suit:
- Ajoutez tous les écouteurs TouchEvent et PointerEvent. Pour les MouseEvents, n'ajoutez que l'événement de début.
- Dans le rappel du geste de début, associez les événements de déplacement et de fin de la souris au document. De cette façon, tous les événements de la souris sont reçus, que l'événement se produise sur l'élément d'origine ou non. Pour PointerEvents, nous devons appeler
setPointerCapture()
sur notre élément d'origine pour recevoir tous les autres événements. Gérez ensuite le début du geste. - Gérez les événements de mouvement.
- À l'événement de fin, supprimez du document les écouteurs de mouvement et de fin, puis mettez fin au geste.
Vous trouverez ci-dessous un extrait de notre méthode handleGestureStart()
, qui ajoute les événements de déplacement et de fin au document :
// Handle the start of gestures
this.handleGestureStart = function(evt) {
evt.preventDefault();
if(evt.touches && evt.touches.length > 1) {
return;
}
// Add the move and end listeners
if (window.PointerEvent) {
evt.target.setPointerCapture(evt.pointerId);
} else {
// Add Mouse Listeners
document.addEventListener('mousemove', this.handleGestureMove, true);
document.addEventListener('mouseup', this.handleGestureEnd, true);
}
initialTouchPos = getGesturePointFromEvent(evt);
swipeFrontElement.style.transition = 'initial';
}.bind(this);
Le rappel de fin que nous ajoutons est handleGestureEnd()
, qui supprime les écouteurs d'événements de déplacement et de fin du document et libère la capture du pointeur une fois le geste terminé, comme suit:
// Handle end gestures
this.handleGestureEnd = function(evt) {
evt.preventDefault();
if (evt.touches && evt.touches.length > 0) {
return;
}
rafPending = false;
// Remove Event Listeners
if (window.PointerEvent) {
evt.target.releasePointerCapture(evt.pointerId);
} else {
// Remove Mouse Listeners
document.removeEventListener('mousemove', this.handleGestureMove, true);
document.removeEventListener('mouseup', this.handleGestureEnd, true);
}
updateSwipeRestPosition();
initialTouchPos = null;
}.bind(this);
En suivant ce modèle d'ajout de l'événement de déplacement au document, si l'utilisateur commence à interagir avec un élément et déplace son geste en dehors de l'élément, nous continuerons à recevoir des mouvements de souris, quel que soit l'endroit où ils se trouvent sur la page, car les événements sont reçus à partir du document.
Ce schéma montre le comportement des événements tactiles lorsque nous ajoutons les événements de déplacement et de fin au document lorsqu'un geste commence.
Répondre efficacement aux gestes tactiles
Maintenant que les événements de début et de fin sont gérés, nous pouvons répondre aux événements tactiles.
Pour tous les événements de démarrage et de déplacement, vous pouvez facilement extraire x
et y
d'un événement.
L'exemple suivant vérifie si l'événement provient d'une TouchEvent
en vérifiant si targetTouches
existe. Si tel est le cas, il extrait clientX
et clientY
à partir du premier contact.
Si l'événement est PointerEvent
ou MouseEvent
, il extrait clientX
et clientY
directement de l'événement lui-même.
function getGesturePointFromEvent(evt) {
var point = {};
if (evt.targetTouches) {
// Prefer Touch Events
point.x = evt.targetTouches[0].clientX;
point.y = evt.targetTouches[0].clientY;
} else {
// Either Mouse event or Pointer Event
point.x = evt.clientX;
point.y = evt.clientY;
}
return point;
}
Un TouchEvent
contient trois listes de données tactiles :
touches
: liste de toutes les pressions actuelles sur l'écran, quel que soit l'élément DOM sur lequel elles se trouvent.targetTouches
: liste des pressions actuellement sur l'élément DOM auquel l'événement est lié.changedTouches
: liste des gestes tactiles qui ont changé, ce qui a déclenché l'événement.
Dans la plupart des cas, targetTouches
vous donne tout ce dont vous avez besoin et ce dont vous avez besoin. (Pour en savoir plus sur ces listes, consultez la section Listes tactiles.)
Utiliser requestAnimationFrame
Étant donné que les rappels d'événements sont déclenchés sur le thread principal, nous souhaitons exécuter le moins de code possible dans les rappels de nos événements, en maintenant notre fréquence d'images élevée et en évitant les à-coups.
En utilisant requestAnimationFrame()
, nous avons la possibilité de mettre à jour l'UI juste avant que le navigateur ne veuille dessiner un frame, ce qui nous aidera à déplacer certaines tâches de nos rappels d'événements.
Si vous ne connaissez pas requestAnimationFrame()
, cliquez ici pour en savoir plus.
Une implémentation typique consiste à enregistrer les coordonnées x
et y
des événements de début et de déplacement, puis à demander un frame d'animation dans le rappel d'événement de déplacement.
Dans notre démonstration, nous stockons la position tactile initiale dans handleGestureStart()
(recherchez initialTouchPos
):
// Handle the start of gestures
this.handleGestureStart = function(evt) {
evt.preventDefault();
if (evt.touches && evt.touches.length > 1) {
return;
}
// Add the move and end listeners
if (window.PointerEvent) {
evt.target.setPointerCapture(evt.pointerId);
} else {
// Add Mouse Listeners
document.addEventListener('mousemove', this.handleGestureMove, true);
document.addEventListener('mouseup', this.handleGestureEnd, true);
}
initialTouchPos = getGesturePointFromEvent(evt);
swipeFrontElement.style.transition = 'initial';
}.bind(this);
La méthode handleGestureMove()
stocke la position de son événement avant de demander une image d'animation si nécessaire, en transmettant la fonction onAnimFrame()
en tant que rappel:
this.handleGestureMove = function (evt) {
evt.preventDefault();
if (!initialTouchPos) {
return;
}
lastTouchPos = getGesturePointFromEvent(evt);
if (rafPending) {
return;
}
rafPending = true;
window.requestAnimFrame(onAnimFrame);
}.bind(this);
La valeur onAnimFrame
est une fonction qui, lorsqu'elle est appelée, modifie notre UI pour la déplacer. En transmettant cette fonction à requestAnimationFrame()
, nous demandons au navigateur de l'appeler juste avant qu'il ne mette à jour la page (c'est-à-dire qu'il peigne les modifications apportées à la page).
Dans le rappel handleGestureMove()
, nous vérifions d'abord si rafPending
est faux, ce qui indique si onAnimFrame()
a été appelé par requestAnimationFrame()
depuis le dernier événement de déplacement. Cela signifie qu'il n'y a qu'un seul requestAnimationFrame()
en attente d'exécution à la fois.
Lorsque notre rappel onAnimFrame()
est exécuté, nous définissons la transformation sur tous les éléments à déplacer avant de mettre à jour rafPending
sur false
, ce qui permet à l'événement tactile suivant de demander une nouvelle image d'animation.
function onAnimFrame() {
if (!rafPending) {
return;
}
var differenceInX = initialTouchPos.x - lastTouchPos.x;
var newXTransform = (currentXPosition - differenceInX)+'px';
var transformStyle = 'translateX('+newXTransform+')';
swipeFrontElement.style.webkitTransform = transformStyle;
swipeFrontElement.style.MozTransform = transformStyle;
swipeFrontElement.style.msTransform = transformStyle;
swipeFrontElement.style.transform = transformStyle;
rafPending = false;
}
Contrôler les gestes à l'aide d'actions tactiles
La propriété CSS touch-action
vous permet de contrôler le comportement tactile par défaut d'un élément. Dans nos exemples, nous utilisons touch-action: none
pour empêcher le navigateur d'effectuer quoi que ce soit avec le toucher de l'utilisateur, ce qui nous permet d'intercepter tous les événements tactiles.
/* Pass all touches to javascript: */
button.custom-touch-logic {
touch-action: none;
}
L'utilisation de touch-action: none
est une option plutôt nucléaire, car elle empêche tous les comportements par défaut du navigateur. Dans de nombreux cas, l'une des options ci-dessous constitue une meilleure solution.
touch-action
vous permet de désactiver les gestes implémentés par un navigateur.
Par exemple, dans Internet Explorer 10+, vous pouvez appuyer deux fois pour zoomer. En définissant touch-action
sur manipulation
, vous empêchez le comportement par défaut du double appui.
Vous pouvez ainsi implémenter vous-même un geste de double-appui.
Vous trouverez ci-dessous une liste des valeurs touch-action
couramment utilisées :
Prise en charge des anciennes versions d'IE
Si vous souhaitez prendre en charge IE10, vous devez gérer les versions de PointerEvents
avec un préfixe du fournisseur.
Pour vérifier la prise en charge de PointerEvents
, vous recherchez généralement window.PointerEvent
, mais dans IE10, vous recherchez window.navigator.msPointerEnabled
.
Les noms d'événements avec des préfixes de fournisseurs sont les suivants : 'MSPointerDown'
, 'MSPointerUp'
et 'MSPointerMove'
.
L'exemple ci-dessous vous montre comment vérifier la prise en charge et changer les noms des événements.
var pointerDownName = 'pointerdown';
var pointerUpName = 'pointerup';
var pointerMoveName = 'pointermove';
if (window.navigator.msPointerEnabled) {
pointerDownName = 'MSPointerDown';
pointerUpName = 'MSPointerUp';
pointerMoveName = 'MSPointerMove';
}
// Simple way to check if some form of pointerevents is enabled or not
window.PointerEventsSupport = false;
if (window.PointerEvent || window.navigator.msPointerEnabled) {
window.PointerEventsSupport = true;
}
Pour en savoir plus, consultez cet article de Microsoft sur les mises à jour.
Référence
Pseudo-classes pour les états tactiles
Pour obtenir la référence définitive sur les événements tactiles, consultez Événements tactiles du W3C.
Événements tactiles, de souris et de pointeur
Ces événements sont les éléments de base pour ajouter de nouveaux gestes à votre application :
Listes tactiles
Chaque événement tactile comprend trois attributs de liste :
Activer la compatibilité avec l'état actif sur iOS
Malheureusement, Safari sur iOS n'applique pas l'état actif par défaut. Pour qu'il fonctionne, vous devez ajouter un écouteur d'événement touchstart
au corps du document ou à chaque élément.
Vous devez le faire derrière un test de l'agent utilisateur afin qu'il ne s'exécute que sur les appareils iOS.
L'ajout d'un début de contact au corps présente l'avantage de s'appliquer à tous les éléments du DOM. Toutefois, cela peut entraîner des problèmes de performances lors du défilement de la page.
window.onload = function() {
if (/iP(hone|ad)/.test(window.navigator.userAgent)) {
document.body.addEventListener('touchstart', function() {}, false);
}
};
L'alternative consiste à ajouter les écouteurs de début de pression à tous les éléments interactifs de la page, ce qui atténue certains des problèmes de performances.
window.onload = function() {
if (/iP(hone|ad)/.test(window.navigator.userAgent)) {
var elements = document.querySelectorAll('button');
var emptyFunction = function() {};
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener('touchstart', emptyFunction, false);
}
}
};