京東category側(cè)邊欄實(shí)現(xiàn)

先看看效果是怎樣的!

我不會(huì)生成gif圖,在網(wǎng)上找了個(gè)在線生成的但是就是有個(gè)水印,有空了去學(xué)習(xí)一下怎么生成gif哈哈哈


先看看側(cè)邊欄都做了哪些事情,有什么效果?

  • 可滑動(dòng)(touchmove)
  • 點(diǎn)擊其中一個(gè)分類時(shí)會(huì)置頂
  • 如果滑動(dòng)到無法滑動(dòng)的限值時(shí)再點(diǎn)擊其中分類不會(huì)置頂

簡(jiǎn)單分為這三個(gè)需求,然后分析一下如何實(shí)現(xiàn)

  • 需要封裝一個(gè)點(diǎn)擊事件,因?yàn)閏lick會(huì)有約300ms的延遲
  • 需要判斷是否已到達(dá)最大滑動(dòng)值,讓其點(diǎn)擊不再置頂
  • 在還沒滑動(dòng)到最大滑動(dòng)值時(shí)點(diǎn)擊,需要移動(dòng)側(cè)邊欄,將當(dāng)前點(diǎn)擊的分類移動(dòng)到頂部

那么先看看結(jié)構(gòu)


看看結(jié)構(gòu)代碼

<div class="jd_layout">
    <!--上圖中灰色框框:layout-->
    <div class="jd_category">
        <!--上圖中藍(lán)色框框:側(cè)邊欄盒子-->
        <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:;">內(nèi)衣配飾</a></li>
                <li class=""><a href="javascript:;">家用電器</a></li>
                <li class=""><a href="javascript:;">電腦辦公</a></li>
                <li class=""><a href="javascript:;">手機(jī)數(shù)碼</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:;">內(nèi)衣配飾</a></li>
                <li class=""><a href="javascript:;">家用電器</a></li>
                <li class=""><a href="javascript:;">電腦辦公</a></li>
                <li class=""><a href="javascript:;">手機(jī)數(shù)碼</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>

隨便寫了下樣式,有點(diǎn)丑,不要介意,這里就直接貼代碼了

<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>

好了,現(xiàn)在才是重點(diǎn),開始一步一步實(shí)現(xiàn)效果

(1)獲取側(cè)邊欄盒子、側(cè)邊欄盒子高度、ul、ul高度

var parentBox=document.querySelector('.jd_category_left');
var ulBox=parentBox.querySelector('ul');
var ulHeight=ulBox.offsetHeight;
var parentHeight=parentBox.offsetHeight;

(2)定義最大和最小Y坐標(biāo),后面用于判斷滑動(dòng)限值。定義彈簧效果坐標(biāo)值

/*靜止?fàn)顟B(tài)下的最大Y坐標(biāo)值*/
var maxPosition=0;
/*靜止?fàn)顟B(tài)下最小的Y坐標(biāo)值*/
var minPosition=parentH-ulH;
/*彈簧效果最大的Y坐標(biāo)值*/
var bounceMaxPosition=maxPosition+100;
/*彈簧效果最小的Y坐標(biāo)值*/
var bounceMinPosition=minPosition-100;

(3)到這里,先添加幾個(gè)公共方法

1.添加過渡
2.刪除過渡
3.設(shè)置ul移動(dòng)

/*添加過渡*/
var openTransition=function(){
    ulBox.style.transition="all .2s";
    ulBox.style.webkitTransition="all .2s";
};
/*移除過渡*/
var removeTransition=function(){
    ulBox.style.transition="none";
    ulBox.style.webkitTransition="none";
};
/*設(shè)置偏移*/
var setTransform=function(distanceY){
    ulBox.style.transform="translateY("+distanceY+"px)";
    ulBox.style.webkitTransform="translateY("+distanceY+"px)";
}

(4)接下來,要添加touch事件了

記錄點(diǎn)擊的時(shí)候的坐標(biāo)
記錄滑動(dòng)的時(shí)候的坐標(biāo)
記錄距離
記錄上一次touch結(jié)束的位置,后面需要累計(jì)每次滑動(dòng)的距離

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){
        /*獲取當(dāng)前手指滑動(dòng)的位置*/
        moveY=e.touches[0].clientY;
        /*計(jì)算本次的偏移值*/
        distanceY=moveY-startY;
        /*判斷當(dāng)前抖動(dòng)的距離,在之前的距離基礎(chǔ),是否已經(jīng)超出指定的范圍*/
        if(currentY+distanceY > bounceMaxPosition){
            console.log("別拉,沒啦");
            currentY=bounceMaxPosition;
        }
        else if(currentY+distanceY < bounceMinPosition){
            console.log("別拉,沒啦");
            currentY=bounceMinPosition;
        }
        else{
            /*移動(dòng)過渡*/
            removeTransition();
            /*設(shè)置偏移*/
            setTransform(currentY+distanceY);
        }
    });

(7)給ul添加touchend

    ulBox.addEventListener("touchend",function(e){
        /*判斷當(dāng)前的位置是否在靜止?fàn)顟B(tài)最小Y坐標(biāo)之外--比它還小*/
        if(currentY+distanceY < minPosition){
            currentY=minPosition;
            /*添加過渡*/
            openTransition();
            /*設(shè)置偏移*/
            setTransform(currentY);
        }
        else if(currentY+distanceY > maxPosition){
            currentY=maxPosition;
            /*添加過渡*/
            openTransition();
            /*設(shè)置偏移*/
            setTransform(currentY);
        }
        else{
            /*松開手指,結(jié)束本次滑動(dòng)的時(shí)候,要將距離存儲(chǔ)到變量中*/
            currentY+=distanceY;
        }
    });

(8)現(xiàn)在要給li標(biāo)簽設(shè)置索引了,因?yàn)橐c(diǎn)擊

    /*設(shè)置li標(biāo)簽的自定義索引*/
    var lis=ulBox.querySelectorAll("li");
    for(var i=0;i<lis.length;i++){
        /*lis[i].setAttribute("index",i);*/
        /*賦值索引*/
        lis[i].index=i;
    }

(9)那么說到點(diǎn)擊,在移動(dòng)端click是由300ms延遲的,上面說到要封裝一個(gè)tap

    var tap=function (dom,callback) {
        /*1.不能滑動(dòng)過
        * 2.end與start時(shí)間間隔一般在150ms內(nèi)*/
        var isMove=false;//標(biāo)記是否滑動(dòng)過
        var startTime=0;//記錄開始觸摸的時(shí)間
        dom.addEventListener("touchstart",function(e){
            /*記錄開始觸摸時(shí)間--以毫秒做為單位*/
            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);
            }
            /*重置標(biāo)記是否滑動(dòng)*/
            isMove=false;
        });
    }

(10)調(diào)用tap事件

    /*傳入父容器,那么可以通過e.target屬性來獲取當(dāng)前真正進(jìn)行響應(yīng)的子元素--捕獲*/
    tap(ulBox,function(e){
        /*清除所有l(wèi)i標(biāo)簽的樣式*/
        var activeLi=ulBox.querySelector(".active");
        activeLi.classList.remove("active");

        /*拿到所有l(wèi)i標(biāo)簽*/
        var li= e.target.parentNode;
        /*獲取li標(biāo)簽的高度*/
        var liH=li.offsetHeight;

        /*添加li標(biāo)簽的樣式*/
        li.classList.add("active");

        /*移動(dòng)需要先獲取當(dāng)前你所點(diǎn)擊的li標(biāo)簽的索引*/
        var index=li.index;

        /*判斷:如果在之前的移動(dòng)基礎(chǔ)之上(之前偏移的值在點(diǎn)擊過后,其實(shí)已經(jīng)包含在點(diǎn)擊的偏移值以內(nèi)) ,再進(jìn)行當(dāng)前點(diǎn)擊所需要的偏移,如果這個(gè)偏移值造成元素在靜止?fàn)顟B(tài)下Y坐標(biāo)的最小值以外,那么我們就應(yīng)該讓其Y固定在最小區(qū)間位置*/
        if(-index*liH < minPosition){
            currentY=minPosition;
            openTransition();
            setTransform(minPosition);
        }
        /*否則,就按正常方式進(jìn)行偏移,同時(shí)重置currentY的值,以便下次可以正常的滑動(dòng)*/
        else{
            /*開啟過渡*/
            openTransition();
            /*設(shè)置偏移*/
            setTransform(-index*liH);
            /*一定要記得一個(gè)事情,重置currentY*/
            currentY=-index*liH;
        }
    })

總結(jié):

  • 先讓ul能夠滑動(dòng)起來
  • 然后記錄距離值(每次累計(jì))
  • 在touch事件中判斷是否達(dá)到限值
  • 給每個(gè)li賦值索引
  • 點(diǎn)擊li后設(shè)置置頂(設(shè)置偏移,負(fù)的索引值*li的高度),同時(shí)判斷是否達(dá)到滑動(dòng)限制,如果達(dá)到則不能置頂
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,167評(píng)論 25 708
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,422評(píng)論 4 61
  • 今天早上起來我就想去水上樂園玩,就給媽媽說帶我去吧,媽媽說你把今天的作業(yè)完成了就帶你們?nèi)ァN揖挖s緊把所有的作...
    張博裕閱讀 309評(píng)論 0 1
  • 1. 對(duì)內(nèi)容推薦的一些看法 前幾天發(fā)現(xiàn)B站移動(dòng)端的首頁-番劇好像改版了,番劇推薦被移到了外面(按以前的印象是放在索...
    嵐BeMilkTeaLover閱讀 734評(píng)論 0 0
  • 牛刀小試 淺copy 和深度copy的區(qū)別 copy/mutab...
    瘋狂的木頭人閱讀 278評(píng)論 1 1

友情鏈接更多精彩內(nèi)容