vue-防京东详情导航跟随滚动
前段时间 项目要求做类似京东详情导航。今日有空记录哈!
坑:window.scrollTo({top: 0, behavior: "smooth" }); 不知为何无法生效
解决:dom取包含总体内容的元素。次例子取 let el = this.$refs.wrapper;
大致样式:gif上看着导航的高亮状态闪的很快,实际上并不如此。新手新手不会操作
大致思路:
step1:先实现导航点击切换到响应锚点
step2:添加页面滚动事件,(注意离开当前页面时要销毁滚动事件,以免切换路由时影响其他路由。。。。)
完整代码:
<!-- * @Author: lingxie * @Date: 2020-05-27 14:35:35 * @Descripttion: --> <template> <div class="model-box"> <div class="nav-wrap" v-if="isShowNav" ref="navWrap"> <nav> <ul> <li v-for="(i,idx) in navList" :key="'nav'+idx"> <span :class="{'active':curNavIdx==idx}" @click="handleNav(idx)">{{i}}</span> </li> </ul> </nav> </div> <div class="con" ref="wrapper" id="wrapper"> <section ref="panel0"> <h3>商品</h3> <p v-for="(i,idx) in 20" :key="'0'+idx">商品</p> </section> <section ref="panel1"> <h3>评价</h3> <p v-for="(i,idx) in 20" :key="'0'+idx">评价</p> </section> <section ref="panel2"> <h3>详情</h3> <p v-for="(i,idx) in 10" :key="'0'+idx">详情</p> </section> <section ref="panel3"> <h3>推荐</h3> <p v-for="(i,idx) in 20" :key="'0'+idx">推荐</p> </section> </div> </div> </template> <script> export default { data() { return { isShowNav: false, //是否展示导航 scrollTop: 0, curNavIdx: 0, navList: ["商品", "评价", "详情", "推荐"] }; }, watch:{ curNavIdx(val){ console.log('索引',val); this.curNavIdx = val; } }, mounted() { this.$nextTick(() => { window.addEventListener("scroll", this.handleScroll, true); }); }, beforeDestroy() { window.removeEventListener("scroll", this.handleScroll, true); }, methods: { // 点击跳转至相应锚点 handleNav(idx) { this.curNavIdx = idx; let el = this.$refs.wrapper; let navHeight = this.$refs.navWrap.offsetHeight; let panel1T = this.$refs.panel1.offsetTop - navHeight; let panel2T = this.$refs.panel2.offsetTop - navHeight; let panel3T = this.$refs.panel3.offsetTop - navHeight; console.log(panel1T,panel2T,panel3T); if( this.curNavIdx==0){ console.log('进入',0); el.scrollTo({top: 0, behavior: "smooth" }); } if( this.curNavIdx==1){ console.log('进入',1); el.scrollTo({top: panel1T, behavior: "smooth" }); } if( this.curNavIdx==2){ console.log('进入',2); el.scrollTo({top: panel2T, behavior: "smooth" }); } if( this.curNavIdx==3){ console.log('进入',3); el.scrollTo({top: panel3T, behavior: "smooth" }); } }, handleScroll() { var el = this.$refs.wrapper; let scrollTop = el.scrollTop; // 获取当前的滚动距离 let clientHeight = el.clientHeight; //获取当前可视区域 let scrollHeight = el.scrollHeight; //获取当前滚动高度 // console.log(scrollTop); if (scrollTop > 30) { this.isShowNav = true; } else { this.isShowNav = false; } if (this.isShowNav) { // console.log(scrollTop,clientHeight,scrollHeight,';;;;;;;;;;;;;;'); // 判断是否滚动到底部 let isBottom = false; if (clientHeight + scrollTop === scrollHeight) { isBottom = true; console.log("滚动到底步了"); } var navE = this.$refs.navWrap; if (navE) { var navHeight = navE.offsetHeight; } let panel1T = this.$refs.panel1.offsetTop - navHeight; let panel2T = this.$refs.panel2.offsetTop - navHeight; let panel3T = this.$refs.panel3.offsetTop - navHeight; // console.log(panel1T,panel2T,panel3T,'推荐高度',panelH); if (scrollTop < panel1T) { if (this.curNavIdx != 0) { this.curNavIdx = 0; } } if (scrollTop >= panel1T && scrollTop < panel2T) { if (this.curNavIdx != 1) { this.curNavIdx = 1; } } if (scrollTop >= panel2T && scrollTop < panel3T) { if (this.curNavIdx != 2) { this.curNavIdx = 2; } } if (scrollTop >= panel3T || isBottom) { if (this.curNavIdx != 3) { this.curNavIdx = 3; } } } } } }; </script> <style lang="less" scoped> *{ margin: 0; padding: 0; box-sizing: border-box; } li{ list-style: none; } .model-box { height: 100%; .paddingT{ padding-top: 60px; } .con { // background: #f8f8f8; position: relative; height: 100%; overflow-y: scroll; &.paddingT{ top: 60px; } } } .nav-wrap { position: fixed; left: 50%; top: 0; width: 100%; transform: translateX(-50%); z-index: 99; background: lightblue; nav { height: 40px; } ul { display: flex; height: 100%; li { flex: 1; padding-top: 10px; text-align: center; position: relative; span { position: absolute; left: 50%; transform: translateX(-50%); font-size: 14px; color: #000; &::after { display: block; position: absolute; left: 50%; transform: translateX(-50%); bottom: -10px; width: 40px; height: 3px; content: ""; } &.active { &::after { background-image: linear-gradient( to right, rgb(21, 255, 165) 27%, rgb(21, 255, 165) 47%, rgb(105, 255, 197) 60%, rgb(189, 255, 229) 100% ); } } } } } } section { margin-bottom: 40px; } </style>
优化滚动事件:
handleScroll() { let el = this.$refs.wrapper; let scrollTop = el.scrollTop; // 获取当前的滚动距离 let clientHeight = el.clientHeight; //获取当前可视区域 let scrollHeight = el.scrollHeight; //获取当前滚动高度 // console.log(scrollTop); if (scrollTop > 30) { this.isShowNav = true; } else { this.isShowNav = false; } if (this.isShowNav) { // console.log(scrollTop,clientHeight,scrollHeight,';;;;;;;;;;;;;;'); // 判断是否滚动到底部 let isBottom = false; if (clientHeight + scrollTop === scrollHeight) { isBottom = true; console.log("滚动到底步了"); } var navE = this.$refs.navWrap; if (navE) { var navHeight = navE.offsetHeight; } let panel1T = this.$refs.panel1.offsetTop - navHeight; let panel2T = this.$refs.panel2.offsetTop - navHeight; let panel3T = this.$refs.panel3.offsetTop - navHeight; const offsetTopArr = [] offsetTopArr.push(0,panel1T,panel2T,panel3T) let navIndex = 0 for (let n = 0; n < offsetTopArr.length; n++) { // 若到底了,则当前索引为3 // 否则导航索引就应该是 n 了 if (scrollTop >= offsetTopArr[n]) { if(!isBottom){ navIndex = n; }else{ navIndex = 3; } } } this.curNavIdx = navIndex } }
一步一叩首,今天的自己比昨天好一点就行,明天的自己需追寻