写在前面
最近在做移动端方面运用到了饿了么的vue前端组件库,因为不想单纯用组件而使用它,故想深入了解一下实现原理。后续将会继续研究一下其他的组件实现原理,有兴趣的可以关注下。
代码在这里:
1. 说明
父容器页面
2. 核心解析
2.1 页面初始化
由于所有页面都在手机屏幕左侧一个屏幕宽度的位置,因此最开始的情况是页面中看不到任何一个子页面,所以第一步应该设置应该显示的子页面,默认情况下
// 得到当前被激活的子页面索引 children.forEach(function(child,index) { }); pages = aPages; 2.2 容器滑动开始(onTouchStart) 在低版本的性能提升作用,使得滑动起来不是那么卡。 前置工作: 滑动开始: 使用一个全局对象记录信息,这些信息包括: 2.3 容器滑动(onTouchMove) 套用全局
那么我们就可以通过开始和滑动中的信息来计算出一些东西: 滑动的水平位移(offsetLeft = currentLeft - startLeft) 滑动的垂直位移(offsetTop = currentTopAbsolute - startTopAbsolute) 是否是用户的自然滚动,这里的自然滚动说的是用户并不是想滑动swiper,而是想滑动页面 判断是左移还是右移(offsetLeft < 0 左移,反之,右移) 重置位移 // 当前页面跟着滑动 // 后一个页面同理 2.4 滑动结束(onTouchEnd) 前置工作: 在滑动中,我们是可以实时地来判断到底是不是用户的自然滚动userScrolling,如果是用户自然滚动,那么
当然如果userScrolling:false,那么就是滑动子页面,执行doOnTouchEnd方法 判断是否是tap事件 判断方向 // 如果非连续,当处于第一页,不会出现上一页,当处于最后一页,不会出现下一页 // 子页面数量小于2时,不执行滑动动画 执行动画 <div class="jb51code"> var prevPage,nextPage,currentPage,pageWidth,offsetLeft; // 定时器滑动 } else { var newIndex; // 得到滑动之后的新的索引 // 动画完成之后的回调 } setTimeout(function() { },10);
var intDefaultIndex = Math.floor(defaultIndex);
var defaultIndex = (intDefaultIndex >= 0 && intDefaultIndex < children.length)
? intDefaultIndex : 0;
index = defaultIndex;
aPages.push(child);
// 所有页面移除激活class
child.classList.remove('is-active');if (index === defaultIndex) {
// 给激活的子<a href="https://www.jb51.cc/tag/yemian/" target="_blank" class="keywords">页面</a><a href="https://www.jb51.cc/tag/jiashang/" target="_blank" class="keywords">加上</a>激活class
child.classList.add('is-active');
}
}
translate(dragState.dragPage,offsetLeft);
if (dragState.nextPage && towards === 'next') {
translate(dragState.nextPage,offsetLeft + dragState.pageWidth);
}
if (dragDuration < 300 || Math.abs(offsetLeft) > pageWidth / 2) {
towards = offsetLeft < 0 ? 'next' : 'prev';
}
if (!continuous) {
if ((index === 0 && towards === 'prev')
|| (index === pageCount - 1 && towards === 'next')) {
towards = null;
}
}
if (children.length < 2) {
towards = null;
}
<pre class="brush:js;">
// 当没有options的时候,为自然滑动,也就是定时器滑动
function doAnimate(towards,options) {
if (children.length === 0) return;
if (!options && children.length < 2) return;
var pageCount = pages.length;
if (!options) {
pageWidth = element.clientWidth;
currentPage = pages[index];
prevPage = pages[index - 1];
nextPage = pages[index + 1];
if (continuous && pages.length > 1) {
if (!prevPage) {
prevPage = pages[pages.length - 1];
} if (!nextPage) {
nextPage = pages[0];
}
}
// 计算<a href="https://www.jb51.cc/tag/shangyiye/" target="_blank" class="keywords">上一页</a>与<a href="https://www.jb51.cc/tag/xiayiye/" target="_blank" class="keywords">下一页</a>之后
// 重置位移
// 参看doOnTouchMove
// 其实这里的options 传与不传也就是<a href="https://www.jb51.cc/tag/huoqu/" target="_blank" class="keywords">获取</a><a href="https://www.jb51.cc/tag/shangyiye/" target="_blank" class="keywords">上一页</a>信息与<a href="https://www.jb51.cc/tag/xiayiye/" target="_blank" class="keywords">下一页</a>信息
if (prevPage) {
prevPage.style.display = 'block';
translate(prevPage,-pageWidth);
}
if (nextPage) {
nextPage.style.display = 'block';
translate(nextPage,pageWidth);
}
prevPage = options.prevPage;
currentPage = options.currentPage;
nextPage = options.nextPage;
pageWidth = options.pageWidth;
offsetLeft = options.offsetLeft;
}
var oldPage = children[index];
if (towards === 'prev') {
if (index > 0) {
newIndex = index - 1;
}
if (continuous && index === 0) {
newIndex = pageCount - 1;
}
} else if (towards === 'next') {
if (index < pageCount - 1) {
newIndex = index + 1;
}
if (continuous && index === pageCount - 1) {
newIndex = 0;
}
}
var callback = function() {
// 得到滑动之后的激活页面,添加激活class
// 重新赋值索引
if (newIndex !== undefined) {
var newPage = children[newIndex];
oldPage.classList.remove('is-active');
newPage.classList.add('is-active');
index = newIndex
}if (isDone) {
end();
}
if (prevPage) {
prevPage.style.display = '';
}
if (nextPage) {
nextPage.style.display = '';
}
// 向后滑动
if (towards === 'next') {
isDone = true;
before(currentPage);
// 当前页执行动画,完成后执行callback
translate(currentPage,-pageWidth,speed,callback);
if (nextPage) {
// 下一面移动视野中
translate(nextPage,speed)
}
} else if (towards === 'prev') {
isDone = true;
before(currentPage);
translate(currentPage,callback);
if (prevPage) {
translate(prevPage,speed);
}
} else {
// 如果既不是左滑也不是右滑
isDone = true;
// 当前页面依旧处于视野中
// 上一页和下一页滑出
translate(currentPage,callback);
if (typeof offsetLeft !== 'undefined') {
if (prevPage && offsetLeft > 0) {
translate(prevPage,pageWidth * -1,speed);
}
if (nextPage && offsetLeft < 0) {
translate(nextPage,speed);
}
} else {
if (prevPage) {
translate(prevPage,speed);
} if (nextPage) {
translate(nextPage,speed);
}
}
}
}