先看看效果是怎样的!
我不会生成gif图,在网上找了个在线生成的但是就是有个水印,有空了去学习一下怎么生成gif哈哈哈
先看看侧边栏都做了哪些事情,有什么效果?
- 可滑动(touchmove)
- 点击其中一个分类时会置顶
- 如果滑动到无法滑动的限值时再点击其中分类不会置顶
简单分为这三个需求,然后分析一下如何实现
- 需要封装一个点击事件,因为click会有约300ms的延迟
- 需要判断是否已到达最大滑动值,让其点击不再置顶
- 在还没滑动到最大滑动值时点击,需要移动侧边栏,将当前点击的分类移动到顶部
那么先看看结构
看看结构代码
<div class="jd_layout">
<!--上图中灰色框框:layout-->
<div class="jd_category">
<!--上图中蓝色框框:侧边栏盒子-->
<div class="jd_category_left">
<!--上图中红色框框:ul-->
<ul class="clearFix">
<li class="active"><a href="javascript:;">热门推荐</a></li>
<li class=""><a href="javascript:;">潮流女装</a></li>
<li class=""><a href="javascript:;">品牌男装</a></li>
<li class=""><a href="javascript:;">内衣配饰</a></li>
<li class=""><a href="javascript:;">家用电器</a></li>
<li class=""><a href="javascript:;">电脑办公</a></li>
<li class=""><a href="javascript:;">手机数码</a></li>
<li class=""><a href="javascript:;">母婴频道</a></li>
<li class=""><a href="javascript:;">图书</a></li>
<li class=""><a href="javascript:;">家居家纺</a></li>
<li class=""><a href="javascript:;">居家生活</a></li>
<li class=""><a href="javascript:;">家具建材</a></li>
<li class=""><a href="javascript:;">热门推荐</a></li>
<li class=""><a href="javascript:;">潮流女装</a></li>
<li class=""><a href="javascript:;">品牌男装</a></li>
<li class=""><a href="javascript:;">内衣配饰</a></li>
<li class=""><a href="javascript:;">家用电器</a></li>
<li class=""><a href="javascript:;">电脑办公</a></li>
<li class=""><a href="javascript:;">手机数码</a></li>
<li class=""><a href="javascript:;">母婴频道</a></li>
<li class=""><a href="javascript:;">图书</a></li>
<li class=""><a href="javascript:;">家居家纺</a></li>
<li class=""><a href="javascript:;">居家生活</a></li>
<li class=""><a href="javascript:;">家具建材</a></li>
</ul>
</div>
</div>
</div>
随便写了下样式,有点丑,不要介意,这里就直接贴代码了
<style type="text/css">
html,body{
height: 100%;
}
.jd_layout{
width: 100%;
height: 100%;
margin: 0 auto;
}
.jd_category{
width:100%;
position: absolute;
height: 100%;
/*padding-top: 45px;*/
}
.jd_category>.jd_category_left{
width: 100px;
height: 100%;
overflow: hidden;
float: left;
}
.jd_category>.jd_category_left>ul{
width: 100%;
}
.jd_category>.jd_category_left>ul>li{
width: 100%;
height: 50px;
border-right:1px solid #ccc;
border-bottom:1px solid #ccc;
background-color: #eee;
}
.jd_category>.jd_category_left>ul>li.active{
border:none;
background-color: #ffffff;
color: #e92322;
}
.jd_category>.jd_category_left>ul>li.active>a{
color: #e92322;
}
.jd_category>.jd_category_left>ul>li>a{
display: block;
width: 100%;
height: 50px;
text-align: center;
line-height:50px;
font-size:14px;
color: #000;
}
</style>
好了,现在才是重点,开始一步一步实现效果
(1)获取侧边栏盒子、侧边栏盒子高度、ul、ul高度
var parentBox=document.querySelector('.jd_category_left');
var ulBox=parentBox.querySelector('ul');
var ulHeight=ulBox.offsetHeight;
var parentHeight=parentBox.offsetHeight;
(2)定义最大和最小Y坐标,后面用于判断滑动限值。定义弹簧效果坐标值
/*静止状态下的最大Y坐标值*/
var maxPosition=0;
/*静止状态下最小的Y坐标值*/
var minPosition=parentH-ulH;
/*弹簧效果最大的Y坐标值*/
var bounceMaxPosition=maxPosition+100;
/*弹簧效果最小的Y坐标值*/
var bounceMinPosition=minPosition-100;
(3)到这里,先添加几个公共方法
1.添加过渡
2.删除过渡
3.设置ul移动
/*添加过渡*/
var openTransition=function(){
ulBox.style.transition="all .2s";
ulBox.style.webkitTransition="all .2s";
};
/*移除过渡*/
var removeTransition=function(){
ulBox.style.transition="none";
ulBox.style.webkitTransition="none";
};
/*设置偏移*/
var setTransform=function(distanceY){
ulBox.style.transform="translateY("+distanceY+"px)";
ulBox.style.webkitTransform="translateY("+distanceY+"px)";
}
(4)接下来,要添加touch事件了
记录点击的时候的坐标
记录滑动的时候的坐标
记录距离
记录上一次touch结束的位置,后面需要累计每次滑动的距离
var startY=0;
var moveY=0;
var distanceY=0;
var currentY=0;
(5)给ul添加touchstart
ulBox.addEventListener("touchstart",function(e){
/*获取起始位置*/
startY= e.touches[0].clientY;
});
(6)给ul添加touchmove
ulBox.addEventListener("touchmove",function(e){
/*获取当前手指滑动的位置*/
moveY=e.touches[0].clientY;
/*计算本次的偏移值*/
distanceY=moveY-startY;
/*判断当前抖动的距离,在之前的距离基础,是否已经超出指定的范围*/
if(currentY+distanceY > bounceMaxPosition){
console.log("别拉,没啦");
currentY=bounceMaxPosition;
}
else if(currentY+distanceY < bounceMinPosition){
console.log("别拉,没啦");
currentY=bounceMinPosition;
}
else{
/*移动过渡*/
removeTransition();
/*设置偏移*/
setTransform(currentY+distanceY);
}
});
(7)给ul添加touchend
ulBox.addEventListener("touchend",function(e){
/*判断当前的位置是否在静止状态最小Y坐标之外--比它还小*/
if(currentY+distanceY < minPosition){
currentY=minPosition;
/*添加过渡*/
openTransition();
/*设置偏移*/
setTransform(currentY);
}
else if(currentY+distanceY > maxPosition){
currentY=maxPosition;
/*添加过渡*/
openTransition();
/*设置偏移*/
setTransform(currentY);
}
else{
/*松开手指,结束本次滑动的时候,要将距离存储到变量中*/
currentY+=distanceY;
}
});
(8)现在要给li标签设置索引了,因为要点击
/*设置li标签的自定义索引*/
var lis=ulBox.querySelectorAll("li");
for(var i=0;i<lis.length;i++){
/*lis[i].setAttribute("index",i);*/
/*赋值索引*/
lis[i].index=i;
}
(9)那么说到点击,在移动端click是由300ms延迟的,上面说到要封装一个tap
var tap=function (dom,callback) {
/*1.不能滑动过
* 2.end与start时间间隔一般在150ms内*/
var isMove=false;//标记是否滑动过
var startTime=0;//记录开始触摸的时间
dom.addEventListener("touchstart",function(e){
/*记录开始触摸时间--以毫秒做为单位*/
startTime=Date.now();
});
dom.addEventListener("touchmove",function(e){
isMove=true;
});
dom.addEventListener("touchend",function(e){
if(isMove==false && Date.now()-startTime <=150){
callback && callback(e);
}
/*重置标记是否滑动*/
isMove=false;
});
}
(10)调用tap事件
/*传入父容器,那么可以通过e.target属性来获取当前真正进行响应的子元素--捕获*/
tap(ulBox,function(e){
/*清除所有li标签的样式*/
var activeLi=ulBox.querySelector(".active");
activeLi.classList.remove("active");
/*拿到所有li标签*/
var li= e.target.parentNode;
/*获取li标签的高度*/
var liH=li.offsetHeight;
/*添加li标签的样式*/
li.classList.add("active");
/*移动需要先获取当前你所点击的li标签的索引*/
var index=li.index;
/*判断:如果在之前的移动基础之上(之前偏移的值在点击过后,其实已经包含在点击的偏移值以内) ,再进行当前点击所需要的偏移,如果这个偏移值造成元素在静止状态下Y坐标的最小值以外,那么我们就应该让其Y固定在最小区间位置*/
if(-index*liH < minPosition){
currentY=minPosition;
openTransition();
setTransform(minPosition);
}
/*否则,就按正常方式进行偏移,同时重置currentY的值,以便下次可以正常的滑动*/
else{
/*开启过渡*/
openTransition();
/*设置偏移*/
setTransform(-index*liH);
/*一定要记得一个事情,重置currentY*/
currentY=-index*liH;
}
})
总结:
- 先让ul能够滑动起来
- 然后记录距离值(每次累计)
- 在touch事件中判断是否达到限值
- 给每个li赋值索引
- 点击li后设置置顶(设置偏移,负的索引值*li的高度),同时判断是否达到滑动限制,如果达到则不能置顶