6

滚动效果
当页面滑动时,左边导航栏会自动定位到当前标题,一级标题展开,二级标题的字体变红色,字体前面有一个小图标。
上图。。。。。。。。。。。

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>

一、明确各种位置

clipboard.png

图片来源于网络

这次项目最常用的就是 scrollTop 、offsetTop
其中 srcollTop可以理解为 :页面随着滚动条下滑而隐藏的高度
offsetTop 可以理解为 :当前元素距离它最近的祖先元素顶部的距离,并且这个祖先元素的position不为static。如果没有,则是该元素与body顶部的距离

二、滚动思路

clipboard.png

滚动到什么地方,左边导航栏出现样式??
当滚动的高度>=右边第i个div的offsetTop,则左边的第i个标题出现样式。

三、添加滚动监听事件
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(){
            //这里放的函数下面讲到
    }
}

四、在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>

前端新手,代码冗长,有不合理和需要改进的地方麻烦提出


houqq
245 声望14 粉丝