JavaScript 實(shí)現(xiàn)頁(yè)面滾動(dòng)動(dòng)畫
在做前端 UI 效果時(shí),讓元素根據(jù)滾動(dòng)位置實(shí)現(xiàn)動(dòng)畫效果是一個(gè)非常流行的設(shè)計(jì),通常我們會(huì)使用第三方插件或庫(kù)來(lái)實(shí)現(xiàn)。在本教程中,我將教大家使用純 JavaScript 和 CSS 來(lái)實(shí)現(xiàn)。
先預(yù)覽一下實(shí)現(xiàn)的效果:
我們使用 CSS 來(lái)實(shí)現(xiàn)動(dòng)畫,用 JavaScript 來(lái)處理觸發(fā)所需的樣式。我們先來(lái)創(chuàng)建布局。
創(chuàng)建布局
我們先使用 HTML 創(chuàng)建頁(yè)面布局,然后為需要實(shí)現(xiàn)動(dòng)畫的元素分配一個(gè)通用類名,后面的 JavaScript 通過(guò)此類名定位這些元素。這里我們給需要根據(jù)滾動(dòng)實(shí)現(xiàn)動(dòng)畫的元素指定為類名 js-scroll,HTML 代碼如下:
<section class="scroll-container"> <div class="scroll-element js-scroll"></div> <div class="scroll-caption">This animation fades in from the top.</div> </section>
添加 CSS 樣式
先來(lái)一個(gè)簡(jiǎn)單的淡入動(dòng)畫效果:
.js-scroll { opacity: 0; transition: opacity 500ms; } .js-scroll.scrolled { opacity: 1; }
頁(yè)面上的所有 js-scroll 元素都會(huì)被隱藏,不透明度為 0。當(dāng)滾動(dòng)到該元素區(qū)域時(shí),給它加上 .scrolled 類名讓它顯現(xiàn)出來(lái)。
用 JavaScript 操作元素
有了布局和樣式,現(xiàn)在我們需要編寫一個(gè) JavaScript 函數(shù),當(dāng)元素滾動(dòng)到視圖中時(shí),為它們分配類名。
我們來(lái)簡(jiǎn)單分解一下邏輯:
- 獲取頁(yè)面上所有 js-scroll 元素
- 使這些元素默認(rèn)淡出不可見(jiàn)
- 檢測(cè)元素是否在視窗內(nèi)
- 如果元素在視窗內(nèi)則分配 scrolled 類名
獲取目標(biāo)元素
獲取頁(yè)面上所有 js-scroll 元素,使用 document.querySelectorAll() 即可:
const scrollElements = document.querySelectorAll('.js-scroll')
默認(rèn)淡出所有目標(biāo)元素
遍歷這些元素,使其全部淡出不可見(jiàn):
scrollElements.forEach((el) => { el.style.opacity = 0 })
檢測(cè)元素是否在視窗內(nèi)
我們可以通過(guò)判斷元素距離頁(yè)面頂部的間距是否小于頁(yè)面可見(jiàn)部分的高度,來(lái)檢測(cè)元素是否在用戶視窗中。
在 JavaScript 中,我們使用 getBoundingClientRect().top
方法來(lái)獲取元素與頁(yè)面頂部的距離,使用 window.innerHeight
或 document.documentElement.clientHeight
來(lái)獲取視窗的高度。
我們將使用上述邏輯創(chuàng)建一個(gè) elementInView
函數(shù):
const elementInView = (el) => { const elementTop = el.getBoundingClientRect().top return ( elementTop <= (window.innerHeight || document.documentElement.clientHeight) ) }
我們可以修改這個(gè)函數(shù)來(lái)檢測(cè)元素是否向頁(yè)面滾動(dòng)了 x 個(gè)像素,或者檢測(cè)頁(yè)面滾動(dòng)的百分比。
const elementInView = (el, scrollOffset = 0) => { const elementTop = el.getBoundingClientRect().top return ( elementTop <= (window.innerHeight || document.documentElement.clientHeight) - scrollOffset ) }
在這種情況下,如果元素已經(jīng)按 scrollOffset 的數(shù)量滾動(dòng)到頁(yè)面中,該函數(shù)返回 true。我們?cè)偕宰餍薷?,把參?shù) scrollOffset 變成百分比:
const elementInView = (el, percentageScroll = 100) => { const elementTop = el.getBoundingClientRect().top return ( elementTop <= (window.innerHeight || document.documentElement.clientHeight) * (percentageScroll / 100) ) }
這部分可以根據(jù)自己的特定需求來(lái)定義邏輯。
注意:可以使用 Intersection Observer API[2] 來(lái)實(shí)現(xiàn)同樣的效果,但它不支持 IE。
給元素添加類名
現(xiàn)在我們已經(jīng)能夠檢測(cè)到元素是否已經(jīng)滾動(dòng)到頁(yè)面中,我們需要定義一個(gè)函數(shù)來(lái)處理該元素的顯示--本例中我們通過(guò)分配 scrolled 類名來(lái)顯示該元素。
const displayScrollElement = (element) => { element.classList.add('scrolled') }
然后,再把我們前面的邏輯與 displayScrollElement 函數(shù)結(jié)合起來(lái),并使用 forEach 方法在所有 js-scroll 元素上調(diào)用該函數(shù)。
const handleScrollAnimation = () => { scrollElements.forEach((el) => { if (elementInView(el, 100)) { displayScrollElement(el) } }) }
另外,當(dāng)元素不再在視圖中時(shí),需要將其重置為默認(rèn)狀態(tài),我們可以通過(guò)定義一個(gè) hideScrollElement 來(lái)實(shí)現(xiàn):
const hideScrollElement = (element) => { element.classList.remove("scrolled"); }; const handleScrollAnimation = () => { scrollElements.forEach((el) => { if (elementInView(el, 100)) { displayScrollElement(el); } else { hideScrollElement(el); } }
最后,我們將把上面的方法傳遞到窗口的滾動(dòng)事件監(jiān)聽(tīng)中,這樣每當(dāng)用戶滾動(dòng)時(shí)它就會(huì)運(yùn)行。
window.addEventListener('scroll', () => { handleScrollAnimation() })
我們已經(jīng)實(shí)現(xiàn)了滾動(dòng)動(dòng)畫的所有功能。
完善示例
請(qǐng)大家回到文章開(kāi)頭看看效果圖??吹?,這些元素以不同的動(dòng)畫出現(xiàn)。這是通過(guò)給類名分配不同的 CSS 動(dòng)畫來(lái)實(shí)現(xiàn)的。這個(gè)示例的 HTML 是這樣的:
<section class="scroll-container"> <div class="scroll-element js-scroll fade-in"></div> <div class="scroll-caption">淡入動(dòng)效</div> </section> <section class="scroll-container"> <div class="scroll-element js-scroll fade-in-bottom"></div> <div class="scroll-caption">切入頂部動(dòng)效</div> </section> <section class="scroll-container"> <div class="scroll-element js-scroll slide-left"></div> <div class="scroll-caption">從左邊切入動(dòng)效</div> </section> <section class="scroll-container"> <div class="scroll-element js-scroll slide-right"></div> <div class="scroll-caption">從右邊切入動(dòng)效</div> </section>
這里我們給不同動(dòng)效的元素分配了不同的 CSS 類名,下面是這些類對(duì)應(yīng)的 CSS 代碼:
.scrolled.fade-in { animation: fade-in 1s ease-in-out both; } .scrolled.fade-in-bottom { animation: fade-in-bottom 1s ease-in-out both; } .scrolled.slide-left { animation: slide-in-left 1s ease-in-out both; } .scrolled.slide-right { animation: slide-in-right 1s ease-in-out both; } @keyframes slide-in-left { 0% { transform: translateX(-100px); opacity: 0; } 100% { transform: translateX(0); opacity: 1; } } @keyframes slide-in-right { 0% { transform: translateX(100px); opacity: 0; } 100% { transform: translateX(0); opacity: 1; } } @keyframes fade-in-bottom { 0% { transform: translateY(50px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } } @keyframes fade-in { 0% { opacity: 0; } 100% { opacity: 1; } }
雖然加了不同動(dòng)畫元素,但我們不需要修改 JavaScript 代碼,因?yàn)檫壿嫳3植蛔儭_@意味著我們可以在頁(yè)面添加任何數(shù)量的不同動(dòng)畫,而無(wú)需編寫新的函數(shù)。
利用節(jié)流閥提高性能
每當(dāng)我們?cè)跐L動(dòng)監(jiān)聽(tīng)器中綁定一個(gè)函數(shù)時(shí),每次用戶滾動(dòng)頁(yè)面,該函數(shù)都會(huì)被調(diào)用。滾動(dòng)一個(gè) 500px 的頁(yè)面會(huì)導(dǎo)致一個(gè)函數(shù)被調(diào)用至少 50 次。如果我們?cè)噲D在頁(yè)面上包含很多元素,這會(huì)導(dǎo)致我們的頁(yè)面速度明顯變慢。
我們可以通過(guò)使用“節(jié)流函數(shù)(Throttle Function)”來(lái)減少函數(shù)的調(diào)用次數(shù)。節(jié)流函數(shù)是一個(gè)高階函數(shù),它在指定的時(shí)間間隔內(nèi)只調(diào)用傳入的函數(shù)一次。
它對(duì)于滾動(dòng)事件特別有用,因?yàn)槲覀儾恍枰獧z測(cè)用戶滾動(dòng)的每個(gè)像素。例如,如果我們有一個(gè)定時(shí)器為 100ms 的節(jié)流函數(shù),那么用戶每滾動(dòng) 100ms,該函數(shù)將只被調(diào)用一次。
節(jié)流函數(shù)在 JavaScript 中可以這樣實(shí)現(xiàn):
let throttleTimer = false const throttle = (callback, time) => { if (throttleTimer) return // 這里標(biāo)記一下,以使函數(shù)不會(huì)重復(fù)執(zhí)行 throttleTimer = true setTimeout(() => { // 到了指定的時(shí)間,調(diào)用傳入的回調(diào)函數(shù) callback() throttleTimer = false }, time) }
然后我們可以修改 window 對(duì)象上的 scroll 事件監(jiān)聽(tīng):
window.addEventListener('scroll', () => { throttle(handleScrollAnimation, 250) })
現(xiàn)在我們的 handleScrollAnimation 函數(shù)在用戶滾動(dòng)時(shí)每隔 250ms 就會(huì)被調(diào)用一次:
以上就是JavaScript 實(shí)現(xiàn)頁(yè)面滾動(dòng)動(dòng)畫的詳細(xì)內(nèi)容,更多關(guān)于JavaScript 頁(yè)面滾動(dòng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- JavaScript 獲取滾動(dòng)條位置并將頁(yè)面滑動(dòng)到錨點(diǎn)
- javascript實(shí)現(xiàn)蒙版與禁止頁(yè)面滾動(dòng)
- vue使用原生js實(shí)現(xiàn)滾動(dòng)頁(yè)面跟蹤導(dǎo)航高亮的示例代碼
- js監(jiān)聽(tīng)html頁(yè)面的上下滾動(dòng)事件方法
- 通過(guò) JS 判斷頁(yè)面是否有滾動(dòng)條的實(shí)現(xiàn)方法
- Javascript實(shí)現(xiàn)頁(yè)面滾動(dòng)時(shí)導(dǎo)航智能定位
- js/jquery控制頁(yè)面動(dòng)態(tài)加載數(shù)據(jù) 滑動(dòng)滾動(dòng)條自動(dòng)加載事件的方法
- js實(shí)現(xiàn)刷新頁(yè)面后回到記錄時(shí)滾動(dòng)條的位置【兩種方案可選】
- js實(shí)現(xiàn)頁(yè)面刷新滾動(dòng)條位置不變
相關(guān)文章
js防抖函數(shù)和節(jié)流函數(shù)使用場(chǎng)景和實(shí)現(xiàn)區(qū)別示例分析
這篇文章主要介紹了js防抖函數(shù)和節(jié)流函數(shù)使用場(chǎng)景和實(shí)現(xiàn)區(qū)別,結(jié)合實(shí)例形式詳細(xì)分析了js防抖函數(shù)和節(jié)流函數(shù)基本功能、定義、用法區(qū)別及操作注意事項(xiàng),需要的朋友可以參考下2020-04-04JavaScript基礎(chǔ)之流程控制語(yǔ)句的用法
下面就為大家?guī)?lái)一篇JavaScript基礎(chǔ)之流程控制語(yǔ)句的用法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08javascript在IE下trim函數(shù)無(wú)法使用的解決方法
這篇文章主要介紹了javascript在IE下trim函數(shù)無(wú)法使用的解決方法,分別敘述了javascript以及jQuery下的解決方案,對(duì)于WEB前端javascript設(shè)計(jì)人員進(jìn)行瀏覽器兼容性調(diào)試有不錯(cuò)的借鑒價(jià)值,需要的朋友可以參考下2014-09-09JavaScript實(shí)現(xiàn)復(fù)制文章自動(dòng)添加版權(quán)
自己辛辛苦苦寫的文章,輕易就被別人復(fù)制-粘貼去了,是不是很傷心呢?小編今天給大家整理了兩個(gè)方法,讓別人復(fù)制自己的文章時(shí),自動(dòng)在文章的結(jié)尾添加自己的版權(quán)信息。2016-08-08javascript與CSS復(fù)習(xí)(《精通javascript》)
js和css結(jié)合來(lái)產(chǎn)生醒目的交互效果,我們可以快速的訪問(wèn)元素自身的樣式屬性2010-06-06p5.js實(shí)現(xiàn)故宮橘貓賞秋圖動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了p5.js實(shí)現(xiàn)故宮橘貓賞秋圖動(dòng)畫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10layer 刷新某個(gè)頁(yè)面的實(shí)現(xiàn)方法
今天小編就為大家分享一篇layer 刷新某個(gè)頁(yè)面的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09Bootstrap Tooltip顯示換行和左對(duì)齊的解決方案
小編在使用Bootstrap的Tooltip功能時(shí)遇到一些小問(wèn)題,換行丟失,文字不是左對(duì)齊。下面小編給大家介紹下Bootstrap Tooltip顯示換行和左對(duì)齊的解決方案,感興趣的朋友一起看看吧2017-10-10