vue实现 (2022-7-18更新)
效果图
交互重点
- 除基本的交互效果,支持监听内容高度改变,动态计算锚点元素的offsetTop
- 锚点元素的offsetTop计算规则,基于最外层滚动容器,非最近的非static的父元素
页面结构
实现代码
- 绑定滚动事件
this.scrollBox = $(this.$refs.scrollBox)
this.scrollBox.bind('scroll', ()=>{
if (this.isClickAnchor) {
return
}
// 滚动
this.autoLightAnchor()
})
- 锚点元素offsetTop值
getOffsetTop(obj) {
let offsetTop = 0
while (obj != this.scrollBox[0] && obj != null) {
offsetTop += obj.offsetTop
obj = obj.offsetParent
}
return offsetTop
},
- 点击锚点
const offsetTop = this.getOffsetTop(锚点元素)
this.isClickAnchor = true
this.scrollBox.animate({ scrollTop: offsetTop }, () =>{
// 即使同一个位置不会触发滚动,回调也会执行,**另animate过程中主动滚动页面会失效
console.log('锚点结束')
this.isClickAnchor = false
})
- 滚动页面自动高亮对应锚点
autoLightAnchor() {
if (this.offsetTopList.length < 2) { // 锚点数量
return
}
const scrollTop = this.scrollBox.scrollTop()
for (let i = 0;i < this.offsetTopList.length;i++) {
if (scrollTop < this.offsetTopList[i]) {
let activeIndex = i
if (i > 0) {
activeIndex = i - 1
}
this.$refs.anchor.activeIndex = activeIndex
break
} else if (i == this.offsetTopList.length - 1) {
// * 最后一个
this.$refs.anchor.activeIndex = i
break
}
}
},
- 监听内容height改变
// const elementResizeDetectorMaker = require('element-resize-detector')
this.erd = elementResizeDetectorMaker()
// 绑定监听内容dom
this.erd.listenTo(this.$refs.scrollBoxContent, () => {
this.$nextTick(() => {
this.setOffsetTopList() // 重新计算锚点的offsetTop
})
})
// 解绑
this.erd.uninstall(this.$refs.scrollBoxContent)
js实现 (2018-10-11更新)
交互重点:
点击锚点,页面滑动到对应的位置,且锚点高亮
页面滑动时,滑动到指定位置时,对应的锚点高亮显示
锚点对应的内容块高度任意指定
特殊处理页面滑动到底部,确保最后一个锚点高亮显示
特殊需求,当某内容块为空,该内容块及对应的锚点均隐藏显示
效果图
页面结构
<div id="rollWrap">
<div class="content">
<h1>
<a href="#section1" class="cur-anchor">内容1</a>
<a href="#section2">内容2</a>
<a href="#no">内容空</a>
<a href="#section3">内容3</a>
</h1>
<div class="other"></div>
<div id="section1" class="section">内容1</div>
<div id="section2" class="section">内容2</div>
<div id="no">空</div>
<div id="section3" class="section">内容3</div>
<footer>
底部
</footer>
</div>
</div>
js源码
var clickflag = false; //标识是否点击锚点触发页面滚动
var offsetHs = [0]; //页面滚动,对应锚点高亮范围值
var navs = '';
init();
/*事件绑定*/
$('#rollWrap a').click(function(){
clickflag = true;
$(this).addClass('cur-anchor').siblings().removeClass('cur-anchor');
$('#rollWrap').animate({
scrollTop: $($(this).attr('href')).offset().top + $('#rollWrap').scrollTop()
},function(){
clickflag = false;
});
return false; //地址栏不显示标识符 #xx
});
$('#rollWrap').on('scroll',function(){
isLightAnchor($('#rollWrap').scrollTop())
})
/*初始化,当内容块的内容为空,隐藏对应的内容块及锚点*/
function init(){
//一系列操作,得知空内容块为 #no
$('#no').remove();
$('a[href=#no]').remove();
navs = $('#rollWrap a');
handleOffsetValue();
}
/*页面滚动,对应的锚点(动态锚点)高亮,偏移值计算*/
function handleOffsetValue(){
for(var i = 0;i < navs.length;i++){
var divID = $(navs[i]).attr('href');
//计算偏移量,必须一进入页面调用,否则$(divID).offset().top 为偏移之后的动态变化值
offsetHs.push($(divID).offset().top+$(divID).get(0).offsetHeight*0.8);
}
}
/*页面scroll,计算是否需要高亮锚点*/
function isLightAnchor(scrollTop){
for(var i = 0;i < navs.length; i ++){
if(scrollTop>=offsetHs[i]&&scrollTop<offsetHs[i+1]){
if (!clickflag) {
$($('#rollWrap a')[i]).addClass('cur-anchor').siblings().removeClass('cur-anchor');
}
}
}
//设置滚动到底部,确保最后一个锚点高亮
if(scrollTop==$('.content').get(0).offsetHeight-$('#rollWrap').height()){
$($('#rollWrap a')[navs.length-1]).addClass('cur-anchor').siblings().removeClass('cur-anchor');
}
}
说明:
- 滚动容器里的元素,其offset().top是相对于容器顶部而言的,当页面发生滚动,该元素对应的offset().top值是动态改变的,可正可负;
- 设置某元素置于容器顶部,可设置容器的scrollTop = 当前的scrollTop+该元素的offset().top
css代码
html,body{
height:100%;
margin:0;
padding:0;
}
div{
margin: 0;
padding: 0;
}
#rollWrap{
height:100%;
overflow-y: scroll;
}
.other{
height:200px;
background-image: url(./bg.jpg);
}
.section{
color:black;
font-size: 2em;
text-align: center;
background-size: 100% 100%;
background-repeat: no-repeat;
}
#section1{
height:500px;
line-height: 500px;
background-image: url(./bg1.jpg);
}
#section2{
height:700px;
line-height: 700px;
background-image: url(./bg2.jpg);
}
#section3{
height:200px;
line-height: 200px;
background-image: url(./bg3.jpg);
}
h1{
right: 20px;
top:0;
position: fixed;
}
h1 a{
color:#333;
font-size: 16px;
display: inline-block;
padding: 5px;
border: 1px solid #ccc;
}
.cur-anchor{
background-color: #ccc;
}
footer{
height:100px;
}