ホームページ > 記事 > ウェブフロントエンド > Hammer.js + カルーセル原理でシンプルなスライド スクリーン関数を実装_JavaScript スキル
最近、画面が 2 つだけの非常に小さな h5 アプリケーションを作成するタスクがありました。これには、水平方向の全画面スライド切り替えといくつかの簡単なアニメーション効果を実行する必要がありました。以前はこのようなことを行うために fullpage.js と jquery を使用していました。とパフォーマンスがあまり良くなかったので、簡単なものを自分で作って実装したいと思いました。最後に、zepto + Hammer.js と carousel を使用してこの問題を解決しましたが、ページ全体で Gzip が有効になっていない場合、すべてのリソース リクエストのデータ サイズは約 200 KB でした。この記事では、このメソッドの実装アイデアを要約します。
効果のデモンストレーション:
1. 実装のポイント
1) スライディング スクリーンはブートストラップのカルーセル プラグインを利用しますが、カルーセルの実装アイデアを利用するだけで済みます。
3) モバイルがジェスチャー操作をサポートする必要があることを考慮すると、ジェスチャー ライブラリ Hammer.js を使用できます。この API は非常にシンプルで使いやすいです。
4) animate.css をアニメーション効果に使用できますが、そのすべてのコードをコードに組み込む必要はありません。必要なアニメーション効果に関連するコードのみをコピーする必要があります。
6) スライディング スクリーン エフェクトはトランジション アニメーションを使用します。アニメーションの終了時のコールバックに応答するには、transition.js を使用することも検討できますが、これは Bootstrap でのみ使用できます。デフォルトでは jquery を使用しますが、zepto と組み合わせて使用する必要があります。
これらの重要なポイントは比較的大まかですが、次のコンテンツで 1 つずつ詳しく紹介します。
2. HTML 構造
空のスライド ページの HTML 構造は次のとおりです:
html、
<div id="container" class="container"> <section id="page-1" class="page page--1"> </section> <section id="page-2" class="page page--2"> </section> <section id="page-3" class="page page--3"> </section> </div>
.container と .page は初期化時に絶対配置と全画面レイアウトを採用します。各セクション.ページはページを表し、デフォルトではすべてのページが同じ位置に配置されます。つまり、すべてのページが表示されると、これらのページは重なり合います。
body { height: 100%; -webkit-tap-highlight-color: transparent; } .container, .page { position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden; } .page { overflow: hidden; display: none; -webkit-transition: -webkit-transform .4s ease; transition: transform .4s ease; -webkit-backface-visibility: hidden; backface-visibility: hidden; }デモ ページの HTML 構造は次のとおりです:
デモ関連の CSS は表示されません。このうちアニメーション化にはgithubで公開されているアニメーションライブラリであるanimate.cssを適用する必要があります。
<div id="container" class="container"> <section id="page-1" class="page page--1"> <div class="page__jump"><a href="#page-2" title="">下一页</a></div> <p class="page__num animated">1</p> </section> <section id="page-2" class="page page--2"> <div class="page__jump"><a href="#page-1" title="">上一页</a><a href="#page-3" title="">下一页</a></div> <p class="page__num animated">2</p> </section> <section id="page-3" class="page page--3"> <div class="page__jump"><a href="#page-2" title="">上一页</a></div> <p class="page__num animated">3</p> </section> </div>
スライド画面の切り替えは、以下に定義されている CSS クラスを追加および削除する js を介して 2 つのページをスライド制御することで実現されます。
.page--active は現在表示されているページを示します。ページが初期化された後、次の js 呼び出しを使用して .page—active:
を最初のページに追加します。.page.page--active, .page.page--prev, .page.page--next { display: block; } .page.page--next, .page.page--active.page--active-right { -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } .page.page--prev, .page.page--active.page--active-left { -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } .page.page--next.page--next-left, .page.page--prev.page--prev-right, .page.page--active { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); }
这样页面默认就显示了第一页。以向左滑屏说明这些css的使用原理:
第一步,找到下一页的section,添加page--next类,将它定位当前页的右边,为滑屏做准备;
第二步,找到当前页的section,给它添加page--active-left,由于这个类改变了translate3D属性的值,所以当前页会往左滑动一屏;
在第二步的同时,给下一页的section,添加page--next-left,由于这个类改变了translate3D属性的值,所以下一页会往左滑动一屏;
第三步,在当前页跟下一页滑屏动画结束后,找到原来的当前页,移除掉page--active和page--active-left类;
在第三步的同时,找到下一页,移除掉page--next和page--next-left类,添加page--active。
gif图说明如下:
向右滑屏原理类似:
第一步,找到上一页的section,添加page--prev类,将它定位当前页的左边,为滑屏做准备;
第二步,找到当前页的section,给它添加page--active-right,由于这个类改变了translate3D属性的值,所以当前页会往右滑动一屏;
在第二步的同时,给上一页的section,添加page--prev-right,由于这个类改变了translate3D属性的值,所以上一页会往右滑动一屏;
第三步,在当前页跟上一页滑屏动画结束后,找到原来的当前页,移除掉page--active和page--active-right类;
在第三步的同时,找到上一页,移除掉page--prev和page--prev-right类,添加page--active。
综合以上实现原理,封装成JS函数如下:
var TRANSITION_DURATION = 400, sliding = false; function getSlideType($targetPage) { var activePageId = $activePage.attr('id'), targetPageId = $targetPage.attr('id'); return activePageId < targetPageId ? 'next' : activePageId == targetPageId ? '' : 'prev'; } function slide(targetPageId) { var $targetPage = $('#' + targetPageId); if (!$targetPage.length || sliding) return; var slideType = getSlideType($targetPage), direction = slideType == 'next' ? 'left' : 'right'; if (slideType == '') return; sliding = true; $targetPage.addClass('page--' + slideType); $targetPage[0].offsetWidth; $activePage.addClass('page--active-' + direction); $targetPage.addClass('page--' + slideType + '-' + direction); $activePage .one($.transitionEnd.end, function () { $targetPage.removeClass(['page--' + slideType, 'page--' + slideType + '-' + direction].join(' ')).addClass('page--active'); $activePage.removeClass(['page--active', 'page--active-' + direction].join(' ')); $activePage = $targetPage; sliding = false; }) .emulateTransitionEnd(TRANSITION_DURATION); }
由于$activePage在页面初始化的时候默认指定为第一页,在每次滑屏结束后都会更新成最新的当前页,所以调用的时候只要把目标页的ID传给slide函数即可。以上代码可能会有疑问的是:
1)$targetPage[0].offsetWidth的作用,这个代码用来触发浏览器的重绘,因为目标页原来是display: none的,如果不触发重绘的话,下一步添加css类后将看不到动画效果;
2)$.transitionEnd.end以及emulateTransitionEnd的作用,这个在下一部分说明。
4. 浏览器css动画结束的回调及模拟
bootstrap提供了一个工具,transition.js,用来判断浏览器是否支持css动画回调事件,以及在浏览器没有在动画结束后自动触发回调的特殊情况下通过模拟的方式来手动触发回调,原先这个工具只能配合jquery使用,为了在zepto中使用,必须稍微改变一下,下面就是改变之后的代码:
(function(){ var transition = $.transitionEnd = { end: (function () { var el = document.createElement('transitionEnd'), transEndEventNames = { WebkitTransition: 'webkitTransitionEnd', MozTransition: 'transitionend', OTransition: 'oTransitionEnd otransitionend', transition: 'transitionend' }; for (var name in transEndEventNames) { if (el.style[name] !== undefined) { return transEndEventNames[name]; } } return false; })() }; $.fn.emulateTransitionEnd = function (duration) { var called = false, _this = this, callback = function () { if (!called) $(_this).trigger(transition.end); }; $(this).one(transition.end, function () { called = true; }); setTimeout(callback, duration); return this; }; })();
$.transitionEnd.end表示当前浏览器支持的动画结束事件的名称。$.fn.emulateTransitionEnd是一个扩展了Zepto原型的一个方法,传入一个动画的过渡时间,当这个时间段过完之后,如果浏览器没有自动触发回调事件,called就始终是false,setTimeout会导致callback被调用,然后callback内部就会手动触发动画结束的回调。为什么要通过这个方式来模拟动画结束,是因为浏览器即使支持动画结束事件的回调,但是有些时候并不会触发这个事件,或者在动画结束后不能立即触发,影响回调的准确性。传入的duration应该与执行动画的元素,在css上设置的transtion-duration相同,注意以下代码中标黄的部分:
var TRANSITION_DURATION = 400 ; $activePage .one($.transitionEnd.end, function () { $targetPage.removeClass(['page--' + slideType, 'page--' + slideType + '-' + direction].join(' ')).addClass('page--active'); $activePage.removeClass(['page--active', 'page--active-' + direction].join(' ')); $activePage = $targetPage; sliding = false; }) .emulateTransitionEnd(TRANSITION_DURATION); .page { overflow: hidden; display: none; -webkit-transition: -webkit-transform .4s ease; transition: transform .4s ease; -webkit-backface-visibility: hidden; backface-visibility: hidden; }
5. hashchange事件
PC端滑屏都是给元素添加点击事件触发的,移动端可以利用window的hashchange事件:
$(window).on('hashchange', function (e) { var hash = location.hash; if (!hash) hash = '#page-1'; slide(hash.substring(1)); }); location.hash = '#page-1';
hashchange事件,在js代码中通过改变loaction.hash或者是点击2fc8db3327435aa54ad74d366b04c0cb下一页5db79b134e9f6b82c0b36e0489ee08ed这样的超链接时,都会触发,所以只要在这个事件的回调去做滑屏切换即可。这样那些上一页和下一页的链接元素都不用加事件了。
6. hammer.js使用简介
hammer.js是一个手势库,支持常用的手势操作,使用简单,引入它的js之后,通过以下的方式来支持手势滑屏:
//初始化手势滑动 var container = document.getElementById('container'), mc = new Hammer.Manager(container), Swipe = new Hammer.Swipe(); mc.add(Swipe); mc.on('swipeleft', function (e) { swipteTo('next', e); }); mc.on('swiperight', function (e) { swipteTo('prev', e); }); function swipteTo(slideType, e) { var $targetPage = $activePage[slideType]('.page'); $targetPage.length && (location.hash = '#' + $targetPage.attr('id')); }
Use the entire container element as the sliding stage. When the swipeleft event is heard, it means sliding to the left, and the page should display the next page; when the swiperight event is heard, it means sliding to the right, and the page should display the next page.
7. Conclusion
The use of animate.css will not be introduced in detail. It is relatively simple. This is its github address: https://github.com/daneden/animate.css, which is very easy to use. Animation library. This article records some recent work experience. Technical things sometimes cannot be fully explained in words, so I can only do my best to explain some issues in a little more detail. What is wrong and what is wrong? If you have any questions, please explain them to me in the comment area and I will check them carefully. In addition, I am not very familiar with the mobile terminal. If you have better insights, you are welcome to share them with us. Thank you for reading, and it’s New Year soon. I wish you good luck in the Year of the Monkey!