前言
在實(shí)際應(yīng)用中,經(jīng)常用到滾動到頁面頂部或某個位置弥咪,一般簡單用錨點(diǎn)處理或用js將document.body.scrollTop設(shè)置為0,結(jié)果是頁面一閃而過滾到指定位置,不是特別友好姐呐。我們想要的效果是要有點(diǎn)緩沖效果。
現(xiàn)代瀏覽器陸續(xù)意識到了這種需求转唉,scrollIntoView意思是滾動到可視皮钠,css中提供了scroll-behavior屬性,js有Element.scrollIntoView()方法赠法。
scroll-behavior
scroll-behavior屬性可取值auto|smooth|inherit|unset
scroll-behavior: smooth;是我們想要的緩沖效果麦轰。在PC瀏覽器中乔夯,頁面默認(rèn)滾動是在<html>標(biāo)簽上,移動端大多數(shù)在<body>標(biāo)簽上款侵,在我們想要實(shí)現(xiàn)平滑“回到頂部”末荐,只需在這兩個標(biāo)簽上都加上:
html,?body?{
??scroll-behavior:?smooth;
}
準(zhǔn)確的說,寫在容器元素上新锈,可以讓容器(非鼠標(biāo)手勢觸發(fā))的滾動變得平滑甲脏,而不局限于<html>,<body>標(biāo)簽妹笆。
利用這個css屬性可以一步將原來純css標(biāo)簽直接切換块请,變成平滑過渡切換效果。
.tab?label?{
??padding:?10px;??
??border:?1px?solid?#ccc;??
??margin-right:?-1px;??
??text-align:?center;??
??float:?left;??
??overflow:?hidden;
}
.tab::after?{
??content:?"";??
??display:?table;??
??clear:?both;
}
.box?{
??height:?200px;??
??border:?1px?solid?#ccc;??
??scroll-behavior:?smooth;??
??overflow:?hidden;??
??margin-top:?10px;
}
.item?{
??height:?100%;??
??position:?relative;??
??overflow:?hidden;
}
.item?input?{
??position:?absolute;??
??top:?0;??height:?100%;??
??width:?1px;??
??border:?0;??
??padding:?0;??
??margin:?0;??
??clip:?rect(0?0?0?0);
}
<h1>純CSS選項(xiàng)卡</h1>
<div?class="tab">
??<label?for="tab1">選項(xiàng)卡1</label>
??<label?for="tab2">選項(xiàng)卡2</label>
??<label?for="tab3">選項(xiàng)卡3</label>
</div>
<div?class="box">
??<div?class="item">
????<input?type="text"?id="tab1">
????<p>選項(xiàng)卡1內(nèi)容</p>
??</div>
??<div?class="item">
????<input?type="text"?id="tab2">
????<p>選項(xiàng)卡2內(nèi)容</p>
??</div>
??<div?class="item">
????<input?type="text"?id="tab3">
????<p>選項(xiàng)卡3內(nèi)容</p>
??</div>
</div>
實(shí)現(xiàn)效果
也可以戳這里
再來看一下這個css屬性scroll-behavior在各大瀏覽器中的支持情況
呃~支持度不是很好拳缠,這樣一行css代碼能應(yīng)用上當(dāng)然是最好的墩新,不行就退化成一閃而過的效果咯。下面再看下js提供的api窟坐。
Element.scrollIntoView()
Element.scrollIntoView()方法讓當(dāng)前的元素滾動到瀏覽器窗口的可視區(qū)域內(nèi)海渊。
element.scrollIntoView(); // 等同于element.scrollIntoView(true)
element.scrollIntoView(alignToTop); // Boolean型參數(shù)
element.scrollIntoView(scrollIntoViewOptions); // Object型參數(shù)
參數(shù)alignToTop
一個Boolean值:
如果為true,元素的頂端將和其所在滾動區(qū)的可視區(qū)域的頂端對齊哲鸳。相應(yīng)的scrollIntoViewOptions: {block: "start", inline: "nearest"}臣疑。這是這個參數(shù)的默認(rèn)值。
如果為false徙菠,元素的底端將和其所在滾動區(qū)的可視區(qū)域的底端對齊讯沈。相應(yīng)的scrollIntoViewOptions: {block: "end", inline: "nearest"}。
參數(shù)scrollIntoViewOptions
一個帶有選項(xiàng)的object:
{?
?behavior:?"auto"??|?"instant"?|?"smooth",??
?block:????"start"?|?"end",
}
behavior可選
定義緩動動畫婿奔, "auto", "instant", 或 "smooth" 之一芙盘。默認(rèn)為 "auto"。
block可選
"start","center","end", 或"nearest"之一脸秽。默認(rèn)為"center"儒老。
inline可選
"start","center","end", 或"nearest"之一。默認(rèn)為"nearest"记餐。
瀏覽器支持
可以看到對于無參數(shù)的情況支持還是很好的驮樊,有參數(shù)的該API在瀏覽器中支持不是很好,我們可以同時結(jié)合CSS設(shè)置scroll-behavior: smooth;滾動效果片酝,在執(zhí)行滾動使用target.scrollIntoView()囚衔,即可達(dá)到“完美滾動”(不太完美)效果。
向下兼容
要達(dá)到所有瀏覽器都有相同(類似)效果雕沿,那就要把剩余不支持scroll-behavior屬性的瀏覽器揪出來练湿,用js去完成使命了。
判斷是否支持scroll-behavior屬性
很簡單审轮,用以下這一行代碼
if(typeof?window.getComputedStyle(document.body).scrollBehavior?===?'undefined')?{
??//?兼容js代碼
}?else?{??
?//?原生滾動api
?//?Element.scrollIntoView()
}
判斷是否支持scroll-behavior屬性肥哎,直接利用原生Element.scrollIntoView()滾動辽俗,否則向下兼容處理。
緩沖算法
緩沖的直觀效果是越來越慢篡诽,直到停止崖飘,也就是在相同時間內(nèi)運(yùn)動的距離越來越短。這樣可以設(shè)置一個定時器杈女,移動到當(dāng)前點(diǎn)到目標(biāo)點(diǎn)距離的緩沖率(比如1/2朱浴,1/3,...)處达椰,比如翰蠢,緩沖率設(shè)為2,當(dāng)前距離目標(biāo)點(diǎn)64px啰劲,下一秒就是32px躏筏,然后16px,8px...呈枉,到達(dá)某個閾值結(jié)束,也就是:
var?position?=?position?+?(destination?-?position)?/?n;
下面來簡單實(shí)現(xiàn)一個點(diǎn)擊右下方的”回到頂部“按鈕埃碱,頁面緩動滾動到頂部的demo猖辫。
<div?class="content">
????<p>很多內(nèi)容。砚殿。啃憎。</p>
????...??
????</div>
??<section?class="back-to-top">
????回到頂部??
???</section>
.content?{
??height:?3000px;??
??border:?1px?solid?#ccc;??
??box-shadow:?0?0?2px?solid;
}
.back-to-top?{
??width:?18px;??
??padding:?10px;??
??border:?1px?solid?#ccc;??
??box-shadow:?0?0?2px?#333;??
??position:?fixed;??
??right:?20px;??
??bottom:?40px;
}
.back-to-top:hover?{
??cursor:?pointer;
}
var?scrollTopSmooth?=?function?(position)?{
??//?不存在原生`requestAnimationFrame`,用`setTimeout`模擬替代
??if?(!window.requestAnimationFrame)?{
??????window.requestAnimationFrame?=?function?(cb)?{
????????return?setTimeout(cb,?17);
????};
??}
??//?當(dāng)前滾動高度
??var?scrollTop?=?document.documentElement.scrollTop?||?document.body.scrollTop;??
??//?step
??var?step?=?function?()?{
????var?distance?=?position?-?scrollTop;
????scrollTop?=?scrollTop?+?distance?/?5;????
????if?(Math.abs(distance)?<?1)?{??????
??????window.scrollTo(0,?position);
????}else?{???
?????window.scrollTo(0,?scrollTop);
?????requestAnimationFrame(step);
????}
??};
??step();
}
$backToTop?=?document.querySelector('.back-to-top')
$backToTop.addEventListener('click',?function?()?{
??scrollTopSmooth(0);
},?false);
</script>
效果圖
或者戳這里
簡單封裝
上面的小demo中似炎,緩沖算法和當(dāng)前滾動業(yè)務(wù)代碼耦合在一起了辛萍,下面單獨(dú)拆解出單獨(dú)一個函數(shù)。
/**
*?緩沖函數(shù)
*?@param?{Number}?position?當(dāng)前滾動位置
*?@param?{Number}?destination?目標(biāo)位置
*?@param?{Number}?rate?緩動率
*?@param?{Function}?callback?緩動結(jié)束回調(diào)函數(shù)?兩個參數(shù)分別是當(dāng)前位置和是否結(jié)束
*/var?easeout?=?function?(position,?destination,?rate,?callback)?{
??if?(position?===?destination?||?typeof?destination?!==?'number')?{?
????return?false;
??}
??destination?=?destination?||?0;
??rate?=?rate?||?2;?
?//?不存在原生`requestAnimationFrame`羡藐,用`setTimeout`模擬替代
??if?(!window.requestAnimationFrame)?{
??????window.requestAnimationFrame?=?function?(fn)?{??????
??????return?setTimeout(fn,?17);
????}
??}??
??var?step?=?function?()?{
????position?=?position?+?(destination?-?position)?/?rate;????
????if?(Math.abs(destination?-?position)?<?1)?{
??????callback(destination,?true);??????
??????return;
????}
????callback(position,?false);
????requestAnimationFrame(step);
??};
??step();
}
拆分后贩毕,這個小緩沖算法就可以被重復(fù)調(diào)用啦,而且仆嗦,適用于滾動到指定位置(不僅僅是到頂部)和緩沖率(控制滾動快慢)辉阶,當(dāng)前小demo調(diào)用:
var?scrollTopSmooth?=?function?(position)?{??
??//?當(dāng)前滾動高度
??var?scrollTop?=?document.documentElement.scrollTop?||?document.body.scrollTop;
??easeout(scrollTop,?position,?5,?function?(val)?{?
????window.scrollTo(0,?val);
??});
}
$backToTop?=?document.querySelector('.back-to-top')
$backToTop.addEventListener('click',?function?()?{
??scrollTopSmooth(200);
},?false);
總結(jié)
綜合來看,簡單實(shí)現(xiàn)一個完美滾動注意以下即可
<html>瘩扼,<body>標(biāo)簽加上scroll-behavior: smooth;屬性谆甜;
判斷當(dāng)前瀏覽器是否支持scrollBehavior屬性;
如果支持直接用原生滾動apiElement.scrollIntoView()集绰;
如果不支持則用js小緩沖算法兼容處理规辱。
完~