DigiPress

Highly Flexible WordPress Theme

メニュー

マウスオーバーで常にカーソル位置を向いて変形する3Dアニメーションサンプル

 2018/08/18


今回のTipsは、ある要素のマウスオーバー時にその要素が常にマウスカーソルの位置の方向を向いて目で追ってくるような、視差効果を伴った3次元アニメーションのコーディングサンプルをご紹介します。

まずは以下の完成イメージで表示された画像をマウスオーバーでぐりぐり動かしてみてください。

See the Pen 3D hovering motion with chasing cursor by digistate (@digistate) on CodePen.

HTMLの基本構成

今回のサンプルでは、WordPressテーマにおけるアーカイブページの記事一覧用のレイアウトを想定して、各記事にあたるarticle要素と、その中には記事タイトル記事概要寄稿者名を適当に入れてみました。

各記事は全体が画像として表示されていますが、これはimgタグではなくbackground-imagediv要素に背景画像を指定しています。

<article>
  <div class="item-wrapper">
    <figure>
      <div class="image" style="background-image:url(画像のURL);"></div>
      <div class="lighting"></div>
    </figure>
    <div class="item-content">
      <h1>タイトル</h1>
      <p>キャプション</p>
      <div class="author">寄稿者名</div>
    </div>
  </div>
</article>
   :
   :

アーカイブページの想定なので、各記事はarticleタグで括っていますが、3次元の奥行きを表現するために、articleには perspectiveを指定して視点距離をセットしておき、実際にマウスオーバー時にrotateで回転移動させるのはarticleのすぐ内側にある.item-wrapperに対して行います。

ライト効果用フィルター要素

今回のサンプルでは、article要素内をマウスオーバーした際に、常にカーソルの位置に光が当たるような演出を施すための要素<div class="lighting"></div>を背景画像要素(class=”image”)の後に指定しています。

<div class="lighting"></div>

このライト効果用の要素は、実際にはCSSでグラデーションさせたものを背景画像要素のフィルターとして被せていて、jQueryでカーソルの位置に合わせてグラデーションの方向を円周に沿うように角度を動的に変えることでライト効果を施しています。

::beforeや::afterの疑似セレクタは、jQueryでCSSをインラインで書き換えることができないため、ライト効果用の要素は個別にdiv要素を設けています。

表示例

sample

CSS

今回のサンプルでは、各記事のarticle要素を3カラム表示にするため、インラインブロックとして幅を調整しています。
ここでは、細かいデザイン面でのスタイリングについては割愛して、マウスオーバーエフェクトに関係する重要なCSSのみについて説明します。

articleに奥行き視点のための距離(perspective)を指定

各articleをマウスオーバーした際に、3次元の空間としてアニメーションを表現するには、各記事の一番外枠となるarticleperspectiveで奥行きの距離を指定する必要があります。

article {
  perspective:1600px;
}

3次元空間の位置関係の保持

各要素が3次元のアニメーションとしてみえるように、実際にCSSで空間移動を表現する.item-wrapper, figure, .item-content の要素には、preserve-3d を指定して子要素の空間の位置関係を保持させておきます。

article .item-wrapper, 
article figure, 
article .item-content {
  -webkit-transform-style: preserve-3d;
          transform-style: preserve-3d;
}

各記事にシャドウを表示

articleをマウスオーバーした際に、より立体的に見えるようにaticle内のコンテンツラッパー(.item-wrapper)に影をつけてみます。
影は.item-wrapper::before疑似セレクタにbox-shadowを施します。

まずは通常時の状態。

article .item-wrapper::before {
  content: '';
  position: absolute;
  top: 5%;
  left: 5%;
  width: 90%;
  height: 90%;
  -webkit-transition: all .2s ease-in;
  transition: all .2s ease-in;
  box-shadow: 0 8px 38px rgba(0, 0, 0, 0.86);
}

マウスオーバー時は、.item-wrapperをjQuery側でCSSを操作して5%拡大させるようにしているため、影との距離をさらに広げてより浮かんでみえるようにします。

article .item-wrapper:hover::before {
  box-shadow: 0 14px 64px rgba(0, 0, 0, 0.92);
}

表示例

shadow

記事コンテンツ(.item-content)のポインターイベントを無効化

jQueryのmousemoveイベントで、.item-wrapperをマウスオーバーしている間の処理を実装しますが、このときarticle内にある、タイトルや記事概要、寄稿者名を包括する.item-content要素内のmousemoveイベントも発生してしまい、カーソル位置の座標がちぐはぐになってしまうため、CSSでこれを無効化しておきます。

article .item-content {
  pointer-events: none;
}

マウスオーバー開始/終了時のみにtransitionを指定

マウスオーバー中は、jQueryのmousemoveイベントが常に絶え間なく発生し、.item-wrapperはこのイベントによってCSS(transform)をインラインで連続して書き換えていくことでカーソルの位置を追うようなアニメーションを表現するため、この要素自体にtransitionをつけてしまうと、負荷が高くなったりアニメーションに遅延が発生してFireFoxではうまく表現できないため、transitionは付けません。

しかし、そうするとマウスオーバー直後とマウスオーバー終了時の拡大/縮小のアニメーションがCSSのみで表現できません。

そこで、jQueryのmouseentermouseleaveイベントを利用してマウスオーバー開始直後のみと終了時に、.ease.leaveというセレクタを追加(addClass)し、そのセレクタに対してtransitionをセットすることで、マウスオーバー開始直後と終了時の.item-wrapperの拡大/縮小のアニメーションを表現します。

article .item-wrapper.enter.ease, 
article .item-wrapper.leave {
  -webkit-transition: all .1s ease-in;
  transition: all .1s ease-in;
}
mouseenterイベントで.easeセレクタを追加(addClass)しますが、setTimeoutで拡大のモーションが完了した時間を見越して、このセレクタはすぐに削除(removeClass)して、transitionを無効にします。

記事タイトル、概要、寄稿者名の奥行き位置の指定

マウスオーバー時のカーソル移動中に、よりリアルな3次元の動きを表現するため、各記事(article)のタイトル(h1)、概要(p)、寄稿者名(.author)それぞれに、奥行き(Z軸)の距離を与えておきます。

/* 記事タイトル(原点から100px手前に) */
article .item-content h1 {
  -webkit-transform: translateZ(100px);
          transform: translateZ(100px);
}
/* 記事概要(原点から50px手前に) */
article .item-content p {
  -webkit-transform: translateZ(50px);
          transform: translateZ(50px);
}
/* 寄稿者名(原点から70px手前に) */
article .item-content .author {
  -webkit-transform: translateZ(70px);
          transform: translateZ(70px);
}

translateZを指定することで、以下の位置関係になります。
capture-2016-12-01-14-45-03

Javascript

今回のサンプルでは、jQueryのmousemove, mouseenter, mouseleaveイベントを利用して制御をしています。

まず、アニメーションさせる要素(.item-wrapper)を変数にセットしておきます。

var articles = $('article > .item-wrapper');

mouseenter, mouseleaveイベントの処理

マウスオーバー時は、.item-wrapper.enter .easeセレクタを追加して、.leaveセレクタを削除すると同時に、ホバー直後のズームアップ用のセレクタ(.ease)は0.28秒後に削除しておきます。

マウスオーバーが解除されたときは、mousemoveイベントで制御した.item-wrapperの回転(rotate)状態と、光反射用の要素(.lighting)のstyleをリセットしておきます。

articles.on({
  // マウスオーバー時
  'mouseenter':function() {
    var current = $(this);
     // 現在の.item-wrapperに .enter .easeセレクタを追加し、.leaveセレクタを削除
    current.addClass('enter ease').removeClass("leave");
    // 0.28秒後に .easeセレクタを削除
    setTimeout(function(){
      current.removeClass('ease');
    }, 280);
  },
  // マウスオーバー解除時
  'mouseleave':function() {
    var current = $(this);
    // 回転した状態をリセット
    current.css({"transform":"rotate(0)"});
    // .enterセレクタを削除し、.leaveセレクタを追加
    current.removeClass('enter').addClass("leave");
    // 光反射用の要素からstyle属性を削除し、初期状態に戻す
    $('figure > .lighting',this).removeAttr('style');
  }}
);

mousemoveイベントの処理

.item-wrapper上をマウスオーバーしてカーソルを移動している間は、常にmousemoveイベントが発生します。

このイベントでは、対象の.item-wrapper内における、左上を原点(0, 0)としたカーソルの位置(offsetX, offsetY)を取得できるので、これを元に.item-wrapper中心を原点とした座標に変換します。

変換した座標を元に、カーソル位置と連動するようなrotateX, rotateYの回転角度が得られます。

// 光源の色(RGB)
var lightingRgb = '255,255,255';

// カーソル移動中
articles.mousemove(function(e) {
  var current = $(this),
      // articleエリアにおける現在のカーソルのx座標 (原点=中心)
      x = current.width() - e.offsetX * 2,
      // articleエリアにおける現在のカーソルのy座標
      y = current.height() - e.offsetY * 2,
      // x軸方向の回転角度 (30は回転具合の調整用数値)
      rx = -x / 30,
      // y軸方向の回転角度 (24は回転具合の調整用数値)
      ry = y / 24,
      // 光反射用要素(.lighting)のグラデーション回転角度(+45は調整用)
      deg = Math.atan2(y, x) * (180 / Math.PI) + 45;

  // .item-wrapperを5%拡大して、x軸、y軸方向に回転
  current.css({"transform":"scale(1.05) rotateY("+rx+"deg) rotateX("+ry+"deg)"});
  // 光反射用要素のグラデーション
  $('figure > .lighting',this).css('background','linear-gradient('+deg+'deg, rgba('+lightingRgb+',0.32) 0%, rgba('+lightingRgb+',0) 100%)');
});

光反射用の要素に施すグラデーションの回転角度も算出していますが、ちょっと説明がややこしいので割愛します!


というわけで、今後制作するWordPressテーマにおけるアーカイブページ用のホバーエフェクトの試作ネタでした。

参考

http://codepen.io/ariona/pen/JopOOr

Share / Subscribe
Facebook Likes
Tweets
Hatena Bookmarks
Pinterest
Pocket
Feedly