<div class="container">
   <a href="#" class="button-wrap js-move">
      <div class="button">ボタン</div>
   </a>
</div>
* {
   margin: 0;
   padding: 0;
   box-sizing: border-box;
}

a {
   text-decoration: none;
   color: #000;
}

.container {
   width: 100%;
   height: 100vh;
   display: flex;
   align-items: center;
   justify-content: center;
}

a.button-wrap {
   width: 200px;
   height: 200px;
   display: flex;
   justify-content: center;
   align-items: center;
   border-radius: 50%;
   overflow: hidden;
   transition: transform .3s cubic-bezier(.24,.45,.32,1);
   will-change: transform;
   position: relative;
}

a.button-wrap:hover .button {
   background-color: white;
   color: #333;
}

.button {
   width: 100px;
   height: 100px;
   display: flex;
   justify-content: center;
   align-items: center;
   background-color: #333;
   border: 1px solid #333;
   border-radius: 50%;
   font-size: 16px;
   line-height: 1;
   color: white;
   transition: all .3s cubic-bezier(.24,.45,.32,1);
}

let target = document.querySelector('.button-wrap');
let elemData;

// マウスカーソルがボタンに乗ったら
target.addEventListener("mouseenter", e => {
  elemData = target.getBoundingClientRect();
})

// マウスカーソルがボタンの上を動いたら
target.addEventListener("mousemove", e => {
  
  // X軸の値 = マウスカーソルのX座標 - ボタン横幅の半分 - ボタンのX座標
  let transX = ((e.clientX - (elemData.width / 2)) - elemData.left) * 0.4;
  
  // Y軸の値 = マウスカーソルのY座標 - ボタン縦幅の半分 - ボタンのY座標
  let transY = ((e.clientY - (elemData.height / 2)) - elemData.top) * 0.4;
  
  // ボタンのtransformのtranslateにそれぞれの値を入れる
  target.style.transform = "translate(" + transX + "px, " + transY + "px)";
  
})

// マウスカーソルがボタンから離れたら
target.addEventListener("mouseleave", e => {
  
  // マウスカーソルがボタンから離れたらtransformのtranslateを0に戻す
  target.style.transform = "translate(0px, 0px)";
  
})

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.