Chargement✨ Premium

Circular Progress

Arc SVG 60 px, stroke-dashoffset 157→0→−157 en 2 s ease-in-out : l'arc de 4 px s'emplit puis se vide en boucle continue sur une piste à 20 %. Zéro JS.

SVGProgress

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. Chargement comprend 29 effets, dont 4 gratuits. Découvrir la catégorie →

3 exemples d'usage

Téléversement d'un fichier vidéo — le pourcentage au centre de l'arc — Circular Progress exemple 1

① Téléversement d'un fichier vidéo — le pourcentage au centre de l'arc

QuandUne médiathèque ou un outil de partage reçoit un fichier lourd (vidéo 4K de 940 Mo) : le navigateur connaît la part déjà envoyée (événement progress de l'upload) et l'affiche pendant les 30 à 90 s que dure le transfert, à côté du débit et du temps restant.
PourquoiC'est le seul loader du lot qui sait afficher une valeur : l'arc trace la part envoyée et laisse son centre vide pour le chiffre — « 63 % » et un arc rempli aux 63 % occupent le même point de regard, ce qu'aucun spinner rotatif ni série de points ne peut offrir. Le remplissage horaire depuis 12 h (rotate(-90deg)) est le code visuel universel du téléchargement, et à 120 px le trait de 8 px reste lisible de loin, là où un spinner de 20 px dans un bouton ne se lit plus.
Réglageswidth: 120px; height: 120px sur .loader-circular-progress (rayon 25 inchangé : stroke-dasharray: 157 et stroke-width: 4 gardés, soit un trait de 8 px à l'écran) ; mode déterminé de la FAQ : animation: none sur .progress, transition: stroke-dashoffset .4s ease, et en JS à chaque événement progress : circle.style.strokeDashoffset = 157 * (1 - pourcentage / 100) ; le <svg> vit dans un conteneur position: relative de 120 px et le pourcentage est centré par-dessus (position: absolute; inset: 0; display: flex; align-items: center; justify-content: center) ; couleurs d'origine (#6366f1, piste à 20 %).
Export PDF d'un rapport de 12 pages — « Page 3 sur 12 » — Circular Progress exemple 2

② Export PDF d'un rapport de 12 pages — « Page 3 sur 12 »

QuandUn éditeur de documents génère le PDF haute définition d'un rapport long (graphiques vectoriels, images 300 dpi) : l'export avance page par page, chaque page rendue est confirmée au client, et l'opération dure 15 à 30 s — trop long pour un simple spinner, trop court pour une file d'attente.
PourquoiL'export est une suite d'étapes discrètes : chaque page validée avance l'arc d'un douzième de tour, et la part déjà faite se lit géométriquement — l'arc qui atteint 6 h dit « à mi-chemin » avant même qu'on lise le texte. Un spinner qui tourne ne saurait pas dire qu'il reste neuf pages ; une barre linéaire le dirait, mais sans centre libre pour le pourcentage ni la compacité d'un disque de 120 px dans un panneau étroit. Les bouts arrondis (stroke-linecap: round) adoucissent chaque palier de 8,3 %, et la transition lisse les sauts entre pages.
Réglageswidth: 120px; height: 120px ; stroke-width: 3 (unités du viewBox : 6 px à l'écran, plus fin que le trait par défaut dans un panneau chargé) ; animation: none sur .progress + transition: stroke-dashoffset .4s ease ; en JS, à chaque page terminée : circle.style.strokeDashoffset = 157 * (1 - pagesFaites / 12) (paliers de 157/12 ≈ 13,1 px) ; rayon 25 et stroke-dasharray: 157 inchangés ; le libellé « Page N sur 12 » sous l'arc et le pourcentage au centre partagent le même compteur ; couleurs d'origine.
Actualisation de widget — carte d'un tableau de bord — Circular Progress exemple 3

③ Actualisation de widget — carte d'un tableau de bord

QuandUn widget de dashboard (graphique, KPI) se recharge après un filtre ; l'indicateur est centré sur la zone de données avec un overlay semi-transparent.
PourquoiLes deux couches (piste + arc animé) signalent clairement le chargement sans cacher les données partielles encore visibles. La boucle de 2 s est perceptible sans être anxiogène pour une requête de 1 à 3 s.
Réglagesposition: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%) sur un overlay background: rgba(10,10,15,.7) couvrant le widget ; stroke-width: 3 pour un rendu plus délicat dans une interface dense.

Comment ça marche

L'effet repose sur un <svg viewBox="0 0 60 60"> de 60×60 px contenant deux <circle> superposés, cx="30" cy="30" r="25". Les deux partagent fill: none, stroke-width: 4 et stroke-linecap: round. Le premier, .bg, reçoit stroke: rgba(99,102,241,.2) — une piste indigo à 20 %. Le second, .progress, reçoit stroke: #6366f1 et le mécanisme d'animation. La circonférence 2π×25 ≈ 157,08 px est arrondie à 157 dans le code.

.progress démarre avec stroke-dasharray: 157 (une seule valeur : tiret et espace valent chacun 157 px) et stroke-dashoffset: 157, ce qui masque entièrement l'arc. L'animation circularProgress2s ease-in-out infinite — parcourt trois états : 0 % (offset 157, arc invisible), 50 % (offset 0, cercle complet), 100 % (offset −157, arc invisible à nouveau). De 0 à 50 %, la queue du tiret remonte vers 12 h : l'arc croît dans le sens horaire depuis 12 h. De 50 à 100 %, la tête du tiret progresse dans le sens horaire : l'arc se vide depuis 12 h. Mesuré : offset 156,5 px à t≈50 ms, 7,2 px à t≈1 000 ms (presque plein), −118 px à t≈2 000 ms (vidange aux trois-quarts). La transition 100 %→0 % est imperceptible : offset −157 et offset 157 donnent le même rendu vide.

transform: rotate(-90deg) avec transform-origin: center center décale le point de départ du tracé de 3 h (position SVG par défaut) à 12 h, rendant le chargement naturellement ascendant. L'animation est entièrement CSS — le JS vendu est vide. Les couleurs sont câblées en dur (#6366f1, rgba(99,102,241,.2)) : aucune variable CSS du site n'est requise.

Accessibilité

  • prefers-reduced-motion absent : l'animation tourne en boucle infinie quelle que soit la préférence. Ajoutez @media (prefers-reduced-motion: reduce) { .loader-circular-progress .progress { animation: none } } pour stopper l'arc ; l'arc plein statique (offset 0) est un repli honnête.
  • L'élément est un <svg> muet sans rôle ni texte alternatif : aucun lecteur d'écran n'annoncera le chargement. Ajoutez role="status" sur le <svg> et un <title> caché (Chargement en cours), ou un texte adjacent aria-live="polite" qui annonce la fin de l'opération.
  • aria-busy n'est pas présent sur le conteneur de contenu en cours de chargement. Posez aria-busy="true" sur la zone concernée pendant le chargement et retirez-le à la fin.
  • Contraste : #6366f1 sur #0a0a0f mesure 3,0:1 (seuil WCAG 1.4.11 pour les éléments graphiques : 3:1) — limite acceptable sur fond sombre, insuffisant sur fond clair (1,9:1 sur blanc). Sur fond clair, portez la couleur à #4f46e5 (contraste ≥ 4,5:1).

Compatibilité navigateur

SVG stroke-dashoffset animé en CSS. Aucune dépendance JS, aucune variable CSS externe.

Chrome 53+✓ Complet
Firefox 49+✓ Complet
Safari 9.1+✓ Complet
Edge 15+✓ Complet
Mobile iOS 9.3+✓ Complet
Android Chrome 53+✓ Complet

Sans CSS animations (navigateurs très anciens) : arc statique à l'offset initial 157 — invisible. Ajoutez stroke-dashoffset: 0 dans un @supports not (animation-name: x) {} pour afficher un arc plein statique.

Le code

Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :

index.html — structure
<svg class="loader-circular-progress" viewBox="0 0 60 60">
  <circle class="bg" cx="30" cy="30" r="25"></circle>
  <circle class="progress" cx="30" cy="30" r="25"></circle>
</svg>
🔒 Débloquer le code complet — à partir de 2,99 € le 1er mois

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éfautEffet
Taille (width / height sur .loader-circular-progress) 60 px Redimensionne l'ensemble. Si vous changez aussi le rayon r, recalculez stroke-dasharray et les valeurs de l'animation : pour r=12, circonférence ≈ 75, remplacez les trois occurrences de 157 par 75.
Rayon (attribut r sur les deux <circle>) 25 Doit rester inférieur à 30 pour tenir dans le viewBox 60×60 avec le trait de 4 px. Toute modification implique de recalculer stroke-dasharray = 2π×r.
Épaisseur du trait (stroke-width) 4 2 px pour un rendu fin (mobile, petits espaces) ; 6 px pour un look affirmé. La valeur s'applique aux deux cercles.
Couleur de progression (stroke: #6366f1 sur .progress) #6366f1 Couleur de l'arc animé. Répercutez la même teinte dans la piste : .bg { stroke: rgba(R,G,B,.2) }.
Opacité de la piste (.bg { stroke: rgba(99,102,241,.2) }) 0,2 (20 %) 0 pour supprimer la piste ; 0,35 pour une piste plus marquée qui renforce la lisibilité sur fond clair.
Durée du cycle (animation: 2s) 2 s 1 s : sensation d'urgence ; 3 s : attente longue et fluide. Modifiez dans la déclaration animation sur .progress.
Easing (ease-in-out) ease-in-out linear pour une vitesse constante (téléchargement) ; ease-in pour une accélération vers la fin (impression d'aboutissement).

FAQ

Indéterminé. Le stroke-dashoffset oscille en boucle infinie entre 157 et −157 — il ne reçoit aucune valeur dynamique. Pour un vrai indicateur de 0 à 100 %, supprimez l'animation, ajoutez transition: stroke-dashoffset 0.4s ease et en JS : circle.style.strokeDashoffset = 157 * (1 - progress / 100). À progress = 100, offset = 0 = cercle plein.
Gradient Ring est un <div> avec conic-gradient et border-radius: 50% qui tourne en continu — l'anneau est toujours plein, il pivote. Circular Progress est un <svg> dont l'arc se remplit puis se vide via stroke-dashoffset — l'arc croît et disparaît en boucle. Gradient Ring signale une attente indéfinie par rotation ; Circular Progress simule une progression qui recommence, ce qui convient mieux aux opérations courtes répétées (envois, rafraîchissements) et permet d'en faire un vrai barre de progression en retirant simplement l'animation (voir question ci-dessus).
Non. À 100 %, stroke-dashoffset = −157 donne le même rendu visuel qu'à 0 % (stroke-dashoffset = 157) : l'arc est invisible dans les deux cas. La transition est donc imperceptible, comme vérifié dans les mesures (offset 156,5 px à t≈50 ms = quasi vide dès le début du cycle suivant).