<svg class="svg-def">
  <defs>

    <!-- a glow that takes on the stroke color of the object it's applied to -->
    <filter id="strokeGlow" y="-10" x="-10" width="250" height="150">

      <feOffset in="StrokePaint" dx="0" dy="0" result="centeredOffset"></feOffset>

      <feGaussianBlur in="centeredOffset" stdDeviation="2" result="blur1"></feGaussianBlur>
      <feGaussianBlur in="centeredOffset" stdDeviation="5" result="blur2"></feGaussianBlur>
      <feGaussianBlur in="centeredOffset" stdDeviation="15" result="blur3"></feGaussianBlur>

      <feMerge>
        <!-- this contains the offset blurred image -->
        <feMergeNode in="blur1"></feMergeNode>
        <feMergeNode in="blur2"></feMergeNode>
        <feMergeNode in="blur3"></feMergeNode>

        <!-- this contains the element that the filter is applied to -->
        <feMergeNode in="SourceGraphic"></feMergeNode>
      </feMerge>
    </filter>

  </defs>
</svg>


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

  <div class="loader-container">

    <svg id="loader" xmlns="http://www.w3.org/2000/svg" width="250px" height="200px" viewBox="0 0 250 200">
      <svg style="overflow: visible">
      <path id="jump" fill="none" stroke="#33FFFF" stroke-width="10" stroke-linecap="round" stroke-miterlimit="10" d="M55.5 98.5c0-35.3 31.3-64 70-64s70 28.7 70 64"></path>
      </svg>

      <g fill="none" stroke-width="1" stroke="#33FFFF" stroke-linecap="round" stroke-miterlimit="10">
        <ellipse id="circleL" cx="55.2" cy="102.5" rx="21.7" ry="5.5"></ellipse>
        <ellipse id="circleR" cx="195.2" cy="103.5" rx="21.7" ry="5.5"></ellipse>
      </g>
    </svg>


  </div>

</div>
$loader-svg-width: 250px;
$loader-svg-height: 200px;


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

body,
.main-view-container {
    width: 100vw;
    height: 100vh;
    margin: 0;
    padding: 0;
    background-color: hsla(150, 2%, 16%, 1);
}

.loader-container {
    position: absolute;
    top: 50%;
    left: 50%;
    margin-left: -$loader-svg-width / 2;
    margin-top: -$loader-svg-height / 2;
}

.reflection {
    transform-origin: 50% 110%;
    transform: scaleY(-1);
    opacity: 0.09;
}
View Compiled
    /**
     * Set the filter property for an element, accounting for both
     * 'webkitFilter' and 'filter'
     *
     * example: setFilter('url("/svg/filters/gooey-effects.svg#goo")', menuContainerElem);
     */
    var setFilter = function setFilter(path, elem) {
      elem.style.filter = path;
      elem.style.webkitFilter = path;
    };

    var loaderContainer = document.querySelector('.loader-container'),
      loaderSVG = document.querySelector('#loader'),
      circleL = document.querySelector('#circleL'),
      circleR = document.querySelector('#circleR'),
      jumpArc = document.querySelector('#jump'),

      BASE_DURATION_MULTIPLIER = 1.0;

    var jumpArcReflection = jumpArc.cloneNode();

    jumpArcReflection.setAttribute('class', 'reflection'); // setAttribute needs to be used for classing SVG in JS
    loaderSVG.appendChild(jumpArcReflection);

    setFilter('url("#strokeGlow")', jumpArc);
    setFilter('url("#strokeGlow")', jumpArcReflection);

    var masterTL = new TimelineMax({
      repeat: -1
    });

    function jump() {

      var jumpTL = new TimelineMax();

      jumpTL
        .set(
          [jumpArc, jumpArcReflection], {
            drawSVG: '0% 0%'
          }
        )
        .set(
          [circleL, circleR], {
            attr: {
              rx: 0,
              ry: 0
            }
          }
        )
        .to(
          [jumpArc, jumpArcReflection],
          BASE_DURATION_MULTIPLIER * 0.4, {
            drawSVG: '0% 30%',
            ease: Linear.easeNone
          }
        )

      // scale up the ripple ovals (with x scaling a bit more since, you know, it's a horizontal oval :-) )
      .to(
        circleL,
        BASE_DURATION_MULTIPLIER * 2, {
          attr: {
            rx: '+=30',
            ry: '+=10'
          },
          opacity: 0, // ripple, then fade out
          ease: Power1.easeOut
        },
        '-=0.1'
      )

      .to(
        [jumpArc, jumpArcReflection],
        BASE_DURATION_MULTIPLIER * 1.0, {
          drawSVG: '50% 80%',
          ease: Linear.easeNone
        },
        '-=1.9'
      )

      .to(
        [jumpArc, jumpArcReflection],
        BASE_DURATION_MULTIPLIER * 0.7, {
          drawSVG: '100% 100%',
          ease: Linear.easeNone
        },
        '-=0.9'
      )

      // finish by animating the right circle ripple
      .to(
        circleR,
        BASE_DURATION_MULTIPLIER * 2, {
          attr: {
            rx: '+=30',
            ry: '+=10'
          },
          opacity: 0, // ripple, then fade out
          ease: Power1.easeOut
        },
        '-=0.5'
      );

      jumpTL.timeScale(3);

      return jumpTL;

    }

    window.onload = function() {

      masterTL.add(jump());

    };
Run Pen

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=5