<main>

    <article class="row">
      
        <h1>Radial Progress Bars</h1>
      
    </article>

    <div class="arrow down"></div>

    <section class="row">
      
        <svg class="radial-progress" data-percentage="82" viewBox="0 0 80 80">
            <circle class="incomplete" cx="40" cy="40" r="35"></circle>
            <circle class="complete" cx="40" cy="40" r="35" style="stroke-dashoffset: 39.58406743523136;"></circle>
            <text class="percentage" x="50%" y="57%" transform="matrix(0, 1, -1, 0, 80, 0)">82%</text>
        </svg>
      
        <svg class="radial-progress" data-percentage="33" viewBox="0 0 80 80">
            <circle class="incomplete" cx="40" cy="40" r="35"></circle>
            <circle class="complete" cx="40" cy="40" r="35" style="stroke-dashoffset: 147.3406954533613;"></circle>
            <text class="percentage" x="50%" y="57%" transform="matrix(0, 1, -1, 0, 80, 0)">33%</text>
        </svg>
      
        <svg class="radial-progress" data-percentage="71" viewBox="0 0 80 80">
            <circle class="incomplete" cx="40" cy="40" r="35"></circle>
            <circle class="complete" cx="40" cy="40" r="35" style="stroke-dashoffset: 63.774330867872806;"></circle>
            <text class="percentage" x="50%" y="57%" transform="matrix(0, 1, -1, 0, 80, 0)">71%</text>
        </svg>
      
        <svg class="radial-progress" data-percentage="24" viewBox="0 0 80 80">
            <circle class="incomplete" cx="40" cy="40" r="35"></circle>
            <circle class="complete" cx="40" cy="40" r="35" style="stroke-dashoffset: 167.13272917097697;"></circle>
            <text class="percentage" x="50%" y="57%" transform="matrix(0, 1, -1, 0, 80, 0)">24%</text>
        </svg>
      
        <svg class="radial-progress" data-percentage="100" viewBox="0 0 80 80">
            <circle class="incomplete" cx="40" cy="40" r="35"></circle>
            <circle class="complete" cx="40" cy="40" r="35" style="stroke-dashoffset: 0;"></circle>
            <text class="percentage" x="50%" y="57%" transform="matrix(0, 1, -1, 0, 80, 0)">100%</text>
        </svg>
      
        <svg class="radial-progress" data-percentage="0" viewBox="0 0 80 80">
            <circle class="incomplete" cx="40" cy="40" r="35"></circle>
            <circle class="complete" cx="40" cy="40" r="35" style="stroke-dashoffset: 219.91148575129;"></circle>
            <text class="percentage" x="50%" y="57%" transform="matrix(0, 1, -1, 0, 80, 0)">0%</text>
        </svg>

    </section>

    <div class="arrow up"></div>

    <article class="row">
      
        <p>The radial progress bars are driven by the <code>data-percentage</code> in each <code>svg</code>, and the radius (<code>r</code>) of its child <code>circle.complete</code>. The percentage is then calculated into the overall circumference (2&pi;r) of the circle, and then spits out the value in <code>stroke-dashoffset</code>.</p>

        <p>Each infographic will individually animate on scroll. The animation begins once the svg is approximately 25% into the window.</p>

        <p>For a no JavaScript option, the <code>stroke-dashoffset</code> is manually inline-styled for each <code>circle.complete</code>. The JavaScript removes the inline styling, automatically calculates the value, and then spits it back out in a smooth transition.</p>
      
    </article>

</main>
@import url(https://fonts.googleapis.com/css?family=Oswald:400|Raleway:400,700,400italic,700italic);

*,
:before,
:after {
    box-sizing: border-box;
}

body {
    background-color: #1d1f20;
    color: #e5e5e5;
    font: 16px/1.25 'Raleway', sans-serif;
    margin: 0;
}

.row {
    margin-left: auto;
    margin-right: auto;
    max-width: 80em;
    padding: 1em;
}

section {
    text-align: center;
}

h1,
h2,
h3,
h4,
h5,
h6 {
    font-family: 'Oswald', sans-serif;
    font-weight: 400;
    text-align: center;
}

p {
    margin-left: auto;
    margin-right: auto;
    max-width: 36em;
}

code {
    color: #f1e399;
    font-style: italic;
}

.arrow {
    border: 2em solid transparent;
    height: 0; 
    margin: 50vh auto;
    width: 0; 
}

.arrow.down {
    border-bottom: none;
    border-top-color: #e5e5e5;
}

.arrow.up {
    border-bottom-color: #e5e5e5;
    border-top: none;
}



/*** RADIAL PROGRESS ***/
/* Circumference = 2πr */
/* π = 3.1415926535898 */
/* r = 35 */
svg.radial-progress {
    height: auto;
    max-width: 200px;
    padding: 1em;
    transform: rotate(-90deg);
    width: 100%;
}
svg.radial-progress circle {
    fill: rgba(0,0,0,0);
    stroke: #fff;
    stroke-dashoffset: 219.91148575129; /* Circumference */
    stroke-width: 10;
}
svg.radial-progress circle.incomplete {
    opacity: 0.25;
}
svg.radial-progress circle.complete {
    stroke-dasharray: 219.91148575129; /* Circumference */
}
svg.radial-progress text {
    fill: #fff;
    font: 400 1em/1 'Oswald', sans-serif;
    text-anchor: middle;
}



/*** COLORS ***/
/* Primary */
svg.radial-progress:nth-of-type(6n+1) circle {
    stroke: #a2ed56;
}

/* Secondary */
svg.radial-progress:nth-of-type(6n+2) circle {
    stroke: #83e4e2;
}

/* Tertiary */
svg.radial-progress:nth-of-type(6n+3) circle {
    stroke: #fd6470;
}

/* Quaternary */
svg.radial-progress:nth-of-type(6n+4) circle {
    stroke: #fca858;
}

/* Quinary */
svg.radial-progress:nth-of-type(6n+5) circle {
    stroke: #fddc32;
}
$(function(){

    // Remove svg.radial-progress .complete inline styling
    $('svg.radial-progress').each(function( index, value ) { 
        $(this).find($('circle.complete')).removeAttr( 'style' );
    });

    // Activate progress animation on scroll
    $(window).scroll(function(){
        $('svg.radial-progress').each(function( index, value ) { 
            // If svg.radial-progress is approximately 25% vertically into the window when scrolling from the top or the bottom
            if ( 
                $(window).scrollTop() > $(this).offset().top - ($(window).height() * 0.75) &&
                $(window).scrollTop() < $(this).offset().top + $(this).height() - ($(window).height() * 0.25)
            ) {
                // Get percentage of progress
                percent = $(value).data('percentage');
                // Get radius of the svg's circle.complete
                radius = $(this).find($('circle.complete')).attr('r');
                // Get circumference (2πr)
                circumference = 2 * Math.PI * radius;
                // Get stroke-dashoffset value based on the percentage of the circumference
                strokeDashOffset = circumference - ((percent * circumference) / 100);
                // Transition progress for 1.25 seconds
                $(this).find($('circle.complete')).animate({'stroke-dashoffset': strokeDashOffset}, 1250);
            }
        });
    }).trigger('scroll');

});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js