參考
參考給出的是一個字幕自動滾動的例子,不過這個例子有個坑,就是#rule必須設(shè)置高度,否則的話,看不到滾動的效果。代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>列表之無限滾動</title>
<style>
#rule{
max-height: 400px;
overflow-y: auto;
}
</style>
</head>
<body>
<div id="rule">
<div class="list" id="list">
<p>用戶185****0000 獲得XXX優(yōu)惠券</p>
<p>用戶185****0000 獲得XXX優(yōu)惠券</p>
<p>用戶185****0000 獲得XXX優(yōu)惠券</p>
<p>用戶185****0000 獲得XXX優(yōu)惠券</p>
<p>用戶185****0000 獲得XXX優(yōu)惠券</p>
<p>用戶185****0000 獲得XXX優(yōu)惠券</p>
<p>用戶185****0000 獲得XXX優(yōu)惠券</p>
<p>用戶185****0000 獲得XXX優(yōu)惠券</p>
<p>用戶185****0000 獲得XXX優(yōu)惠券</p>
<p>用戶185****0000 獲得XXX優(yōu)惠券</p>
<p>用戶185****0000 獲得XXX優(yōu)惠券</p>
<p>用戶185****0000 獲得XXX優(yōu)惠券</p>
</div>
<div class="list2" id="list2"></div>
</div>
<script>
(function(){
var speed = 50;
var list = document.getElementById('list');
var list2 = document.getElementById('list2');
var rule = document.getElementById('rule');
list2.innerHTML = list.innerHTML;
function Marquee() {
if (list2.offsetTop - rule.scrollTop <= 0)
rule.scrollTop -= list.offsetHeight;
else {
rule.scrollTop++;
}
}
var MyMar = setInterval(Marquee, speed);
rule.addEventListener('mouseover',function () {
clearInterval(MyMar)
});
rule.addEventListener ('mouseout',function () {
MyMar = setInterval(Marquee, speed)
});
})();
</script>
</body>
</html>
作為一個例子,這個已經(jīng)是非常棒的效果了,但是卻不是想要的效果,我想要的效果是:下滑到底部時自動加載下一頁,上滑時自動加載到上一頁。
可能有人會問,這個還需要實現(xiàn)嗎?答案:是的。
我們現(xiàn)在講的其實是性能優(yōu)化的問題,這里要用到元素重用的方法,并不是一次性將所有數(shù)據(jù)加載到頁面。而是通過兩個list動態(tài)地切換,從而實現(xiàn)無限滾動的效果。當(dāng)然,這里的‘無限滾動’并不是一直可以滾動下去,在實際應(yīng)用中,第一頁將不能再上滑,最后一頁將不能再下滑。
技術(shù)點(diǎn)
- 監(jiān)聽滑動事件
rule.addEventListener('scroll',function(){
...
})
- 判斷是否滑動到了底部或者頂部
//判斷是否滑動到了底部
var top = rule.scrollTop + rule.offsetHeight + rule.offsetTop;
if (list2.offsetTop + list2.offsetHeight <= top){
...
}
...
//判斷是否滑動到了頂部
else if(rule.scrollTop <= 10){
...
}
- 滑動的方向
var recordTop = rule.scrollTop;
rule.addEventListener('scroll',function(){
if(rule.scrollTop - recordTop > 0){//向下滾動
...
}else if(rule.scrollTop - recordTop < 0){//向上滾動
...
}
...
recordTop = rule.scrollTop;
})
- 代碼替換
在list于list2交替時,兩部分代碼需要做一下替換
var html = list.innerHTML;
list.innerHTML = list1.innerHTML;
list1.innerHTML = html;
- 定義狀態(tài) loadingUp,doneUp,loadingDown,doneDown,STATE_undo
const
STATE_loadingUp='loadingUp', //正在加載上一頁數(shù)據(jù)
STATE_doneUp='doneUp',//完成上一頁數(shù)據(jù)的加載
STATE_loadingDown='loadingDown',//正在加載下一頁數(shù)據(jù)
STATE_doneDown='doneDown',//完成下一頁數(shù)據(jù)的加載
STATE_undo='';//默認(rèn)狀態(tài)
由于數(shù)據(jù)需要在滑動到底部之前加載,所以需要定義狀態(tài),以免數(shù)據(jù)被重復(fù)加載。
- 分頁
分頁的話,這里用page、pageSize和total來實現(xiàn)
完整案例
如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>列表之無限滾動</title>
<style>
#rule{
max-height: 400px;
overflow-y: auto;
padding: 0px;
margin: 0px;
}
#list{
/* background-color: gray; */
padding: 0px;
margin: 0px;
}
#list2{
/* background-color:lightgray; */
padding: 0px;
margin: 0px;
}
#vv, #gg{
width: 100%;
}
.line{
padding: 10px;
}
</style>
</head>
<body>
<input type="text" id="vv" readonly />
<input type="text" id="gg" readonly />
<div id="rule">
<div class="list" id="list">
</div>
<div class="list2" id="list2"></div>
</div>
<script>
(function(){
var speed = 50;
var list = document.getElementById('list');
var list2 = document.getElementById('list2');
var rule = document.getElementById('rule');
var con = document.getElementById('vv');
var con2 = document.getElementById('gg');
const
STATE_loadingUp='loadingUp',
STATE_doneUp='doneUp',
STATE_loadingDown='loadingDown',
STATE_doneDown='doneDown',
STATE_undo='';
var pageData = {
page:0,
pageSize:20,
items:[],
total:2,
state:'', //狀態(tài): loadingUp,doneUp,loadingDown,doneDown
recordTop : rule.scrollTop,
};
function createElements(ele,page){
var flag = ele.innerHTML ? true : false;
ele.innerHTML = '';
if(page >= pageData.total){
pageData.state = STATE_undo;
return;
}
var size = Math.min((page+1)*pageData.pageSize, pageData.items.length) - page*pageData.pageSize;
var num = page * pageData.pageSize;
for(var i = 0;i<size;i++){
ele.innerHTML += '<div class="line"><span>'+pageData.items[num+i]+'</span></div>';
}
ele.setAttribute('page', page);
if(!flag){
pageData.state = STATE_undo;
}else if(pageData.state == STATE_loadingDown){
pageData.state = STATE_doneDown;
}else if(pageData.state == STATE_loadingUp){
pageData.state = STATE_doneUp;
}
}
function replaceEle(e1, e2){
var html = e1.innerHTML;
e1.innerHTML = e2.innerHTML;
e2.innerHTML = html;
var page = e1.getAttribute('page');
e1.setAttribute('page',e2.getAttribute('page'));
e2.setAttribute('page',page);
}
//init
var size = 100;
for(var i = 0; i< size;i++){
pageData.items.push((i+1)+". 用戶185****0000 獲得XXX優(yōu)惠券");
}
pageData.total = Math.ceil(size/pageData.pageSize);
// list2.innerHTML = list.innerHTML;
createElements(list,0);
createElements(list2,1);
rule.addEventListener('scroll',function(){
var data = {
list:{
offsetTop:list.offsetTop,
offsetHeight:list.offsetHeight,
},
list2:{
offsetTop:list2.offsetTop,
offsetHeight:list2.offsetHeight,
},
rule:{
scrollTop:rule.scrollTop,
offsetTop:rule.offsetTop,
offsetHeight:rule.offsetHeight,
}
};
con.value = JSON.stringify(data);
var data2 = {
list:list.offsetTop + list.offsetHeight,
list2:list2.offsetTop + list2.offsetHeight,
rule:rule.scrollTop + rule.offsetHeight + rule.offsetTop,
dir:rule.scrollTop - pageData.recordTop,
page:pageData.page,
state:pageData.state
};
con2.value = JSON.stringify(data2);
var top = rule.scrollTop + rule.offsetHeight + rule.offsetTop;
var offset = 200;
// if (list2.offsetTop - rule.scrollTop <= 0 && pageData.page < 0){//向下翻頁
if ((list2.offsetTop + list2.offsetHeight <= top) && pageData.page + 2 < pageData.total && pageData.state == STATE_doneDown){//向下翻頁
rule.scrollTop -= list.offsetHeight;
pageData.page++;
replaceEle(list,list2);
pageData.state = STATE_undo;
}else if(rule.scrollTop <= 10 && pageData.page > 0 && pageData.state == STATE_doneUp){//向上翻頁
rule.scrollTop += list.offsetHeight;
pageData.page--;
replaceEle(list,list2);
pageData.state = STATE_undo;
}
else if(rule.scrollTop - pageData.recordTop > 0 && (list2.offsetTop + list2.offsetHeight <= top + offset)){//向下滾動 加載下一頁數(shù)據(jù)
if(pageData.state == STATE_doneUp || pageData.state == STATE_loadingUp){
pageData.state = STATE_undo;
//TODO 去除掉事件
}
if(pageData.state == STATE_undo){//加載數(shù)據(jù)
pageData.state = STATE_loadingDown;
createElements(list,pageData.page+2)
}
}else if(rule.scrollTop - pageData.recordTop < 0 && rule.scrollTop <= offset){//向上滾動 加載上一頁數(shù)據(jù)
if(pageData.state == STATE_doneDown || pageData.state == STATE_loadingDown){
pageData.state = STATE_undo;
//TODO 去除掉事件
}
if(pageData.page == 0){}
else if(pageData.state == STATE_undo){//加載數(shù)據(jù)
pageData.state = STATE_loadingUp;
if(!list.innerHTML){
createElements(list,pageData.page)
}else{
createElements(list2,pageData.page-1)
}
}
}else if(rule.scrollTop > offset && (list2.offsetTop + list2.offsetHeight > top + offset)){//矯正
var page1 = list.getAttribute('page');
var page2 = list2.getAttribute('page');
if(Math.abs(page1 - page2) != 1 || (page1 != pageData.page+1 && page2 != pageData.page+1)){
console.error(page1+","+page2+","+pageData.page);
createElements(list2,pageData.page+1);
pageData.state = STATE_undo;
}
}
pageData.recordTop = rule.scrollTop;
});
})();
</script>
</body>
</html>
提示
去掉#list和#list2的背景顏色的注釋可以更加清晰的了解源代碼,無限滾動的原理。