Vanilla-tilt.js 3Dホバーエフェクト 使用方法・オプション等解説と実装結果

マウスの移動によってカードや画像、ボタン等の要素を立体的に傾斜させてくれるVanilla-tilt.jsの紹介をします。
基本的な使用方法、オプション、それらを踏まえた実装までご覧いただけます。

実際に自分でマウスの動きに応じて(mousemove()メソッドを使って)、要素を回転させてみたことがありますが、X軸とマイナス軸の回転角度をプラスにするかマイナスにするかや、子要素の3D配置がうまくいかなくて悩みました。

それに比べて、これはとても簡単で、オプションも程よく揃っていて、スムーズに動きます!
要素の上でマウスを動かすのがクセになりそうです。

vanilla-tilt
子要素のボタンを浮かせた状態でも動きます。

使用方法

始め方

混乱しやすいのですが、jQueryのプラグインとしてのtilt.jsと、そこから派生したjQuery非依存のvanilla-tilt.jsの2つがあります。
今回は、オプションや記述方法は、後者のvanilla-tilt.jsを基本に説明します。

下記のページから、ダウンロード可能です。

vanilla-tilt.js 公式ページ(英語)のダウンロードページ(デモページも兼ねていて、簡単な使用方法の説明もあります。)
tilt.js 公式ページ (英語) の  jQueryバージョンのダウンロードページ(デモページ)

vanilla-tilt.js GitHub vanilla-tilt.js
tilt.js GitHub jQueryバージョン tilt.js

なお、CDNの読込みも可能です。後述の記述方法に記載しています。

読込みと適用要素への記述方法

最も簡単な使用方法は、bodyタグ前にライブラリを読み込み、適用する要素に「date-title」を含めるだけです。

<body>
  <!-- オプション変更なしの記述 -->
  <div class="your-element" data-tilt></div>
  <!-- オプション追加の例 マウスが乗ったら1.2倍に、要素が傾いた時に反射を追加-->
  <div class="your-element" data-tilt data-tilt-scale="1.2 data-tilt-glare=true></div> 

  <!-- vanilla-tilt.jsを使用する場合、 jQuery読み込み不要-->
  <script src="vanilla-tilt.js"></script> <!-- ダウンロードしたファイルを読み込む場合(パスはご自身のものに変更) -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/vanilla-tilt/1.7.2/vanilla-tilt.min.js"></script> <!-- CDNで読み込む場合 -->

  <!-- jQueryプラグインとして使用する場合 -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"> </script> <!-- 先にjQueryを読み込む -->
  <script src="tilt.js"></script> <!-- jQueryを読み込み後に、tilt.jsを読み込む(パスはご自身のものに変更)ダウンロードしたファイルを読み込む場合 -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/tilt.js/1.2.1/tilt.jquery.min.js"></script> <!-- CDNで読み込む場合 -->
  <script src="main.js"></script> <!-- 必要に応じて(パスはご自身のものに変更) -->

</body>

オプション

オプションの内容も分かりやすいです。
但し、ここで使用されているXとYは、CSSのtransformプロパティでいうrotateのXとYとは反対を意味していたりしますので、ご注意ください。

オプション名デフォルト値説明
reversefalseデフォルトでは、要素の中でマウスが乗っている箇所が手前に飛び出すように見えます。
この値をtrueにすると、マウスが乗っている箇所が奥に沈むようになります。
max35回転の最大角度を指定可能です。GitHubのページでは15となっていますが。
startX0対象要素がページ上に表示された時(初期状態)のX軸の傾斜角度、つまりCSSのtransformプロパティの関数rotateY()の引数を指定可能です。
startY0 対象要素がページ上に表示された時(初期状態)のY軸の傾斜角度 、つまりCSSのtransformプロパティの関数 rotateX()の引数を指定可能です。
perspective1000この値が小さくなる程、要素とユーザーとの視点距離が近くなります。
つまり、要素の奥行が大きくなり、あまりにも小さな値にするとびっくりするので、適度に調整した方がよいと思います。
scale1要素のサイズを指定します。
この値は単純に倍数を指します。
つまり、「1.2」ならば、1.2倍になるということです。
speed300要素がにマウスが乗る又は離れる時の要素が動き始める速度を指定します。
transitiontrueアニメーションのトランジション(アニメーションの速度操作)を有効にするか。
axisnull傾斜を無効にするか。
つまり、X軸またはY軸の回転を無効にすることが可能です。
X軸の回転角度を無効にする場合の値は「x」になります。
resettrue マウスが要素から離れた時、回転角度をリセットするか。つまり、falseにするとリセットされず、最後にマウスが離れた時の角度を維持します。
easing“cubic-bezier(.03,.98,.52,.99)”アニメーションのイージング関数を指定します。
glarefalse要素が傾いたときに反射させるか。
“max-glare”1反射の最大の不透明度を指定します。
数値が小さくなる程、反射効果は小さくなります。
“glare-prerender”false反射要素を自ら追加しない場合で、自動で反射要素を作成するか。
trueの場合は、マウスイベントによる反射要素は自身で用意する必要があります。
“mouse-event-element”nullマウスのイベントリスナーの要素は、自身で用意する必要があります。
“full-page-listening”false要素外でのマウスの動きに反応させるか。
gyroscopetrueデバイスの向きの検出を有効にするか。
gyroscopeMinAngleX-45X軸上のデバイスの最小角度です。つまり、この角度で回転したデバイスは、マウスが要素の左側の境界にあるかのように要素が傾きます。
gyroscopeMaxAngleX45X軸上のデバイスの最大角度です。つまり、この角度で回転したデバイスは、マウスが要素の右側の境界にあるかのように要素が傾きます 。
gyroscopeMinAngleY-45Y軸上のデバイスの最小角度です。つまり、この角度で回転したデバイスは、マウスが要素の上側の境界にあるかのように要素が傾きます。
gyroscopeMaxAngleY45Y軸上のデバイスの最大角度です。つまり、この角度で回転したデバイスは、マウスが要素の下側の境界にあるかのように要素が傾きます 。

オプションを踏まえて実装(JSファイルなし)

CODEPEN

「Run Pen」をクリックしてください。

オプションの異なるものを5つ作成していますので、スクロールして、それぞれマウスを乗せて動かしてみてください。
2番目と3番目のボックスの子要素のボタンが浮かんでいるように見えます。
gyroscope関連のオプションは試していません。

See the Pen vanilla-tilt.js by blue moon (@blue-moon) on CodePen.

HTMLファイル

<body>
  <ul class="box-list">
    
    <!-- 最大角度は25度と控えめ。90度は要素が視線に対して垂直となり見えなくなるため、注意 -->
    <li class="box" data-tilt data-tilt-max="25" data-tilt-speed="400">
      <!-- 子要素にもオプション追加可能 -->
      <h2 data-tilt data-tilt-scale="1.1" data-tilt-perspective="500">tilt1</h2>
      <p>tilt1_box option</p>
      <ul class="op-list">
        <li>data-tilt-max="25"</li>
        <li>data-tilt-speed="400"</li>
      </ul>
      <p>tilt1_title option</p>
      <ul class="op-list">
        <li>data-tilt-scale="1.1"</li>
        <li>data-tilt-perspective="500"</li>
      </ul>
    </li>

    <!-- 表示開始時のY軸の回転角度を指定、最大値を超えないこと。一度マウス乗って離れると、最初の角度に戻るわけではない。 -->    
    <li class="box" data-tilt data-tilt-max="45" data-tilt-startX="-30">
      <h2>tilt2</h2>
      <p>tilt2_box option</p>
      <ul class="op-list">
        <li>data-tilt-max="45"</li>
        <li>data-tilt-startX="30"</li>
      </ul>
      <p>button_css</p>
      <ul class="op-list">
        <li>transform: translateZ(15px);</li>
      </ul>
      <button class="tilt_btn_2">button</button> <!-- ここでは、tilt要素にしていない。 浮いて見えるのはCSSの記述によるもの。-->
    </li>
    
    <!-- マウスの乗った箇所が押し込まれる。 マウスが離れた角度を維持 -->
    <li class="box" data-tilt data-tilt-reverse="true" data-tilt-reset="false">
      <h2>tilt3</h2>
      <p>tilt3_box option</p>
      <ul class="op-list">
        <li>data-tilt-reverse="true"</li>
        <li>data-tilt-reset="false"</li>
      </ul>
      <p>button_css</p>
      <ul class="op-list">
        <li>transform: translateZ(15px);</li>
      </ul>
      <button class="tilt_btn_3"> <!-- 2つ目のボックス内のボタンと異なる構造、理由はCSSにて -->
        <span>button</span>
      </button>
    </li>

    <!-- 角度が小さいと反射効果が低いため、最大角度を大きめにした方がよいかも -->
    <li class="box" data-tilt data-tilt-max="50" data-tilt-glare data-tilt-max-glare="0.9">
      <h2>tilt4</h2>
      <p>tilt4_box option</p>
      <ul class="op-list">
        <li>data-tilt-max="50"</li>
        <li>data-tilt-glare</li>
        <li>data-tilt-max-glare="0.9"</li>
      </ul>
    </li>

    <!-- マウスが要素外でも反応させる。x軸の回転を無効、y軸の回転のみ有効、xとyを間違えないよう注意 -->
    <li class="box" data-tilt data-tilt-max="70" data-tilt-full-page-listening data-tilt-axis="x">
      <h2>tilt5</h2>
      <p>tilt5_box option</p>
      <ul class="op-list">
        <li>data-tilt-max="70"</li>
        <li>data-tilt-full-page-listening</li>
        <li>data-tilt-axis="x"</li>
      </ul>
    </li>
    
  </ul>
</body>

CSSファイル

:root {
  --btn-gradient: linear-gradient(to bottom right, #a6efff, #a30aff);
  --base-color: #fbfbfb;
}

* {
  box-sizing: border-box;
}

/* 途中省略 */

.box {
  /* 途中省略 */
  border-radius: 5px;
  border: 3px rgba(225,225,225,0.8) solid;
  border-bottom: 3px rgba(40,40,40,0.35) solid;   /* 下辺と右辺のボーダーは濃く */ 
  border-right: 3px rgba(40,40,40,0.35) solid;
  transition: all 0.5s linear;
  box-shadow: 1.8px 2.3px 3.9px rgba(0,0,0,0.035),   /* シャドウを複数重ね、より自然に溶け込むように */ 
              5px 6.3px 9.6px rgba(0,0,0,0.05),
              12.1px 15.1px 20.4px rgba(0,0,0,0.065),
              20px 30px 35px rgba(0,0,0,0.1);
}

.box:nth-child(1) {
  background-image: linear-gradient(to right bottom, rgb(219,234,254), rgb(59,130,246));
}

.box:nth-child(2) {
  background-image: linear-gradient(to right bottom, rgb(254,204,233), rgb(255,0,144));
  transform-style: preserve-3d;  /* 子要素(ボタン)を3D空間に配置、z軸方向に移動可能となる。*/ 
}

.box:nth-child(3) {
  background-image: linear-gradient(to right bottom, rgb(254,255,204), rgb(245,158,30));
  transform-style: preserve-3d;
}

.box:nth-child(4) {
  background-image: linear-gradient(to right bottom, rgb(233,213,255), rgb(107,33,168));
}

.box:nth-child(5) {
  background-image: linear-gradient(to right bottom, rgb(167,243,208), rgb(16,185,129));
}

/* 途中省略 */

button {
  /* 途中省略 */
  box-shadow: 1.1px 1.3px 5.3px rgba(0, 0, 0, 0.101),   /* ボタンも浮いた状態のため自然な影を落とす */ 
              3.6px 4.5px 17.9px rgba(0, 0, 0, 0.149),
              16px 20px 80px rgba(0, 0, 0, 0.25);
  display: block;
  font-size: 0.5em;
  cursor: pointer;
  transition: all .2s;
  transform: translateZ(15px);  /* z軸方向へ15px手前に(ユーザー側へ)移動 */ 
}

.tilt_btn_2 {
  border-style: solid;
  border-width: 2px;
  border-image: var(--btn-gradient) 1;  /* ボタン枠を変数に格納しておいたグラデーション塗に */
  color: #808080;
  background: var(--base-color);
}

.tilt_btn_2:hover {
  border: none;  /* box-sizing: border-box;によりボーダー幅分小さくはならない。ボーダーと背景を別でグラデーションすると、グラデーションが繋がらない */
  background: var(--btn-gradient);  /* ボタン枠と同じグラデーション*/
  color: var(--base-color);
}

.tilt_btn_3 {
  padding: 0;  /* インナーの大きさや位置に影響するため、0にする */
  border: none;
  border-radius: 1.3em;  /* ボックス2のボタンは、枠をグラデーションにしている場合、角を丸くできない */
  display: flex;
  justify-content: center;
  align-items: center;
  background: var(--btn-gradient);
}
/* padding設定、box-sizing: content-box;、spanサイズ100%でも、角丸のグラデーション枠を実現可能 */

.tilt_btn_3 span {
  width: calc(100% - 0.5em);  /* ボタン内側に一回り小さな背景色白のインナーを設け、はみ出している部分(グラデーション)が枠となる */
  height: calc(100% - 0.5em);
  display: inline-block;
  border-radius: 1.2em;  /* ここでも角を丸く、値は調整 */
  background: var(--base-color);
  color: #808080;
  line-height: 1.8;
}

.tilt_btn_3:hover span{
  width: 100%;  /* ホバー時に親要素と同じ大きさにして枠の部分まで覆う */
  height: 100%;
  background: var(--btn-gradient);  /* 親要素と同じグラデーションで、枠として見えていた部分と繋がっている */
  color: var(--base-color);
  line-height: 2.1;  /* サイズが大きくなるため調整 */
}

子要素を浮かせて見せる

2番目のボックス内のボタンのように3D空間に浮いたように見せているのは、tiltによるオプションではありません。
CSS側で、親要素で子要素を3D空間配置とし(transform-style: preserve-3d;)、対象の子要素でz軸方向へ移動しています。

なお、 親要素にtransformプロパティを設定すると、スタッキングコンテキストを構成しなくなり、この親要素の中ではz-indexが効きませんのでご注意ください。

MDN CSS:カスケーディングスタイルシート 重ね合わせコンテキスト

また、浮かせたい子要素もtiltを個別に設定しようとすると、子要素のtransform: translateZ();が上書きされ、その結果浮いた状態になりません。

【参考】3D配置

ホバーエフェクトはありませんが、下記はカードを3D空間にあるように並べたものです。

タイトルとURLをコピーしました