<div id="submit-button" class="submit-container">
  <div @click="clickedSubmit" 
       ref="submit-btn"
       :class="buttonClass"
       class="submit-btn">
    <span v-show="!clicked">Submit</span>    
    <span v-show="loaded">✔</span>
  </div>
  
  <!--  grey circle  -->
  <svg v-if="loading" class="loader-svg">
    <path stroke="#CCCCCC" fill="none" stroke-width="4" d="M25,2.5A22.5,22.5 0 1 1 2.5,25A22.5,22.5 0 0 1 25,2.5"></path>
  </svg>
  
  <!--  green circle  -->
  <!--  circumference  -->
  <!-- 3.1416 * 50 = ~157 -->
  <svg v-if="loading" class="loader-svg">
    <path stroke="#20BF7E" fill="none" stroke-width="4" d="M25,2.5A22.5,22.5 0 1 1 2.5,25A22.5,22.5 0 0 1 25,2.5" stroke-dasharray="157" :stroke-dashoffset="loaderOffset"></path>
  </svg>
</div>
.submit-container {
  position: relative;
  margin: 80px;
}

.submit-btn {
  width: 100px;
  color: #20BF7E;
  font-size: 20px;
  font-family: Arial;
  text-align: center;
  text-decoration: none;
  padding: 10px 20px 10px 20px;
  border: solid #20BF7E 4px;
  text-decoration: none;
  cursor: pointer;
  border-radius: 25px;
  transition: width .3s, margin .3s, background-color .3s, color .3s;
}

.submit-btn:hover {
  background-color: #20BF7E;
  color: white;
}

.submit-btn.round {
  margin-left: 50px;
  border-color: #CCCCCC;
  background: white;
  
  /*  circle should be 50px width & height */
  /* borderLeft + paddingLeft + paddingRight + borderRight  */
  /* 4 + 20 + 20 + 4 = 48 + 2 = 50 */
  width: 2px; 
  /* borderTop + paddingTop + paddingBottom + borderBottom  */
  /* 4 + 10 + 10 + 4 = 28 + 22 = 50 */
  height: 22px;
}
.submit-btn.loaded {
  color: white;
  background-color: #20BF7E;
}

.loader-svg {
  pointer-events: none;
  position: absolute;
  top: 0px;
  left: 50px;
  width: 50px; 
  height: 50px; 
  transform-origin: 25px 25px 25px;
}
const circumference = 157
new Vue({
  el: '#submit-button',
  data: {
    clicked: false,    
    loading: false,
    loaded: false,
    loaderOffset: circumference
  },
  methods: {
    clickedSubmit () {
      // no actions until loading finishes
      if (this.clicked && !this.loaded)
        return
      
      // restart when button finished the animation
      if (this.loaded) {
        this.restart()
        return
      }
      
      // start loading animation 
      this.clicked = true
      
      // when css transition ends, execute animateLoader method
      this.$refs['submit-btn'].addEventListener("transitionend", 
        this.animateLoader, false);
    },
    animateLoader () {
      this.loading = true
      // remove transition end event listener
      this.$refs['submit-btn'].removeEventListener("transitionend", 
        this.animateLoader, false);
      
      // animate the loaderOffset property,
      // on production this should be replaced 
      // with the real loading progress
      TweenLite.to(this, 3, {
        loaderOffset: 0,
        ease: Power4.easeInOut,
        onComplete: this.completeLoading // execute this method when animation ends
      })
    },
    completeLoading () {
      this.loading = false
      this.loaded = true
    },
    restart () {
      this.clicked = false
      this.loaded = false
      this.loaderOffset = circumference
    }
  },
  computed: {
    buttonClass () {
      if (this.loaded) {
        return 'loaded'
      }
      
      if (this.clicked) {
        return 'round'
      }
      
      return ''
    }
  }
})

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenLite.min.js