Ajoutez des effets de révélation interactifs et performants à votre site à l'aide de masques CSS et de propriétés personnalisées enregistrées. En utilisant un dégradé radial comme masque et en enregistrant ses valeurs d'arrêt, nous pouvons effectuer une transition fluide de l'entrée et de la sortie, tout en suivant le pointeur d'un utilisateur avec un minimum de code JavaScript.
Implémentation
1. Enregistrer des propriétés personnalisées
Pour permettre une interpolation fluide des valeurs de point de dégradé, vous devez enregistrer les variables à l'aide de @property. Cela informe le moteur du navigateur sur le type de données, ce qui lui permet de passer d'une valeur à l'autre lors des mises à jour.
/* Register the spotlight inner and outer sizes to enable interpolation */
@property --inner-size{
syntax: "<length-percentage>";
inherits: true;
initial-value: 0px;
}
@property --outer-size{
syntax: "<length-percentage>";
inherits: true;
initial-value: 0px;
}
Les propriétés personnalisées qui suivent la position du pointeur n'ont pas besoin d'être transitionnées. Il n'est donc pas nécessaire de les enregistrer.
2. Définir la couche de masquage
Appliquez mask-image à l'élément que vous souhaitez afficher. Utilisez un radial-gradient qui référence les propriétés enregistrées.
.reveal-layer {
/* Only transition the size properties, NOT the position variables */
transition: --inner-size 0.2s ease-in-out, --outer-size 0.2s ease-in-out;
/* The spotlight is defined by the transparent center of the mask */
mask-image: radial-gradient(
circle at var(--mouse-x) var(--mouse-y),
black var(--inner-size, 0%),
transparent var(--outer-size, 0%)
);
/* Ensure the mask doesn't repeat if the element is large */
mask-repeat: no-repeat;
/* Make the mask layer non-interactive */
pointer-events: none;
}
/* Update the gradients stops on interaction */
.reveal-layer:hover {
--inner-size: 100px;
--outer-size: 120px;
}
3. Mettre à jour les coordonnées avec JavaScript
Suivez la position du pointeur et mettez à jour les variables CSS. Comme les propriétés sont enregistrées et qu'une transition est définie, le projecteur se déplace de manière fluide, même si les événements de pointeur sont peu fréquents.
const container = document.querySelector('.container');
// Store the container's bounding rect
let rect = container.getBoundingClientRect();
// Update the rect when the container is resized
const resizeObserver = new ResizeObserver(()=>{
rect = container.getBoundingClientRect();
})
resizeObserver.observe(container);
container.addEventListener('pointermove', (e) => {
// Calculate position as a percentage of the container.
const x = ((e.clientX - rect.left) / rect.width) * 100;
const y = ((e.clientY - rect.top) / rect.height) * 100;
// Update the registered properties
container.style.setProperty('--mouse-x', `${x}%`);
container.style.setProperty('--mouse-y', `${y}%`);
});
4. Accessibilité et interaction
Garantie d'accessibilité OBLIGATOIRE : ce modèle repose sur les interactions du pointeur pour révéler un spotlight visuel. Vous DEVEZ vous assurer que l'ensemble du contenu sous-jacent reste entièrement visible, lisible et accessible indépendamment au clavier par défaut dans la mise en page sous-jacente, en utilisant la couche de mise en avant uniquement comme une amélioration visuelle non essentielle pour les utilisateurs de pointeurs. N'utilisez jamais cet effet pour masquer ou bloquer du contenu essentiel pour les utilisateurs de claviers ou de technologies d'assistance.
- Événements de pointeur : définissez
pointer-events: nonesur le calque de superposition du masque pour permettre aux interactions standards de clic et de toucher de passer aux commandes situées en dessous. - Forcer la réduction des mouvements : désactivez l'interpolation de transition fluide pour les utilisateurs qui demandent une réduction des mouvements.
/* MANDATORY Copy-Paste Safety: Disable transition scaling for motion-sensitive users */
@media (prefers-reduced-motion: reduce) {
.reveal-layer {
transition: none !important;
}
}
Stratégies de remplacement
Création de remplacement pour les propriétés non enregistrées
Les navigateurs compatibles avec mask-image, mais pas avec @property, afficheront toujours le projecteur, mais le mouvement passera d'un état à l'autre, car ils ne peuvent pas interpoler les valeurs dans un radial-gradient. Fournissez des valeurs de remplacement lorsque vous utilisez var().
.reveal-layer {
mask-image: radial-gradient(
circle at var(--mouse-x) var(--mouse-y),
/* Use fallback values when using the `var()` function for browsers that don't get an initial value from the @property registration. */
black var(--inner-size, 0%),
transparent var(--outer-size, 0%)
);
}
Assistance Basic Mask
Pour les navigateurs qui ne sont pas du tout compatibles avec le masquage CSS :
- Propriété préfixée : utilisez la propriété préfixée
-webkit-mask-imagepour une compatibilité plus étendue avec les navigateurs. - Amélioration progressive : concevez l'état de base de l'UI de manière à ce qu'elle soit entièrement fonctionnelle et lisible sans l'effet de révélation. Cela est utile lorsque l'effet n'ajoute qu'un style visuel et n'est pas nécessaire pour lire le contenu.