<div id="app"></div>
@import url("https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700");

body {
  padding: 0;
  margin: 0;
}

#app {
  font-family: "Open Sans", sans-serif;
  background-image: linear-gradient(to bottom right,
    #ff4e4e 0 15%, #f8ff64 40% 60%, #00ce1a 85% 100%);
  width: 100vw;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: nowrap;
  overflow: hidden;
}

.btn-splatter {
  color: #ffffff;
  font-family: inherit;
  font-size: 24px;
  font-weight: bold;
  background-color: #4487ed;
  width: 18rem;
  height: 6rem;
  border: 0;
  outline: 0;
  padding: 0;
  margin: 0;
  flex-shrink: 0;
  position: relative;
  border-radius: 0.9rem;
  box-shadow: 0 0.3rem 0.6rem rgba(0, 0, 0, 0.6);
  text-shadow: 0 1px rgba(0, 0, 0, 0.2);
  user-select: none; /* Thanks to @SplittyDev */
  transition:
    background-color 96ms cubic-bezier(0.18, 0.89, 0.32, 1.28),
    box-shadow 96ms cubic-bezier(0.18, 0.89, 0.32, 1.28),
    transform 96ms cubic-bezier(0.18, 0.89, 0.32, 1.28);
}

.btn-splatter:focus {
  outline: 0;
}

.btn-splatter:active {
  background-color: #2770df;
  box-shadow: 0 0 0 rgba(0, 0, 0, 0.5);
  transform: translateY(0.1rem) scale(0.975);
  transition:
    background-color 96ms ease-out,
    box-shadow 96ms ease-out,
    transform 96ms ease-out;
}

.splatter {
  position: absolute;
  top: 0;
  left: 0;
}

.splatter-round, .splatter-ring, .splatter-star, .splatter-square {
  position: absolute;
}

.splatter-round {
  width: 1rem;
  height: 1rem;
  background-color: #1da5ff;
  border-radius: 50%;
}

.splatter-ring {
  width: 0.8rem;
  height: 0.8rem;
  border: solid 0.35rem #0099e6;
  border-radius: 50%;
}

.splatter-star {
  width: 1.8rem;
  height: 1.8rem;
  background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" fill="%23006dd0" viewBox="0 0 24 24"%3E%3Cpath%20d%3D%22M12%2C17.27L18.18%2C21L16.54%2C13.97L22%2C9.24L14.81%2C8.62L12%2C2L9.19%2C8.62L2%2C9.24L7.45%2C13.97L5.82%2C21L12%2C17.27Z%22%3E%3C%2Fpath%3E%3C/svg%3E');
}

.splatter-square {
  width: 1rem;
  height: 1rem;
  background-color: #0c6bc2;
}
import React, { useState, useEffect, useRef } from 'https://esm.sh/react@18.2.0'
import ReactDOM from 'https://esm.sh/react-dom@18.2.0'
import gsap from 'https://cdn.skypack.dev/gsap@3.11.4';

enum SplatterType {
  SplatterRound,
  SplatterRing,
  SplatterStar,
  SplatterSquare,
}

enum SplatterPosition {
  X,
  Y,
}

enum SplatterTB {
  Top,
  Bottom,
}

enum SplatterLR {
  Left,
  Right,
}

type SplatterStyle = {
  top: string,
  left: string,
  transform: string,
}

type SplatterProps = {
  type: SplatterType,
  style: SplatterStyle,
}

const SplatterShapeIcon: ReactElement = (props: SplatterProps) => {
  const [display, setDisplay] = useState(true);
  const refSplatter = useRef(null);
  
  const type: SplatterType = props.type;
  const style: SplatterStyle = props.style;
  
  let className = '';
  
  if (type === SplatterType.SplatterRound) {
    className = 'splatter-round';
  } else if (type === SplatterType.SplatterRing) {
    className = 'splatter-ring';
  } else if (type === SplatterType.SplatterStar) {
    className = 'splatter-star';
  } else if (type === SplatterType.SplatterSquare) {
    className = 'splatter-square';
  }
  
  let angle = Math.floor(Math.random() * 359) + 1;
  let toX = `${Math.cos(angle) * 3}em`;
  let toY = `${Math.sin(angle) * 3}em`;
  
  useEffect(() => {
    gsap.to(refSplatter.current, 0.8, {
      opacity: 0,
      translateX: toX,
      translateY: toY,
      scale: 0,
      rotate: 270,
      filter: 'blur(0.2rem)',
      onComplete: () => {
        setDisplay(false);
      },
    });
  }, [refSplatter]);
  
  if (!display) {
    return null;
  }
  
  return (
    <div ref={refSplatter} className={className} style={style}></div>
  );
};

const FancyExplodingButton: ReactElement = () => {
  const [splatters, setSplatters]: Array<SplatterProps> = useState([]);

  const getSizeByType: number = (type: SplatterType) => {
    switch (type) {
      case SplatterType.SplatterRound:
      case SplatterType.SplatterSquare:
        return `${1 / 2}em`;
      case SplatterType.SplatterRing:
        return `${1.5 / 2}em`;
      case SplatterType.SplatterStar:
        return `${1.8 / 2}em`;
    }
  };
  
  const createSplatter: void = (count: number, w: number, h: number) => {
    let tmpSplatters: Array<SplatterProps> = JSON.parse(JSON.stringify(splatters));

    for (let i: number = 0; i < count; i++) {
      let type: SplatterType = Math.floor(Math.random() * 4);
      let splatterPosition: SplatterPosition = Math.round(Math.random());
      let xy: SplatterPosition = Math.round(Math.random());
      let tb: SplatterTB = Math.round(Math.random());
      let lr: SplatterLR = Math.round(Math.random());
      let xPosition: number = (xy === SplatterPosition.X)
                            ? Math.floor(Math.random() * w)
                            : (lr === SplatterLR.Left ? 0 : w);
      let yPosition: number = (xy === SplatterPosition.X)
                            ? (tb === SplatterTB.Top ? 0 : h)
                            : Math.floor(Math.random() * h);
      let size: number = getSizeByType(type);
      let scale: number = (Math.random() * 2) + 0.5;
      let style: SplatterStyle = {
        top: `calc(${yPosition}px - ${size})`,
        left: `calc(${xPosition}px - ${size})`,
        transform: `rotate(0deg) scale(${scale})`,
      };
      
      tmpSplatters.push({ type, style } as SplatterProps);
    }

    setSplatters(tmpSplatters);
  };
  
  const addSplatter: void = (evt) => {
    let elm = evt.target;
    let count = Math.floor(Math.random() * 4) + 5;
    let w = elm.clientWidth;
    let h = elm.clientHeight;
    
    createSplatter(count, w, h);
  };
  
  return (
    <button class="btn-splatter" onClick={addSplatter}>
      PRESS ME
      {splatters.map((props: SplatterProps, index: number) => {
        return (
          <SplatterShapeIcon
            type={props.type}
            style={props.style}
            key={index}
          />
        );
      })}
    </button>
  );
};

ReactDOM.render(<FancyExplodingButton />, document.querySelector('#app'));
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.