第16回 マウスホイールに対応した横スクロールのページを作成する
基本的にWebページは縦長になるようにレイアウトされ、上下にスクロールしながら閲覧することが一般的です。横スクロール型のWebサイトはFlashサイトなどで一部見られましたが、特別な理由が無い限り導入されることはありませんでした。しかし、Webサイトとして他にはあまりみられない個性のひとつとなるため、うまく導入すれば印象的なWebサイトにすることができるかもしれません。
解説:(有)ムーニーワークス ハヤシユタカ横スクロールが採用されない理由のひとつに、ユーザーがスクロールバーを横にスライドさせる操作がしづらいことが理由のひとつに挙げられます。しかし、そういったユーザーの不便さをJavascriptやjQueryで操作系の機能を補助することで解消することができます。今回はメニューによるダイレクトリンクのほか、マウスホイールやスワイプ操作で横スクロールするWebページを作成してみます。
■今回作成したサンプル
|
●横スクロールの仕組み
まずはどのように横スクロールの仕組みを実装していけば良いのか、図を見ながら順を追って考えてみましょう。本来は縦に並ぶ、ひとまとまりの内容(※以下、コンテンツカラム)1つにつき1ページ分として分割し、横並びに配置していきます。これら全てのコンテンツカラムをまとめるため、コンテナとなる1つの要素に格納します(※以下、これをカラムコンテナ)。また、後述の現在地機能で必要となるため、それぞれのコンテンツカラムの配置位置座標を任意の変数に記憶しておく必要があります。
|
カラムコンテナに格納した各コンテンツカラムは、今回のデフォルト仕様では、横幅(width)と縦幅(height)のサイズはブラウザ表示領域(※以下、スクリーン)に合わすため、スクリーンサイズよりも縦に長くなるコンテンツは画面内に収まるようにfloatなどで回り込ませる方法や、またはスクロールバーを表示してインラインフレームのように見せる方法があります。
カラムコンテナの用意ができたら、次にユーザーのスクロール操作に連動して、縦(上下)移動ではなく、横(左右)移動するように処理を変更します。これは、スクロールバーやマウスホイールによる上下スクロールの処理をキャンセルし、カラムコンテナを左右移動させることで、あたかもスクリーンが左右にスクロールしているかのように見せることができます。
最後に、ナビゲーションメニューをクリックすると、各コンテンツカラムにダイレクトに移動する仕組みと、付加機能として現在表示されているコンテンツカラムとそれに対応するメニューが反転して表示される「現在地」機能も実装します。ダイレクトにリンクする機能は通常のアンカーリンクとなんら変わりはなく、本来上下に配置されているコンテンツが横にならんで配置されているので、必然的に横スクロールしながらその要素まで遷移してくことになりますが、現在地機能は最初の手順で記憶しておいた各コンテンツカラムの配置位置座標を元に処理を行ってくれます。
●まとめると以下の手順になります。
(1)HTML上でコンテンツカラムを用意してカラムコンテナに格納する。
(2)それぞれのコンテンツカラムを横に並べ、それぞれの縦横サイズをスクリーンサイズに
合わせ、また配置位置座標を任意の記憶領域に保存しておく。
(3)マウスホイール、スワイプなど、ユーザによるスクロール操作が行われた場合は、
デフォルトの処理をキャンセルし、左右スクロール用の処理を実行する。
(4)ナビゲーションメニューによるダイレクトリンク機能と現在地機能を実装する。
以上、これら4つの実装を順を追っていきます。
●HTML上でコンテンツカラムを用意してカラムコンテナに格納する
はじめに挙動が分かりやすいようにシンプルな内容の1つ目のサンプル(sample_basic.html)をベースに解説します。まずは各コンテンツカラム(サンプルでは、div class=”column”)を用意し、これらを内包するコンテナ要素(サンプルでは、div class=”columnContainer”)に格納します。コンテンツカラムに付与したCSSセレクタ「column」が横スクロールで表示させる対象となり、その中に作成したCSSセレクタ「column_mainContent」は自動的に上下左右中央に配置される要素となります。実際はこの要素内にコンテンツを記述してくことになります。
JavascriptはjQuery本体のほか、イージングを処理するjQuery.easingプラグイン、マウスホイールの操作を制御するjQuery.mousewheelプラグイン、スワイプなどのジェスチャー系操作を制御するjQuery.touchswipeプラグインを読み込みます。
■■■■ HTMLコード
<div id="contentContainer"> <div id="columnContainer"> <div id="column1" class="column"> <div class="column_mainContent"> <p class="number">1</p> </div> </div> …省略… <div id="column6" class="column"> <div class="column_mainContent"> <p class="number">6</p> </div> </div> </div> <!--/ columnContainer --> <div id="subContainer"> <nav id="mainNavigation"> <ul> <li><a href="#column1">CONTENT 1</a></li> <li><a href="#column2">CONTENT 2</a></li> <li class="notNarrow"><a href="#column3">CONTENT 3</a></li> <li><a href="#column4">CONTENT 4</a></li> <li class="notNarrow"><a href="#column5">CONTENT 5</a></li> </ul> </nav> <!--/ mainNavigation --> </div> <!--/ subContainer --> </div> <!--/ contentContainer --> …省略… <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> <script src="js/jquery.mousewheel.js"></script> <script src="js/jquery.easing.1.3.js"></script> <script src="js/jquery.touchSwipe.min.js"></script> <script src="js/script.js"></script>
●コンテンツカラムを横に並べ、縦横サイズ調整と座標を保存する
コンテンツカラムを横に並べるにはそれぞれの要素が持つleftプロパティを調整していくことになりますが、コンテンツカラムひとつ1つのサイズはそのときのスクリーンサイズのよって異なるため、まずコンテンツカラムのサイズをスクリーンサイズに合わせ、その後それぞれのleftプロパティに座標を割り当て、その値を記憶しておきます。記憶した値は後述のダイレクトリンク機能や現在地識別機能に利用します。
注意したいのが、ユーザーのウィンドウリサイズやスマートフォンやタブレット端末の向きの変更によってスクリーンサイズが変わり、それに応じてコンテンツカラムのサイズや座標も変更されるので、ユーザーによるウィンドウリサイズや端末向きの変更(orientation change)の際は、再度変更された配置位置座標を記憶をしておく必要があります。
■■■■ JSコード
_$w.on('load', function(){ …省略… fitWindowScale(_$t); // 各カラムのサイズをスクリーンサイズに合わせる処理を実行 setTargetPostion(_$t); // 各カラムの現在の位置を取得する処理を実行 }); _$w.on('resize orientationchange', function(){ setTimeout( function() { fitWindowScale(_$t); setTargetPostion(_$t); adjustScrollPosition(-10); }, 200); }); …省略… // 要素をウィンドウワイズにあわせる function fitWindowScale(_$t){ var _win = getWindowInfo(); _$cn = $(prop.columnContainer), _pos = { x: 0, y: 0}; _$t.each(function(){ // 各カラムの縦幅、横幅をスクリーン(ブラウザ表示領域)サイズにあわせ、カラムコンテナ内で横に順番に並ぶようにCSSを調整 $(this).css({ left: _pos.x, width: prop.fitWindowWidthScale ? _win.w : $(this).width(), height: _win.h }); _pos.x += $(this).width(); // 次の横並び位置をカラム幅分右にずらす _pos.y += $(this).height(); // 仮想コンテナの総縦幅を決めるため、各カラム縦幅を加算していく }); _$cn.width(_pos.x); // カラムコンテナの横幅を設定 getVurtualContainerInfo(_pos.y); // 仮想コンテナの縦幅をbody要素の縦幅に設定 } function setTargetPostion(_$t){ prop.dataPossessor = []; _$t.each(function(){ prop.dataPossessor.push(parseInt($(this).css('left'),10)); }); changeCurrent();
●ユーザーによるスクロールをキャンセルし、左右スクロール用の処理を実行する
ユーザーのスクロールバーやマウスホイールによるスクロール操作をキャンセルし、左右スクロール用の処理を実行するという仕組みは、最も肝となる部分です。今回のサンプルは、デフォルトではスクロールバーによる操作を無効にしているので、マウスホイールとスワイプによる操作について解説していきます。
マウスホイール操作やスワイプ操作の制御はJavascriptで行うことができますが、ブラウザやOSによってよってその実装方法が異なるため煩雑になりがちです。今回導入するmousewheelプラグインやtouchSwipeプラグインは、それらの煩雑な仕様を隠蔽してシンプルに利用できるように提供してくれます。これらのプラグインを利用して、スクロールの向きや分量を取得したら、それらの値を左右スクロール用処理関数adjustScrollPosition()に送って処理を実行します。
■■■■ JSコード
_$w.on('resize scroll', function(e){ preventDefault(e); setTargetPostion(_$t); if($('body').is(':animated')) { return; } adjustScrollPosition(); }); // スワイプイベント _$w.on('touchmove',function(e){ preventDefault(e); }); _$w.swipe({ swipeLeft:function(ev, dir, dist, dur, fin) { adjustScrollPosition(dist, dist); }, swipeRight:function(ev, dir, dist, dur, fin) { adjustScrollPosition(-dist, dist); } }); // マウスホイールイベント _$w.on('mousewheel', function(e) { preventDefault(e); var _dur = e.deltaY; _dur = _dur > 50 ? 50 : _dur < - 50 ? -50 : _dur; switch(prop.mode.wheel){ case 0: _dur = 0; break; case 1: _dur *= -150; break; case 2: _dur *= 2; break; default: } adjustScrollPosition(_dur, Math.abs(_dur) * 2); }); …省略… function adjustScrollPosition(_diff, _dur, _ease){ var _cn = getColumnContainerInfo(), // カラムコンテナの情報取得 _win = getWindowInfo(), // ウィンドウ情報取得 _vc = getVurtualContainerInfo(), // 仮想コンテナコンテナの情報取得 _destX = _cn.left, // カラムコンテナの横位置の算出結果を格納 _destY = 0; // カラムコンテナの縦位置の算出結果を格納 if(prop.mode.scroll == 0) { // スクロールモードが0の場合は、縦位置を横位置変換する if(_diff == undefined) { _destY = _vc.top; } else { _diff *= _vc.ratio; _destY = _vc.top + _diff; } _destY = _destY <= 0 ? 0 : _destY >= _vc.maxpos ? _vc.maxpos : _destY; _destX = -_destY / _vc.ratio; } else { _destX -= _diff; } _destX = _destX >= 0 ? 0 : _destX <= -_cn.maxpos ? -_cn.maxpos : _destX; var _option = { // アニメーションオプション duration: _dur ? _dur : prop.mode.scroll != 0 ? 500 : 0, easing: _ease || prop.transitionEasingDefault, queue: false, complete: function(){ setTargetPostion($(prop.columnContainerItem)); } }; _vc.obj.stop(true, true).animate({ scrollLeft: _destX, scrollTop: prop.mode.scroll == 0 ? _destY : 0, }, _option); _cn.obj.stop(true, true).animate({ left: _destX, top: prop.mode.scroll == 0 ? _destY : 0 }, _option); }
●ナビゲーションメニューによるダイレクトリンク機能と現在地機能を実装する
ナビゲーションメニューによってダイレクトに各コンテンツカラムへリンクする機能と、現在表示しているカラムコンテナの位置に対応してナビゲーションメニューが反転表示される現在地機能を実装します。
ダイレクトに各カラムコンテンツへ遷移する仕組みは、通常のアンカーリンクによる仕組みとなんら変わりはなく、クリックされたナビゲーションメニューのhref属性を取得して、それと同じid属性(idセレクタ)を持つカラムコンテナの座標(leftプロパティ)へ向けてアニメーション遷移させれば良いでしょう。
現在地機能は、コンテンツカラムのリサイズと横並べ処理の際に記憶しておいた各コンテンツカラムの座標をもとに、現在表示されている座標と比較して、現在地の座標値の方が大きくなった時点を現在地として、その座標に対応するメニューを反転表示させています。
現在表示されているカラムコンテナの位置に連動してメニューが反転表示されている |
■■■■ JSコード
// ナビゲーションのクリックイベント $(prop.mainNavigation).find('a').on('click', function(e){ preventDefault(e); // リンクの飛び先のキャッシュ var _href = $(this).attr('href'), _diff = 0; // リンク先へアニメーション遷移させる if(_href && $(_href).length > 0) { _diff = getColumnContainerInfo().left + parseInt($(_href).css('left'), 10); // スクロール位置を各カラムの仮想コンテナ内の縦位置に移動させる。縦のスクロール移動は別途設定したスクロールイベントによって、横のスクロールに変換される adjustScrollPosition(_diff, prop.transitionSpeed, prop.transitionEasingJump); } }); …省略… function changeCurrent(){ var _win = getWindowInfo(), _cn = getColumnContainerInfo(), _pos = -_cn.left - _win.w / 2, // 現在の横スクロール位置から、各カラム領域の半分位をマイナスした地点を基準に、メニューの現在地を判定する _dp = prop.dataPossessor, _$nav = $(prop.mainNavigation).find('li'), _cur = replaceString(prop.currentSelector); for(var i = 0; i < _dp.length; i++) { if( _pos <= _dp[i]){ var _id = '#' + $(prop.columnContainerItem).eq(i).attr('id'), _$cur = $(prop.mainNavigation).find('a[href="'+_id+'"]'); if(_$cur.length > 0){ _$nav.removeClass(_cur); _$cur.parent('li').addClass(_cur); } return; } } }
書籍のほうでは実際にコンテンツを入れたサンプルも掲載しております。
・横スクロールするページ(コンテンツサンプル)
URL:http://www.html5-memo.com/sample/jq-books/14/sample_layout.html
今回は以上になります。
次回はアプリ風インターフェイスのフォトギャラリーを作成します。
『現場でかならず使われている jQueryデザインのメソッド』
今回紹介したサンプルは『現場でかならず使われている jQueryデザインのメソッド』にてサンプルダウンロードとコードの解説を行っております。 |
作成したサンプルや本の紹介はこちらをご覧ください。
2015/4/1
●本連載で使用している書籍「現場でかならず使われている jQueryデザインのメソッド」
Profile
【ハヤシユタカ】2001年、有限会社ムーニーワークスを設立。WEB制作の他、書籍執筆、セミナー講演、企業研修などを行う。また、クリエイター育成機関デジタルハリウッドでは1999年より教鞭をとる。2004年からは、デジタルハリウッド大学大学院、明治大学大学院非常勤講師を歴任。2012年からはデジハリオンラインスクールにてWEB関連講座のeラーニング教材の開発に携わり、会社・自宅のPCからいつでも「HTML5」「WordPress」を学べる講座を担当。2013年御苗場Vol12.横浜にて「The Zoological Garden ~言葉のない対話~」エプソン賞受賞。
こちらの連載もあわせてご覧ください
●WordPressでブログでなくビジネスサイトをつくろう:http://www.mdn.co.jp/di/articles/2782/
●ゼロからはじめるHTML5でのサイト制作:http://www.mdn.co.jp/di/articles/2605/
●HTML5でサイトをつくろう:http://www.html5-memo.com/
●Webデザインクリップ:http://webdesignmatome.com/
【著書・共著】
●現場でかならず使われている jQueryデザインのメソッド (2014/4/25)
●HTML5デザイン 仕事のネタ帳 CSS3+JavaScript+CSSフレームワークと活用するプロのテクニック (2014/3/24)
●現場で役立つCSS3デザインパーツライブラリ (2013/5/17)
●WordPress 3.x 現場のワークフローで覚えるビジ?ネスサイト制作 (2012/9/20)
●WebデザイナーのためのHTML5入門 (2012/08)
●ポケット詳解 jQuery Mobile辞典 (2012/05)
●すべての人に知っておいてほしい HTML5 & CSS3 の基本原則(2012/10/20)
●すべての人に知っておいてほしい スタイルシートデザインの基本原則(2012/5/25)