Animated Buttons Experiment

animated buttons experiment

by Travis Almand

HTML

<p>CSS Effects</p>
<button class="primary-btn" data-anibtn="expand">expand</button>
<button class="primary-btn" data-anibtn="stretch">stretch</button>
<button class="primary-btn" data-anibtn="fall">fall</button>
<button class="primary-btn" data-anibtn="blur">blur</button>
<button class="primary-btn" data-anibtn="bounceExpand">bounceExpand</button>

<br />

<button class="secondary-btn" data-anibtn="shake">shake</button>

<br />

<p>SVG filters</p>
<button class="primary-btn" data-anibtn="displaceSvg">svg displace</button>
<button class="primary-btn" data-anibtn="explodeSvg">svg explode</button>





<svg id="svg-filters">
  <filter id="displaceFilter">
    <feTurbulence type="turbulence" baseFrequency="0.05" numOctaves="4" result="turbulence" stitchTiles="stitch" />
    <feDisplacementMap id="displaceSvgMap" in2="turbulence" in="SourceGraphic" scale="0" xChannelSelector="R" yChannelSelector="G" />
  </filter>
  <filter id="explodeFilter">
    <feTurbulence type="turbulence" baseFrequency="0.75" numOctaves="4" result="turbulence" stitchTiles="stitch" />
    <feDisplacementMap id="explodeSvgMap" in2="turbulence" in="SourceGraphic" scale="0" xChannelSelector="R" yChannelSelector="G" />
  </filter>
</svg>

SCSS

$primary-color: green;
$secondary-color: red;

@mixin btns($color) {
  background-color: $color;
  border: 1px solid darken($color, 10%);
  color: #fff;
  
  &:hover {
    background-color: darken($color, 10%);
  }
}

#svg-filters {
  position: absolute;
}

button {
  background: none;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  display: inline-block;
  margin: 10px;
  opacity: 1;
  padding: 5px 10px;
}

.primary-btn {
  @include btns($primary-color);
}
.secondary-btn {
  @include btns($secondary-color);
}

[class*="BtnDup"] {
  pointer-events: none;
  position: absolute;
  z-index: 666;
}

.expandBtnDup {
  animation: expandBtn 0.25s;
}
@keyframes expandBtn {
  0% {
    opacity: 1;
    transform: scale3d(1, 1, 1);
  }
  100% {
    opacity: 0;
    transform: scale3d(2, 2, 1);
  }
}

.stretchBtnDup {
  animation: stretchBtn 0.25s;
}
@keyframes stretchBtn {
  0% {
    opacity: 1;
    transform: scale3d(1, 1, 1);
  }
  100% {
    opacity: 0;
    transform: scale3d(3, 0.25, 1);
  }
}

.fallBtnDup {
  animation: fallBtn 0.25s;
}
@keyframes fallBtn {
  0% {
    opacity: 1;
    transform: translate3d(0, 0, 0) rotate3d(0, 0, 0, 0deg);
  }
  100% {
    opacity: 0;
    transform: translate3d(0, 100%, 0) rotate3d(0, 0, 1, 10deg);
  }
}

.bounceExpandBtnDup {
  animation: bounceExpandBtn 0.25s forwards;
}
@keyframes bounceExpandBtn {
  0% {
    transform: scale3d(1, 1, 1);
  }
  25% {
    transform: scale3d(1.25, 1.25, 1);
  }
  50% {
    opacity: 1;
    transform: scale3d(1, 1, 1);
  }
  100% {
    opacity: 0;
    transform: scale3d(1.5, 1.5, 1);
  }
}

.shakeBtnDup {
  animation: shakeBtn 0.5s forwards;
}
@keyframes shakeBtn {
  0% {
    transform: translate3d(0, 0, 0) scale3d(1.25, 1.25, 1);
  }
  20% {
    transform: translate3d(-50%, 0, 0) scale3d(1.25, 1.25, 1);
  }
  40% {
    transform: translate3d(50%, 0, 0) scale3d(1.25, 1.25, 1);
  }
  50% {
    opacity: 1;
  }
  60% {
    transform: translate3d(-50%, 0, 0) scale3d(1.25, 1.25, 1);
  }
  80% {
   ...

JavaScript

(function animateTheBtns () {
  let $aniBtns = document.querySelectorAll('[data-anibtn]');

  for (let i = 0; i < $aniBtns.length; i++) {
  	let $element = $aniBtns[i];
    let type = $aniBtns[i].dataset.anibtn
    let $map = document.querySelector('#' + type + 'Map');
    let svgTimer;
    
    $element.addEventListener('click', function () {
      let xPos = this.getBoundingClientRect().left;
      let yPos = this.getBoundingClientRect().top;
      let marginLeft = parseFloat(window.getComputedStyle(this, null).getPropertyValue('margin-left'));
      let marginTop = parseFloat(window.getComputedStyle(this, null).getPropertyValue('margin-top'));
      let $dup = this.cloneNode('deep');
      
      this.style.pointerEvents = "none";
      
      $dup.classList.add(type + 'BtnDup');
      $dup.style.left = (xPos - marginLeft) + 'px';
      $dup.style.top = (yPos - marginTop) + 'px';
      document.body.appendChild($dup);
      
      if ($map) {
	      $map.scale.baseVal = 0;
      }
	    window.clearInterval(svgTimer);

      $dup.addEventListener('animationend', function () {
      	$element.style.pointerEvents = "auto";
        document.body.removeChild($dup);
        
        if (svgTimer) {
        	$map.scale.baseVal = 0;
	        window.clearInterval(svgTimer);
        }
      });
      
      if ($map) {
      	svgTimer = window.setInterval(function () {
        	$map.scale.baseVal += 1;
        }, 10);
      }
    });
  }
})();