최근에 화면이 2개뿐인 매우 작은 h5 애플리케이션을 만드는 작업이 있었습니다. 이전에는 이러한 작업을 수행하기 위해 fullpage.js와 jquery를 사용했습니다. , 그리고 성능이 별로 좋지 않아서 간단한 것을 직접 만들어서 구현해보고 싶었습니다. 마지막으로 이 문제를 해결하기 위해 zepto hammer.js와 carousel을 사용했는데 효과는 꽤 좋았습니다. 전체 페이지에서 Gzip을 활성화하지 않았을 때 모든 리소스 요청의 데이터 크기는 약 200KB였습니다. 이 문서에서는 이 메서드의 구현 아이디어를 요약합니다.
효과 시연:
1. 구현 포인트
1) 슬라이딩 화면은 부트스트랩의 캐러셀 플러그인을 사용하지만 그만큼 복잡하지는 않습니다. 캐러셀 구현 아이디어만 활용하면 됩니다.
2) 슬라이딩 화면 전환의 트리거링은 PC의 트리거링과 다릅니다. PC는 일반적으로 요소의 클릭 콜백에 의해 트리거됩니다. 슬라이딩 화면 페이지의 경우 창의 해시변경 이벤트에 의해 처리될 수 있습니다. 하이퍼링크를 통해 설정됩니다. 스위치는 js를 통해 앵커 포인트나 location.hash를 변경하여 트리거될 수 있습니다.
5) jquery 대신 zepto가 선호됩니다.
6) 슬라이딩 화면 효과는 애니메이션 종료 시 콜백에 응답하기 위해 Transition.js를 사용하는 것을 고려해 볼 수 있습니다. 이 역시 Bootstrap에서 제공하는 도구이지만 사용할 수만 있습니다. 기본적으로 jquery를 사용하므로 zepto와 함께 사용하면 약간 변경해야 합니다.
이러한 핵심 사항은 비교적 대략적인 내용이므로 다음 콘텐츠에서 하나씩 자세히 소개하겠습니다.
2.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>html,
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; }.container 및 .page는 초기화 시 절대 위치 지정 및 전체 화면 레이아웃을 채택합니다. 각 section.page는 페이지를 나타내며 기본적으로 표시되지 않습니다. 모든 페이지가 동일하게 배치됩니다. 즉, 모든 페이지가 표시되면 해당 페이지가 겹쳐집니다.
데모 페이지의 html 구조는 다음과 같습니다.
<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는 표시되지 않습니다. 그 중 animation은 github에서 사용 가능한 애니메이션 라이브러리인 animate.css를 적용하기 위해 필요합니다.
3. 슬라이딩 화면 전환 구현 아이디어
슬라이딩 화면 전환은 js를 통해 슬라이드되는 두 페이지를 제어하여 아래에 정의된 CSS 클래스를 추가 및 삭제함으로써 이루어집니다.
.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); }.page--active는 페이지가 초기화된 후 다음 js 호출을 사용하여 첫 번째 페이지에 .page—active:
这样页面默认就显示了第一页。以向左滑屏说明这些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')); }
전체 컨테이너 요소를 슬라이딩 스테이지로 사용하면 swipeleft 이벤트가 들리면 페이지가 왼쪽으로 슬라이드되고, swiperight 이벤트가 들리면 페이지가 다음 페이지를 표시해야 한다는 의미입니다. 페이지가 오른쪽으로 슬라이드되고 페이지에 다음 페이지가 표시되어야 합니다.
7. 결론
animate.css의 사용법은 자세히 소개하지 않겠습니다. github 주소는 https://github.com/daneden/animate.css입니다. 사용하기 쉬운 애니메이션 라이브러리. 이 기사에는 최근의 업무 경험이 기록되어 있습니다. 때로는 기술적인 사항을 말로 완전히 설명할 수 없기 때문에 최선을 다해 몇 가지 문제를 좀 더 자세히 설명하겠습니다. 궁금한 점이 있으면 문의해 주세요. 댓글란에서 설명해 주시면 자세히 확인해 보겠습니다. 또한 저는 모바일 단말기에 대해 잘 알지 못하기 때문에 더 나은 통찰력을 가지고 계시다면 언제든지 공유해 주시기 바랍니다. 읽어 주셔서 감사합니다. 곧 새해가 되어 원숭이 해에 행운이 가득하시길 바랍니다!