今回のTipsは、ある要素のマウスオーバー時にその要素が常にマウスカーソルの位置の方向を向いて目で追ってくるような、視差効果を伴った3次元アニメーションのコーディングサンプルをご紹介します。
まずは以下の完成イメージで表示された画像をマウスオーバーでぐりぐり動かしてみてください。
See the Pen 3D hovering motion with chasing cursor by digistate (@digistate) on CodePen.
INDEX
HTMLの基本構成
今回のサンプルでは、WordPressテーマにおけるアーカイブページの記事一覧用のレイアウトを想定して、各記事にあたるarticle
要素と、その中には記事タイトル、記事概要、寄稿者名を適当に入れてみました。
各記事は全体が画像として表示されていますが、これはimg
タグではなくbackground-image
でdiv
要素に背景画像を指定しています。
<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でカーソルの位置に合わせてグラデーションの方向を円周に沿うように角度を動的に変えることでライト効果を施しています。
表示例
CSS
今回のサンプルでは、各記事のarticle
要素を3カラム表示にするため、インラインブロックとして幅を調整しています。
ここでは、細かいデザイン面でのスタイリングについては割愛して、マウスオーバーエフェクトに関係する重要なCSSのみについて説明します。
articleに奥行き視点のための距離(perspective)を指定
各articleをマウスオーバーした際に、3次元の空間としてアニメーションを表現するには、各記事の一番外枠となるarticle
にperspectiveで奥行きの距離を指定する必要があります。
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); }
表示例
記事コンテンツ(.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のmouseenterとmouseleaveイベントを利用してマウスオーバー開始直後のみと終了時に、.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; }
.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を指定することで、以下の位置関係になります。
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テーマにおけるアーカイブページ用のホバーエフェクトの試作ネタでした。
参考