Rain
Un canvas 2D trace 120 gouttes en diagonale par simple addition de pixels, avec anneau de ripple à l'impact — vanilla pur, aucune dépendance.
Vous aurez accès à la démo interactive avec un compte gratuit.
Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Atmosphère comprend 57 effets, dont 10 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
La boucle requestAnimationFrame tourne sans interruption — pas d'IntersectionObserver, pas de mécanisme de pause. À chaque frame, le canvas est entièrement recouvert avec #0a0a0f (fillRect plein), ce qui efface la frame précédente sans effet de traîne. Les 120 gouttes sont instanciées au chargement : chaque objet stocke {x, y, len, speed} avec len ∈ [8,23] px et speed ∈ [4,10] px/frame — valeurs fixes par goutte, tirées une seule fois.
Chaque goutte est tracée par un segment d'1 px de (x, y) à (x + 1, y + len). La dérive horizontale fixe de +1 px/frame détermine l'angle de la diagonale : arctan(1/speed), soit ~6–14° selon la vitesse de la goutte. La vitesse est appliquée directement (y += speed, x += 1) sans normalisation par delta-temps — la fluidité dépend donc du taux de rafraîchissement de l'écran. Couleur : rgba(150,180,220,0.3), bleu-gris à 30 % d'opacité.
Quand y > h, la goutte est réinitialisée (y = -len, x aléatoire) et un splash {x, y: h-2, life: 1} est poussé dans un second tableau. Chaque frame : life -= 0.05 sur ~20 frames (~333 ms à 60 fps). Le splash est rendu comme un arc de cercle de rayon 5 × (1 - life) : il grandit de 0 à 5 px tandis que son opacité (0.3 × life) décroît. C'est un anneau de ripple éphémère — pas une accumulation persistante.
L'effet n'implémente ni prefers-reduced-motion, ni ResizeObserver, ni API publique. Le canvas est dimensionné une seule fois au chargement via parentElement.getBoundingClientRect(). L'IIFE cible getElementById('psRain') : conception mono-instance, un canvas par page.
Accessibilité
- prefers-reduced-motion absent : aucun test
window.matchMedia('(prefers-reduced-motion:reduce)')n'est présent — l'animation tourne en permanence, même chez les utilisateurs sensibles au mouvement. À implémenter manuellement dans votre intégration. - Le
<canvas>ne porte pasaria-hidden="true"dans le code source — les lecteurs d'écran peuvent annoncer un élément vide. Ajoutez cet attribut dans votre HTML d'intégration. - Le conteneur
.ps-canvas-wrapn'a nirole="img"niaria-label— l'effet est sémantiquement invisible pour les technologies d'assistance. Ajoutez un libellé descriptif sur l'élément parent. - Pas d'élément interactif (clic, focus) : la scène est purement décorative. Aucun problème de navigation au clavier intrinsèque, mais le texte overlay doit rester dans l'ordre de tabulation normal.
- Contraste overlay : le canvas seul (fond
#0a0a0f, gouttes à 30 % d'opacité) n'impose pas de contrainte de contraste propre — ajoutez un scrim semi-opaque si du texte est superposé pour maintenir WCAG AA (ratio ≥ 4,5:1).
Compatibilité navigateur
Nécessite Canvas 2D Context et requestAnimationFrame — supportés dans tous les navigateurs modernes. Zéro dépendance externe.
Sans support <code>canvas</code>, le <code><div></code> parent reste visible avec son fond — aucune erreur JS fatale, le contenu textuel overlay reste lisible.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="demo-preview">
<div class="ps-canvas-wrap" role="img" aria-label="Pluie animée en arrière-plan">
<canvas class="ps-canvas" id="psRain" aria-hidden="true"></canvas>
<!-- Facultatif : scrim + contenu overlay -->
<div class="ps-scrim" aria-hidden="true"></div>
<div class="ps-content">
<h2 class="ps-title">Votre message</h2>
<button class="ps-cta">CTA</button>
</div>
</div>
</div>
HTML + CSS + JS complet, prêt à coller — avec des centaines d'effets premium.
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
| Nombre de gouttes (const 120) | 120 | Boucle d'init : for (let n = 0; n < 120; n++). De 40 (bruine) à 200 (orage dense) — au-delà de 250, des ralentissements apparaissent sur mobile bas de gamme. |
| Vitesse par goutte (speed: 6×r+4) | 4–10 px/frame | Remplacer 6 * Math.random() + 4 : 3 * Math.random() + 2 = bruine lente, 10 * Math.random() + 8 = orage violent. Non normalisé par delta-temps : dépend du taux de rafraîchissement. |
| Longueur de traînée (len: 15×r+8) | 8–23 px | Remplacer 15 * Math.random() + 8 : traînées courtes (4 + 4 * Math.random()) = pluie fine ; longues (20 + 20 * Math.random()) = averse torrentielle. |
| Dérive horizontale (e.x += 1) | 1 px/frame | La ligne e.x += 1 fixe l'angle de la diagonale. Augmenter à 2–3 = vent latéral fort ; mettre à 0 = chute verticale pure ; valeur négative = pluie venant de droite. |
| Couleur des gouttes (rgba) | rgba(150,180,220,0.3) | Modifier couleur et opacité inline dans la boucle : rgba(220,230,255,0.5) = pluie froide brillante, rgba(100,130,100,0.3) = orage verdâtre, opacité 0,15 = fond très discret. |
| Rayon max du splash (5) | 0–5 px | Modifier 5 * (1 - c.life) : 8–12 px = impact visible sur grand écran ; 2–3 px = frémissement léger. Durée du ripple : contrôlée par le décrément life -= 0.05 (20 frames ≈ 333 ms). |
FAQ
IntersectionObserver. Pour l'ajouter : déclarez un flag let active = true avant l'IIFE, observez le .ps-canvas-wrap et basculez active selon l'intersection. Dans la boucle, remplacez requestAnimationFrame(c) par if (active) requestAnimationFrame(c); else cancelAnimationFrame(rafId).getElementById('psRain') — un seul canvas par page par défaut. Pour plusieurs instances, extrayez la logique dans une fonction initRain(id) contenant les tableaux drops et splashes en closure, puis appelez-la pour chaque canvas avec un id distinct ('psRain1', 'psRain2', etc.). La fonction globale setupCanvas(id) définie hors de l'IIFE est réutilisable directement.if (window.matchMedia('(prefers-reduced-motion:reduce)').matches) return;. Vous pouvez aussi dessiner une frame statique (quelques gouttes fixes) avant de sortir, pour conserver la présence visuelle sans animation continue.