<div class="main-view-container">

  <svg version="1.1" xmlns="http://www.w3.org/2000/svg" class="svg-def">

    <filter id="glow1" y="-50%" height="180%">
      <feGaussianBlur stdDeviation="3" result="coloredBlur"></feGaussianBlur>
      <feMerge>
        <feMergeNode in="coloredBlur"></feMergeNode>
        <feMergeNode in="SourceGraphic"></feMergeNode>
      </feMerge>
    </filter>

    <filter id="glow2" y="-50%" height="180%">
      <feGaussianBlur stdDeviation="3" result="coloredBlur"></feGaussianBlur>
      <feMerge>
        <feMergeNode in="coloredBlur"></feMergeNode>
        <feMergeNode in="SourceGraphic"></feMergeNode>
      </feMerge>
    </filter>

  </svg>

  <div class="content">

    <div class="loader">
      <svg id="loaderSVG" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 250 292.6">

        <g id="checkPathLayer" filter="url(#glow2)">
          <path class="path path--filtered" id="checkPath" opacity="0" fill="none" stroke="#EDEDED" stroke-width="7" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M162.8 112.8l-46.7 66.7-.2-.2-28.8-26.9"></path>
        </g>

        <g id="watchThumbPieces" stroke="none" opacity="0">
          <path class="path path--filled" id="watchThumbTopHead" fill="#333333" d="M134.4,51.2h-19c-1.7,0-3-1.3-3-3v-11c0-1.7,1.3-3,3-3h19c1.7,0,3,1.3,3,3v11
		C137.4,49.9,136.1,51.2,134.4,51.2z"></path>
          <path class="path path--filled" id="watchThumbSideHead" fill="#333333" d="M63.3,72.9L49.9,86.3c-1.2,1.2-3.1,1.2-4.2,0l-7.8-7.8c-1.2-1.2-1.2-3.1,0-4.2
		l13.4-13.4c1.2-1.2,3.1-1.2,4.2,0l7.8,7.8C64.5,69.8,64.5,71.7,63.3,72.9z"></path>
          <rect class="path path--filled" id="watchThumbTopBase" x="117.9" y="51.2" fill="#333333" width="14" height="15"></rect>

          <rect class="path path--filled" id="watchThumbSideBase" x="54.9" y="77.4" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -41.9085 68.6215)" fill="#333333" width="14" height="15"></rect>
        </g>

        <g id="start">
          <!-- The face will act as our initial click hood to activate the animation -->
          <circle class="path path--stroked" id="watchRimBg" fill="none" stroke="#333" stroke-width="11" stroke-miterlimit="10" cx="125" cy="146.3" r="75">
          </circle>

          <g class="click-listener">
            <g id="watchRimGlowLayer" filter="url(#glow1)">
              <circle class="path path--filtered" id="watchRimGlow" opacity="0" fill="transparent" stroke="#EDEDED" stroke-width="11" stroke-miterlimit="10" cx="125" cy="146.3" r="75">
              </circle>
            </g>

            <g id="downloadIconContainer">
              <path class="path path--filled" id="downloadIconBase" fill="#333" d="M110.9 166h28.2c1.1 0 2-.9 2-2v-62c0-1.1-.9-2-2-2h-28.2c-1.1 0-2 .9-2 2v62c0 1.1.9 2 2 2z"></path>
              <path class="path path--filled" id="downloadIconArrow" fill="#333" d="M155 162.3c1.1 0 1.3.6.5 1.4l-29.3 27c-.8.7-2.1.7-2.9 0l-29.3-27c-.8-.7-.6-1.4.5-1.4H155z"></path>
            </g>
          </g>
        </g>

      </svg>
    </div>

  </div>

</div>
body {
    background-color: hsla(210, 10%, 8%, 1);
}


$watch-color__primary: hsla(191, 15%, 22%, 1);
$watch-color__glow--loading: hsla(205, 72%, 50%, 1);
$watch-color__glow--complete: hsla(133, 100%, 66%, 1);

.svg-def {
    position: absolute;
    width: 0;
    height: 0;
}


.click-listener {
    cursor: pointer;

    &.animating {
        cursor: default;
    }
}

#watchRimGlow {
    z-index: 4;
    transform-origin: 50% 50%;
    transform: rotateZ(-90deg) rotateX(180deg);   // Rotate so that SVG path starts at top of the watch and the length runs clockwise (the default, (i.e. trigonometric) start point is x-most point of the circle, with length running counterclockwise)
}

.path--filled {
    fill: $watch-color__primary;
}
.path--stroked {
    stroke: $watch-color__primary;
}
.path--filtered {
    stroke: $watch-color__glow--loading;
}

#checkPath {
    &.path--filtered {
        stroke: $watch-color__glow--complete;
    }
}

#loaderSVG {
    width: 20em;
    opacity: 0;   /* animate into view onload after we've positioned */
}
View Compiled
(function(window) {

  // Wire up our references
  var loaderSVG = document.querySelector('#loaderSVG'),
    checkMarkPath = document.querySelector('#checkPath'),
    watchRimBackgroundLayer = document.querySelector('#watchRimBg'),
    watchRimGlow = document.querySelector('#watchRimGlow'),

    clickable = document.querySelector('.click-listener'),

    watchThumbPiecesLayer = document.querySelector('#watchThumbPieces'),
    watchThumbTopBase = document.querySelector('#watchThumbTopBase'),
    watchThumbTopHead = document.querySelector('#watchThumbTopHead'),
    watchThumbSideBase = document.querySelector('#watchThumbSideBase'),
    watchThumbSideHead = document.querySelector('#watchThumbSideHead'),

    watchThumbPieces = [
      watchThumbTopBase,
      watchThumbTopHead,
      watchThumbSideBase,
      watchThumbSideHead
    ],

    // Animate arrow and base into timer center and hand, respectively,
    // when we being the load animation
    centerArrow = document.querySelector('#downloadIconArrow'),
    centerBase = document.querySelector('#downloadIconBase'),

    isAnimating = false,

    ANIMATION_DURATION_MULTIPLIER = 1,

    DURATIONS = {
      makeWatchHand: ANIMATION_DURATION_MULTIPLIER * 0.4,
      makeWatchThumbs: ANIMATION_DURATION_MULTIPLIER * 0.45,
      addRimGlow: ANIMATION_DURATION_MULTIPLIER * 1.1,
      pressThumb: ANIMATION_DURATION_MULTIPLIER * 0.12,
      runWatch: ANIMATION_DURATION_MULTIPLIER * 3.1,
      hideWatchThumbs: ANIMATION_DURATION_MULTIPLIER * 0.2,
      morphHandsToCheck: ANIMATION_DURATION_MULTIPLIER * 1.1
    },

    toggledClasses = {
      animating: 'animating',
      pressThumb: 'press-thumb',
      loadComplete: 'load-complete'
    },

    LABELS = {
      makeWatchHand: 'makeWatchHand',
      makeWatchThumbs: 'makeWatchThumbs',
      addRimGlow: 'addRimGlow',
      pressThumb: 'pressThumb',
      glowComplete: 'glowComplete',
      loadComplete: 'loadComplete'
    },

    tlConfig = {
      repeat: 0, // TODO: Handle resetting the icon at some point?
    },

    masterTl = new TimelineMax(tlConfig);

  TweenMax.set(
    loaderSVG, {
      position: 'absolute',
      top: '50%',
      left: '50%',
      yPercent: -50,
      xPercent: -50,
      opacity: 1
    }
  );

  /**
   * Arrow head rotates 180 degrees, scales down, then translates
   * up to form the center node of the watch hand.
   *
   * At the same time, the arrow base will
   * scale down its X-axis to the width of a watch hand.
   */
  function makeHand(duration) {
    var tl = new TimelineMax();

    TweenMax.set([centerBase, centerArrow], {
      transformOrigin: '50%, 50%'
    });

    tl.add([
      TweenMax.to(
        centerArrow,
        duration, {
          rotation: 180,
          scale: 0.5,
          y: '-=100%',
          ease: Power3.easeOut
        }
      ),

      TweenMax.to(
        centerBase,
        duration, {
          scaleX: 0.2,
          y: '-=10%',
          ease: Power3.easeOut
        }
      )
    ]);

    return tl;
  }

  function setupAnimation() {
    masterTl.set(checkMarkPath, {
      drawSVG: '58%, 58%'
    });
  }

  function makeThumbs(duration) {
    var tl = new TimelineMax();
    tl.set(watchThumbPieces, {
      drawSVG: '0%'
    });
    tl.set(watchThumbPiecesLayer, {
      opacity: 1
    });

    tl.to(
      [watchThumbTopBase, watchThumbSideBase],
      duration / 2, {
        drawSVG: '100%',
        ease: Linear.easeNone
      }
    );
    tl.to(
      [watchThumbTopHead, watchThumbSideHead],
      duration / 2, {
        drawSVG: '100%',
        ease: Back.easeOut.config(1.7)
      }
    );
    return tl;
  }

  /**
   * Press the top thumb
   */
  function pressThumb(duration) {

    var thumbPressTl = new TimelineMax(),
      boundingRect = watchThumbTopBase.getBoundingClientRect(),
      yDist = boundingRect.top - boundingRect.bottom;

    // simultaneously undraw top thumb base and
    // follow through by down-shifting the thumb head
    thumbPressTl.add([
      TweenMax.to(
        watchThumbTopBase,
        duration * (1 / 8), {
          drawSVG: '0%'
        }),
      TweenMax.to(watchThumbTopHead, duration, {
        y: '-=' + yDist
      })
    ]);

    // restore the thumb
    thumbPressTl.add([
      TweenMax.to(watchThumbTopBase, duration, {
        drawSVG: '100%'
      }),
      TweenMax.to(watchThumbTopHead, duration, {
        y: '+=' + yDist
      })
    ]);

    return thumbPressTl;
  }

  /**
   * Propagate the glow filter around the watch face.
   */
  function addRimGlow(duration) {

    var rimGlowTl = new TimelineMax();

    rimGlowTl.add(TweenMax.set(watchRimGlow, {
      drawSVG: '0%'
    }));
    rimGlowTl.add(TweenMax.set(watchRimGlow, {
      opacity: 1
    }));
    rimGlowTl.add(TweenMax.to(watchRimGlow, duration, {
      drawSVG: '100%'
    }));
    return rimGlowTl;
  }

  function startWatch(duration, rimGlowTl) {
    var tl = new TimelineMax();

    tl.set(centerBase, {
      transformOrigin: '50% 90%'
    });

    tl.add([
      TweenMax.to(centerBase, duration, {
        rotation: 360,
        ease: Linear.easeNone
      }),

      // undraw the glow around the rim
      //TweenMax.to(watchRimGlowLayer, 0.1, {drawSVG: '100% 0%'}),
      //function () { rimGlowTl.reverse(0); }
      TweenMax.to(watchRimGlow, duration, {
        drawSVG: '0%',
        ease: Linear.easeNone
      })

    ]);

    return tl;
  }

  function hideThumbs(duration) {
    var tl = new TimelineMax();

    tl.to([watchThumbSideHead, watchThumbTopHead], duration / 2, {
      drawSVG: '0%',
      ease: Power3.easeOut
    });
    tl.to([watchThumbSideBase, watchThumbTopHead], duration / 2, {
      drawSVG: '0%',
      ease: Power3.easeOut
    });
    tl.set(watchThumbPieces, {
      opacity: 0,
      zIndex: -1
    });

    return tl;
  }

  /**
   * -- Center node becomes undrawn, hand shrinks to smaller dot and pops up
   * -- Translate dot back down to base of the checkmark and, alas, draw the checkmark
   */
  function morphHandsToCheck(duration) {
    var tl = new TimelineMax();

    tl.add([
      TweenMax.to(
        centerArrow,
        duration * 0.2, {
          opacity: 0,
          zIndex: -1,
          ease: Power3.easeOut
        }
      ),

      TweenMax.set(centerBase, {
        transformOrigin: '50% 90%'
      }),

      TweenMax.to(
        centerBase,
        duration * 0.2, {
          scaleY: 0.2,
          ease: Power3.easeOut
        }
      )
    ]);

    tl.add(
      TweenMax.to(centerBase, duration * 0.2, {
        y: '-=200%',
        ease: Power3.easeOut
      })
    );

    tl.add(
      TweenMax.to(
        centerBase,
        duration * 0.2, {
          y: '+=200%',
          ease: Power3.easeOut,
          onComplete: function() {
            TweenMax.set(centerBase, {
              opacity: 0,
              zIndex: -1
            });
          }
        }
      )
    );

    // Prepare the checkmark path and then draw it
    tl.set(checkMarkPath, {
      opacity: 0,
      drawSVG: '0%',
      zIndex: 5
    });

    tl.add(
      TweenMax.fromTo(
        checkMarkPath,
        duration * 0.4, {
          opacity: 1,
          drawSVG: '50% 50%'
        }, // meet in middle
        {
          drawSVG: '100%', // draw out from middle
          onComplete: resetTl
        }
      )
    );
    return tl;
  }

  function animateLoader() {

    var rimGlowTl = addRimGlow(DURATIONS.addRimGlow);
    setupAnimation();
    masterTl.add(makeHand(DURATIONS.makeWatchHand), LABELS.makeWatchHand);
    masterTl.add(makeThumbs(DURATIONS.makeWatchThumbs), LABELS.makeWatchThumbs);
    masterTl.add(rimGlowTl, LABELS.makeWatchThumbs + '+=0.35');
    masterTl.addLabel(LABELS.addRimGlow);
    masterTl.add(pressThumb(DURATIONS.pressThumb), LABELS.addRimGlow + '+=0.35');
    masterTl.addLabel(LABELS.pressThumb);
    masterTl.add(startWatch(DURATIONS.runWatch, rimGlowTl), LABELS.pressThumb + '+=0.1');
    masterTl.addLabel(LABELS.loadComplete);
    masterTl.add(
      [
        hideThumbs(DURATIONS.hideWatchThumbs),
        morphHandsToCheck(DURATIONS.morphHandsToCheck)
      ],
      LABELS.loadComplete + '+=0.2'
    );

  }

  function resetTl() {
    clickable.classList.remove(toggledClasses.animating);
    isAnimating = false;

    // TODO: Return the button to its original state here
  }

  function init() {
    clickable.addEventListener('mouseup', function() {
      if (!isAnimating) {
        isAnimating = true;
        clickable.classList.add(toggledClasses.animating);
        animateLoader();
      }
    }, false);
  }

  window.addEventListener('load', function() {
    init();
  }, false);

}(window));

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. //cdnjs.cloudflare.com/ajax/libs/gsap/1.16.1/TweenMax.min.js
  2. //s3-us-west-2.amazonaws.com/s.cdpn.io/16327/DrawSVGPlugin.js?r=12