<!-- 水波纹扩散 -->
<button class="btn">水波纹散开</button>
<!-- 两边扩散 -->
<button class="btn2">两边散开</button>
.btn,
.btn2 {
  position: relative;
  width: 150px;
  height: 60px;
  background: #409eff;
  outline: 0;
  border: none;
  padding: 12px 20px;
  overflow: hidden;
  color: #fff;
}
.btn::before,
.btn2::before {
  content: '';
  display: block;
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  transition: 0.2s;
  background: #fff;
  opacity: 0;
}
.btn:active::before,
.btn2:active::before {
  opacity: 0.2;
}
.btn::after {
  content: '';
  display: block;
  position: absolute;
  width: 200%;
  height: 100%;
  left: var(--x, 0);
  top: var(--y, 0);
  background-image: radial-gradient(circle, #fff 10%, transparent 10.01%);
  background-repeat: no-repeat;
  background-position: 50%;
  transform: translate(-50%, -50%) scale(10);
  opacity: 0;
  transition: transform 0.8s, opacity 0.8s;
}
.btn:active::after {
  transform: translate(-50%, -50%) scale(0);
  opacity: 0.3;
  transition: 0s;
}
.btn2::after {
  content: '';
  display: block;
  position: absolute;
  background: rgb(255, 255, 255, 0.7);
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  opacity: 0;
  transition: all 0.8s, opacity 0.8s;
}
.btn2:active::after {
  left: var(--l, 0);
  right: var(--r, 0);
  opacity: 0.3;
  transition: 0s;
}
(() => {
  const el = document.querySelector('.btn');
  el.addEventListener('mousedown', e => {
    const { left, top } = el.getBoundingClientRect();
    el.style = `--x:${e.clientX - left}px;--y:${e.clientY - top}px`;
  });

  const el2 = document.querySelector('.btn2');
  el2.addEventListener('mousedown', e => {
    const { left, right } = el2.getBoundingClientRect();
    el2.style = `--l:${e.clientX - left}px;--r:${right - e.clientX}px`;
  });
})();
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.