前言:在写项目时,偶尔便会遇到需要滚动的情况,比如写大屏时,信息滚动展示等。我记性不太好,时间赖好一长,便会记不清
offset
、client
、scroll
和scrollIntoView
、scrollTo
、scrollBy
这之间的区别,那么趁着这次又碰见滚动展示的需求,整理一下,也方便自己遗忘时查阅
1、offset、client、scroll之间的区别
这个就直接上图了,文字描述再多,也不如直接画图说明来的直接
图片来源:offsetHeight,clientHeight,scrollHeight,innerHeight,outerHeight等属性的解释;
offsetTop、clientHeight、scrollTop等属性的含义
1.1、offset
1.2、client
1.3、scroll
2、简单布局一块滚动的区域,用于说明测试
<template>
<div id="content">
<button @click="onScroll" style="margin-top: 20px;">每次点击,滚动四行距离</button>
<div id="scrollContainer" class="out">
<div v-for="items in data" :key="items"
:id="items+'data'" class="data"
>{{ items }}</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
data: [],
num: 0,
timer: null,
}
},
created() {
for(let i = 0; i < 200; i++) {
this.data.push(i+1);
};
},
methods: {
onScroll() {
// 每点击一次,向下滚动4行的距离
},
autoScroll() {
// 自动滚动展示
}
},
};
</script>
<style scoped>
.out {
display: flex;
flex-wrap: wrap;
width: 400px;
height: 400px;
margin: 20px auto 0;
background: #eee;
overflow: auto;
}
.data {
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
width: 20%;
height: 100px;
border: 1px solid #333;
}
</style>
页面显示如图:
![]()
3、scrollIntoView、scrollTo、scrollBy之间的区别
首先scrollIntoView和scrollTo、scrollBy的调用对象是不一样的
scrollIntoView是滚动容器中的元素去调用:childrenElement.scrollIntoView(参数可选)
scrollTo、scrollBy是滚动容器去调用:parentElement.scrollTo/scrollBy(参数可选)
其次,scrollTo、scrollBy都是滚动到指定位置,但是scrollTo是绝对位置,scrollBy是相对位置,这个光靠文字不好说明,需要结合动图演示,将在下文和参数一起进行具体说明、演示
参考文章:Element.scrollIntoView();scrollIntoView()方法;JS原生滚动到指定元素
3.1、scrollIntoView
childrenElement.scrollIntoView()
,将调用它的元素,滚动到可滚动的父元素的可见区域。有两种传参,可以传Boolean
或者Object
,其中Object
包含三个参数:behavior、block、inline
。
3.1.1、scrollIntoView可传参数说明
参数为
Object
:
参数说明 | 可选值 | |
---|---|---|
behavior | 表示滚动的方式,立即的还是平滑的 | auto(默认)/instant/smooth(常用) |
block | 垂直方向的对齐方式 | start(默认)/end/center/nearest |
inline | 水平方向的对齐方式 | start/end/center/nearest(默认) |
参数为
Boolean
:
参数说明 | 对应Object参数 | |
---|---|---|
true | 元素顶端将和其所在滚动区的可视区域顶端对齐 | block:"start",其余默认值 |
false | 元素底端将和其所在滚动区的可视区域底端对齐 | block: "end",其余默认值 |
3.1.2、scrollIntoView滚动效果
Boolean:true/false;Object:block:start/end,滚动效果
onScroll() {
this.num += 21; // 一行5个,要每次向下移动四行,跨越20个数字,+21(为方便,dom id为"数字+data")
// 获取到滚动容器中的子元素,使调用scrollIntoView的子元素可见
const ele = document.getElementById(this.num+"data");
ele.scrollIntoView(true/false);
// ele.scrollIntoView({ block: "start"/"end" });
}
下图,第一个图为true(可省略)/block:start,第二个图为false/block:end
Object:behavior,立即/平滑滚动效果
onScroll() {
this.num += 21; // 一行5个,要每次向下移动四行,跨越20个数字,+21(为方便,dom id为"数字+data")
// 获取到滚动容器中的子元素,使调用scrollIntoView的子元素可见
const ele = document.getElementById(this.num+"data");
ele.scrollIntoView({
behavior: "smooth", // smooth,平滑滚动到可见区域
block: "start"/"end", // 元素的底端将和其所在滚动区的可视区域的底端对齐,相当于 scrollIntoView(false)
});
}
下图,第一个图为立即滚动,第二个图为平滑滚动
我个人常用的这些,其他效果自行尝试
3.2、scrollTo
parentElement.scrollTo()
可滚动容器,滚动至绝对指定位置,就是只要位置确定了,不论怎么滑动、从哪开始滑动,都只会滑到这个固定位置。有两种传参,一种直接传(x, y)
坐标,一种是传Object,包含三个参数:top、left、behavior
3.2.1、scrollTo可传参数说明
参数为
Object
参数说明 | 可选参数 | |
---|---|---|
behavior | 表示滚动的方式,立即的还是平滑的 | auto(默认)/instant/smooth(常用) |
top | 滚动元素Y轴方向滚动的像素数 | 最小0,最大滚动容器的clientHeight |
left | 滚动元素X轴方向滚动的像素数 | 最小0,最大滚动容器的clientWidth |
参数为
(x, y)
参数说明 | 对应的Object参数 | |
---|---|---|
x | 滚动元素X轴方向滚动的像素数 | object的left,最小0,最大滚动容器的clientWidth |
y | 滚动元素Y轴方向滚动的像素数 | object的top,最小0,最大滚动容器的clientHeight |
在
scrollTo()
中,通常使滚动容器滚到最上/左部,将(x, y)
或者Object
的top、left
都设置成0即可,即使给上负值,浏览器也会再设置成0
,所以最小就是0
;通常判断是否滚到最底部了,是用“scrollHeight - scrollTop === clientHeight
”去判断的(原因查看上方的offset、client、scroll
区别),所以能滚动的最大值就是clientHeight/clientWidth
,即使给上无穷大,浏览器也会再设置成这个最大值
3.2.2、scrollTo滚动效果
this.num += 4; // 每次点击,滚动四行距离
const ele = document.getElementById("scrollContainer");
ele.scrollTo({
top: 400 * 0.25 * this.num, // 400,滚动容器高度;0.25是每行高度(400×25%);
behavior: "smooth"
});
与上文的
ele.scrollIntoView({behavior: "smooth", block: "start"})
效果一致
其他效果可以自行尝试
3.3、scrollBy
parentElement.scrollBy()
可滚动容器,滚动至相对现在位置的指定位置,即从现在的位置在X/Y轴上滚动指定像素的距离。参数与scrollTo
一致,只是scrollBy
参数可以为负值,正值
是向下/右
移动,直到最下/右
部,负值
是向上/左
移动,直到最上/左
部
3.4、scrollTo和scrollBy滚动效果对比
const ele = document.getElementById("scrollContainer");
ele.scrollTo({
top: 90,
behavior: "smooth"
});
const ele = document.getElementById("scrollContainer");
ele.scrollby({
top: 90,
behavior: "smooth"
});
点击按钮,向下滚动90像素距离,以此比较
下图中,第一个图为scrollTo
的滚动效果,第二个图为scrollby
的滚动效果
![]()
可以发现,top
固定时,scrollTo
只会滚动到这个固定位置,不论怎么点击,都不会有变化;而scrollby
每按一次就会滚动一个相对当前位置的固定距离。这就是上面所我表达的“绝对位置
”和“相对位置
”
4、自动滚动
在写项目时,尤其是大屏,经常会碰见,让某些东西自动循环滚动,去展示信息。下面就简单说一下自动循环滚动的方法,包括无缝循环滚动
4.1、使用scrollTop实现自动滚动
autoScroll() {
const ele = document.getElementById("scrollContainer");
this.timer = setInterval(() => {
// 每20毫秒向下移动10个像素,速度设置较快,主要是快速滚动下方,方便演示
ele.scrollTop += 10;
if(ele.scrollHeight - ele.scrollTop === ele.clientHeight) { // 判断是否滚到最底部
clearInterval(this.timer);
this.timer = null;
setTimeout(() => {
ele.scrollTop = 0; // 最底部停留2秒回到最开始
this.autoScroll(); // 然后重新开始滚动
}, 2000)
};
}, 20);
}
4.2、自动无缝循环滚动
无缝循环,基本逻辑是:
1:复制一份一模一样的出来放到后面;
2:当第一份正文内容已经全部滚动显示出来后,再继续滚动就是显示的第二份复制的内容,既然是复制的,那必然内容与正文一模一样,因此也就看不出来是新的内容在滚动;
3:当第一份的正文内容完全滚动完毕后,即第一份的正文内容已经全部滚出可见区域;
4:再将scrollTop
设置为0
,重头开始滚动,从而实现无缝循环。下图示意:
在
2、简单布局一块滚动的区域,用于说明测试
中的代码,做如下修改:
// template中修改,“复制一份”一模一样的内容,放到后面
<div id="content">
<div id="scrollContainer" class="out">
<div id="scroll1">
<div v-for="items in data" :key="items+Math.random()" class="data">
{{ items }}
</div>
</div>
<div id="scroll2">
<div v-for="items in data" :key="items+Math.random()" class="data">
{{ items }}
</div>
</div>
</div>
</div>
// 样式修改如下,删掉out的felx布局,添加scroll1、scroll2
.out {
width: 400px;
height: 400px;
background: #eee;
overflow: auto;
margin: 20px auto 0;
border: 1px solid #333;
}
#scroll1,
#scroll2 {
width: 100%;
display: flex;
flex-wrap: wrap;
}
// 为了方便测试,能快速滚动到底,循环添加的数字由200个改为了50个
for(let i=0;i<50;i++) {
this.data.push(i+1)
}
自动循环滚动:
autoScroll() {
const ele = document.getElementById("scrollContainer");
setInterval(() => {
ele.scrollTop += 2
// 判断第一份内容是否完全滚动完毕。如果一份内容的scrollHeight是1,那两份张自然是2
// 所以当scrollHeight×0.5<=scrollTop时,即第一份内容完全滚动完毕,即全部滚出可视区域
// 不用==判断是因为,滚动速度不同,两者不一定能正好相等
if(ele.scrollHeight * 0.5 <= ele.scrollTop) {
// ele.scrollTop = 0; 如果可以让scrollHeight×0.5和crollTop相等,可直接设置成0
ele.scrollTop = ele.scrollTop - ele.scrollHeight * 0.5;
}
}, 20);
},
效果如图,注意观察滚动条变化:
4.2.1、隐藏滚动条
为了视觉效果更好,可以使用
::-webkit-scrollbar{ display:none; }
隐藏滚动条
.out::-webkit-scrollbar{
display:none;/*隐藏滚动条*/
}
// 兼容火狐浏览器和ie
.out{
scrollbar-width: none; // 兼容火狐浏览器
-ms-overflow-style: none; // 兼容ie
}
5、扩展:requestAnimationFrame请求动画帧
参考文章:一文详解requestAnimationFrame请求动画帧
requestAnimationFrame
的用法与setTimeout
、setInterval
很相似,只是不需要设置时间间隔而已。requestAnimationFrame
使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。它返回一个整数,表示定时器的编号,这个值可以传递给cancelAnimationFrame
用于取消这个函数的执行。
autoScroll() {
const ele = document.getElementById("scrollContainer");
const scrollFun = () => {
ele.scrollTop += 2
if(ele.scrollHeight * 0.5 <= ele.scrollTop) {
ele.scrollTop = 0;
}
requestAnimationFrame(scrollFun)
}
requestAnimationFrame(scrollFun)
},