vue實現滾動監聽,點擊瞄點平滑滾動,控制內嵌滾動條滾動

滾動效果
當頁面滑動時,左邊導航欄會自動定位到當前標題,一級標題展開,二級標題的字體變紅色,字體前面有一個小圖標。
上圖。。。。。。。。。。。 html

clipboard.png

Html代碼前端

<template>
    <div id="notes">
        <nav-top></nav-top>
        <!-- 中間部分 -->
        <div class="notes_detail_page">
          <div class="left" id="left_y_scroll" ref="lys">
            <div >
                <div class="side_nav" id="chap_nav">
                    <ul v-for="NItem1 in chapList" class="side_nav_level1">
                        <li id="lis" @click="UpClick(NItem1.ordered)"  class="first_li" >{{NItem1.ordered}}&nbsp;{{NItem1.title}}
                            <ul  class="side_nav_level2" v-show="UnderTitle.id==NItem1.ordered&&UnderTitle.isshow" >
                                <li  v-for="(NItem2,index) in NItem1.episodeList" @click.stop :id="'lNav'+(NItem2.id-1)">
                                                 <!--@click.stop
                                                    阻止事件冒泡
                                                 -->
                                    <span>
                                        <img v-show="isshow==NItem2.id-1" src="@/assets/images/landmark.png"/>
                                    </span>
                                    <a id="nav_a" @click="jump(NItem2.id-1)" :class="{'a_red':isshow==NItem2.id-1}">{{NItem1.ordered}}.{{NItem2.ordered}}&nbsp;{{NItem2.title}}</a>
                                </li>
                            </ul>
                        </li>
                    </ul>
                </div>
          </div>

        </div>
          <div class="right">
            <div id="hide">
                <h1 id="v_title">{{VideoList.title}}</h1>
                   <div class="avatar_items">
                       <img class="avatar" :src="AuthorList.head_img"/>
                       <div class="items">
                           <p id="author">{{AuthorList.name}}</p>
                           <span id="c_time">{{VideoList.create_time}}</span>
                           <img class="view" src="@/assets/images/view.png"/>
                           <span id="viewNum">{{VideoList.view_num}}</span>
                           <img class="fabulous" src="@/assets/images/fabulous.png"/>
                           <span id="praise_num">6666</span>
                       </div>
                   </div>
                   <div class="note_list"  v-for="NItem1 in chapList">
                        <div v-for="NItem2 in NItem1.episodeList" class="notes_text" :data-id="NItem1.ordered">
                            <h2 v-html="NItem1.ordered+'.'+NItem2.ordered+'&nbsp;'+NItem2.title"></h2>
                            <p v-html="NItem2.note"></p>
                        </div>
                   </div>
            </div>
           </div>
          </div>

        <footer-container></footer-container>
    </div>
</template>

1、明確各類位置vue

clipboard.png

圖片來源於網絡ios

此次項目最經常使用的就是 scrollTop 、offsetTop
其中 srcollTop能夠理解爲 :頁面隨着滾動條下滑而隱藏的高度
offsetTop 能夠理解爲 :當前元素距離它最近的祖先元素頂部的距離,而且這個祖先元素的position不爲static。若是沒有,則是該元素與body頂部的距離web

2、滾動思路axios

clipboard.png

滾動到什麼地方,左邊導航欄出現樣式??
當滾動的高度>=右邊第i個div的offsetTop,則左邊的第i個標題出現樣式。api

3、添加滾動監聽事件
1.添加滾動監聽事件
知識點:addEventListener() 方法,事件監聽
用法:element.addEventListener(event, function, useCapture);
第一個參數是:事件的類型。這個項目是滾動監聽,因此event是寫「scroll」。(其餘項目可根據需求,若是是點擊就寫「click」,以此類推);
第二個參數是:監聽事件時調用的函數。這裏調用的是 this.menu 這個函數。
第三個參數是:布爾值。即true/false,這個參數是與事件捕獲和事件冒泡有關,與這兩個有關通常是點擊事件,這個項目是滾動事件,因此第三個參數能夠不寫。數組

mounted(){
    window.addEventListener("scroll",this.menu)
}

2.在data裏面定義一個字符串scroll存放滾動的高度也就是scrollTop,而且在watch中監聽data裏面scroll的數據變化。瀏覽器

data(){
    return{
        scroll:""
        }
},
watch(){
    srcoll:function(){
            //這裏放的函數下面講到
    }
}

4、在methods定義方法,主要有三個步驟網絡

1.觸發滾動監聽事件時,調用的函數,此函數的做用是獲取滾動條的高度

menu:function(){
    this.srcoll=window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
}
//兼容不一樣的三個瀏覽器版本
//也就是上文調用的函數window.addEventListener("scroll",this.menu)

2.定義一個平滑滾動的方法,參考網絡上的大神寫的。

定義變量total=div[index].offsetTop,則滾動條就須要滾動這個total的距離。
爲了達到平滑滾動的效果,把總距離total分紅50個小段,每10ms執行一次。
而且還要區分是向上滑動仍是向下滑動,完整代碼以下:

jump:function(index){ //把左邊導航欄li的下標傳進來。
                this.isshow=index;//使左邊導航出現相應的樣式
                let divArr=document.querySelectorAll(".notes_text");//獲取右邊的div數組
                let total=divArr[index].offsetTop;//獲取第index個div到窗口頂部的距離
                let distance = document.documentElement.scrollTop || document.body.scrollTop || window.pageYOffset//獲取滾動條的高度(兼容三種瀏覽器版本)

                //平滑滾動的效果,把總距離分紅50個小段,每10ms執行一次
                let step = total / 50 
                if (total > distance) { //當divArr.[index]offsetTop的距離>滾動條的距離,向下滑動,此時滑動的距離是total
                  smoothDown() //向下滑動
                } else {
                  let newTotal = distance - total //當div到窗口的距離<滾動條的距離,向上滑動,此時滑動的距離是distance - total
                  step = newTotal / 50;
                  smoothUp()
                }

                //向下滑動
                function smoothDown () {
                  if (distance < total) {
                    distance = distance + step
               document.body.scrollTop = distance
                    document.documentElement.scrollTop = distance
                    window.pageYOffset = total;
                    setTimeout(smoothDown, 10)
                  } else {
                    document.body.scrollTop = total
                    document.documentElement.scrollTop = total
                    window.pageYOffset = total;
                  }
                }

                //向上滑動
                function smoothUp () {
                  if (distance > total) {
                    distance -= step
               document.body.scrollTop = distance
                    document.documentElement.scrollTop = distance
                    window.pageYOffset = total;
                    setTimeout(smoothUp, 10)
                  } else {
                    document.body.scrollTop = total
                    document.documentElement.scrollTop = total
                    window.pageYOffset = total;
                  }
               }
            }

3.定義方法實現當滾動條的高度>divArr[index].offsetTop時,左邊樣式改變的函數

loadSroll: function () {
                let divArr=document.querySelectorAll(".notes_text");//獲取右邊div的數組
                for (var i = 0; i < divArr.length; i++) { 
                    if (this.scroll >= divArr[i].offsetTop) { //滾動條的高度>divArr[index].offsetTop,左邊導航欄樣式改變
                        var OrderId = divArr[i].getAttribute("data-id");//獲取div中data-id的屬性值。爲了實現左邊一級標題打開的效果
                        this.UnderTitle={id:OrderId,isshow:true};//實現一級標題打開
                        this.isshow = i;//實現字體變紅,小圖標出現

                        //控制左邊內嵌滾動條滾動
                        var lOffsetTop = document.getElementById("lNav"+i).offsetTop;//獲取左邊導航欄li距離li最近的祖先元素id爲left_y_scroll的元素的頂部的距離。(left_y_scroll的position爲fixed),詳情見下圖
                        if(lOffsetTop > 300){  //當li的offsetTop大於300時,滾動條須要下滑,由於整個div的高度爲350px,因此我這裏設置了300
                            this.$refs.lys.scrollTop = lOffsetTop; //使子滾動條的滾動高度,等於lOffsetTop
                        }else{
                            this.$refs.lys.scrollTop = 0;//在不大於300的狀況下,子滾動條的高度爲0.
                        }

                    }
                }
            }

這裏的這個函數,須要放在watch裏面,由於這裏的this.srcoll 是data裏面的srcoll,而srcoll是靠watch監聽。
因此咱們須要在watch()裏面寫:

watch(){
    srcoll:function(){
        this.loadSroll()
    }
}

lOffsetTop的距離以下圖

clipboard.png

滾動監聽的效果,就能夠實現了
完成代碼以下!!!!!

<script>
    import NavTop from "@/components/NavTop.vue"
    import FooterContainer from "@/components/FooterContainer.vue"
    export default{
        components:{
            NavTop,
            FooterContainer
        },
        created(){
                this.init();
        },
        data(){
            return{
                chapList:[],//一級標題和二級標題的數組
                VideoList:[],
                AuthorList:[],
                epiList:[],
                UnderTitle:"",//左邊導航欄的狀態判斷,其中isshow爲true且id相符時,顯示
                scroll:"",//存儲滾動條的高度
                isshow:-1,//默認導航欄小圖標不顯示,點擊以後顯示
                left_up:false,
            }
        },
        watch: {
            scroll: function () { //用來監聽data中的scroll字符串的變化
                this.loadSroll()
            }
        },
        mounted() {
            window.addEventListener('scroll', this.menu);//在mounted鉤子中給window添加一個滾動監聽事件
        },
        methods:{
            init:function(){
                this.getVideoDetail();
            },
            getVideoDetail:function(){
                this.$axios.get(this.GLOBAL.host+"/pub/api/v1/web/video_detail",{
                    params:{
                        video_id:this.$route. query.video_id
                    }
                }).then(res =>{
                    //console.log(res.data.data);
                    this.VideoList=res.data.data.video;
                    this.AuthorList=res.data.data.author;
                    this.chapList=res.data.data.chapter_list;
                    //console.log(this.chapList);
                    for(var i=0;i<this.chapList.length;i++){
                        for(var j=0;j<this.chapList[i].episodeList.length;j++){
                            this.epiList.push(this.chapList[i].episodeList[j])
                            for(var k=0;k<this.epiList.length;k++){
                                this.epiList[k].id=k+1;
                                //console.log(k)
                            }
                        }
                    }
                    //console.log(this.epiList);
                });
            },
            UpClick:function(id){
                if(typeof this.UnderTitle.isshow=="underfine"||this.UnderTitle.id!=id){
                    this.UnderTitle={id:id,isshow:true}
                }else{
                    //當點擊兩次以上,ul隱藏的狀況
                    this.UnderTitle.isshow=!this.UnderTitle.isshow;
                }
            },
            menu:function(){
                //獲取滾動條的高度
                this.scroll=window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
            },
            jump:function(index){
                this.isshow=index;//使左邊導航出現相應的樣式
                //console.log(index)
                let divArr=document.querySelectorAll(".notes_text");//獲取右邊的div數組
                let total=divArr[index].offsetTop;//獲取第index個div的offsetTop距離
                let distance = document.documentElement.scrollTop || document.body.scrollTop || window.pageYOffset//獲取滾動條的高度(兼容三種瀏覽器版本)

                //平滑滾動的效果,把總距離分紅50個小段,每10ms執行一次
                let step = total / 50 
                if (total > distance) { //當div到窗口的距離>滾動條的距離,向下滑動,此時滑動的距離是total
                  smoothDown() //向下滑動
                } else {
                  let newTotal = distance - total //當div到窗口的距離<滾動條的距離,向上滑動,此時滑動的距離是distance - total
                  step = newTotal / 50;
                  smoothUp()
                }

                //向下滑動
                function smoothDown () {
                  if (distance < total) {
                    distance = distance + step
               document.body.scrollTop = distance
                    document.documentElement.scrollTop = distance
                    window.pageYOffset = total;
                    setTimeout(smoothDown, 10)
                  } else {
                    document.body.scrollTop = total
                    document.documentElement.scrollTop = total
                    window.pageYOffset = total;
                  }
                }

                //向上滑動
                function smoothUp () {
                  if (distance > total) {
                    distance -= step
               document.body.scrollTop = distance
                    document.documentElement.scrollTop = distance
                    window.pageYOffset = total;
                    setTimeout(smoothUp, 10)
                  } else {
                    document.body.scrollTop = total
                    document.documentElement.scrollTop = total
                    window.pageYOffset = total;
                  }
               }
            },
            loadSroll: function () {
                let divArr=document.querySelectorAll(".notes_text");
                //console.log(divArr)
                for (var i = 0; i < divArr.length; i++) {
                    if (this.scroll >= divArr[i].offsetTop - 88 ) { //這裏其實能夠減去一個數值,使其左邊的導航欄的樣式提早出現,我這裏是減去了top欄的高度。
                        //console.log(i)
                        var OrderId = divArr[i].getAttribute("data-id");
                        //console.log(OrderId);
                        this.UnderTitle={id:OrderId,isshow:true};
                        this.isshow = i;
                        var lOffsetTop = document.getElementById("lNav"+i).offsetTop;//獲取li到窗口的距離
                        console.log(lOffsetTop);
                        if(lOffsetTop > 300){  //當li的offsetTop大於300時,滾動條須要下滑,由於整個div的高度爲350px,因此我這裏設置了300
                            this.$refs.lys.scrollTop = lOffsetTop; //使子滾動條的滾動高度,等於lOffsetTop
                        }else{
                            this.$refs.lys.scrollTop = 0;//在不大於300的狀況下,子滾動條的高度爲0.
                        }

                    }
                }
            }
        },

    }


</script>

前端新手,代碼冗長,有不合理和須要改進的地方麻煩提出

相關文章
相關標籤/搜索