Three.js 進(jìn)階之旅:頁面平滑滾動-塞爾達(dá)王國之淚

聲明:本文涉及圖文和模型素材僅用于個人學(xué)習(xí)、研究和欣賞,請勿二次修改、非法傳播、轉(zhuǎn)載、出版、商用、及進(jìn)行其他獲利行為。


摘要
瀏覽網(wǎng)頁時,常被一些基于鼠標(biāo)滾輪控制的頁面動畫所驚艷到,比如 greensock 官網(wǎng)這些?showcase
?案例頁面就非常優(yōu)秀,它們大多數(shù)都是使用?Tween.js
、gasp
?及?greensock
?提供的一些動畫擴(kuò)展庫實(shí)現(xiàn)的。使用?Three.js
?也能很容易實(shí)現(xiàn)絲滑的滾動效果,本文使用?React
?+?Three.js
?+?React Three Fiber
?技術(shù)棧,實(shí)現(xiàn)一個《塞爾達(dá)傳說:王國之淚》主題風(fēng)格基于滾動控制的平滑滾動圖片展示頁面。通過本文的閱讀,你將學(xué)習(xí)到的知識點(diǎn)包括:了解?R3F
?中?useFrame hook
?及?useThree hook
?基本原理及用法;了解?@react-three/drei
?庫的基本組成,學(xué)習(xí)使用它提供的?Preload
、useIntersect
、ScrollControls
、Scroll
、及?Image
?等組件和方法;用?CSS
?生成簡單的循環(huán)懸浮動畫等。
效果
本文案例的實(shí)現(xiàn)效果如下圖所示,當(dāng)頁面每個模塊滾動進(jìn)入視區(qū)時,每個模塊會具有平滑向上移動的視差效果,并且伴隨著由大到小的縮放動畫,當(dāng)鼠標(biāo)懸浮到當(dāng)前模塊時,模塊會產(chǎn)生高亮??
?效果。除此之外,頁面還有一些其他的裝飾,比如塞爾達(dá)風(fēng)格的頁面背景和邊框、具有緩動動畫效果的??ㄖ?/strong>以及同樣具有平滑滾動效果的文字裝飾王國之淚四個字。

頁面的整體布局是這樣的,總共有?7
?頁,即高度為?700vh
,每一頁都具有不同的布局風(fēng)格樣式,滾動時都會具有緩動效果。

打開以下鏈接,在線預(yù)覽效果,本文中的?gif
?造成丟幀和畫質(zhì)損失,大屏訪問效果更佳。
?????
?在線預(yù)覽地址:https://dragonir.github.io/tearsOfTheKingdom/
本專欄系列代碼托管在?Github
?倉庫【threejs-odessey】,后續(xù)所有目錄也都將在此倉庫中更新。
??
?代碼倉庫地址:git@github.com:dragonir/threejs-odessey.git
原理
本文是使用?React Three Fiber
?實(shí)現(xiàn)的,它不僅可以非常容易實(shí)現(xiàn)漂亮的三維圖形,在二維平面頁面開發(fā)中也能大放異彩。在開始實(shí)現(xiàn)本文案例之前,我們先來匯總下本文中需要應(yīng)用到的知識點(diǎn)。掌握這些原理和方法,可以幫助我們迅速構(gòu)建一個交互體驗(yàn)極佳的平滑滾動頁面。
useFrame
此?hook
?允許在頁面每一幀渲染的時候運(yùn)行代碼,比如更新渲染效果、控件等,與?Three.js
?中調(diào)用?requestAnimationFrame
?實(shí)行重繪動畫效果是一樣的。你將接收到狀態(tài)值?state
?和時鐘增量?delta
?;卣{(diào)函數(shù)將在渲染幀之前被調(diào)用,當(dāng)組件卸載時,它會自動從渲染循環(huán)中注銷。
??
?注意,在 useFrame 中不能使用 setState 更新狀態(tài)值!
控制渲染循序
如果你需要更多的控制,你可以傳遞一個數(shù)字渲染優(yōu)先級值。這將導(dǎo)致?React Three Fiber
?完全禁用自動渲染。現(xiàn)在,渲染順序?qū)⒂晌覀冏约嚎刂疲@在后期渲染通道處理以及在多個視圖渲染的場景下非常有用。
??
?回調(diào)將按優(yōu)先級值的升序(最低在前,最高在后)執(zhí)行,類似于?DOM
?的層級順序。
負(fù)索引
使用負(fù)索引無法接管渲染循環(huán)控制,但如果確實(shí)必須對組件樹中的?useFrame
?序列進(jìn)行排序,使用負(fù)索引將很有用。

useThree
此?hook
?允許訪問的狀態(tài)模型包括默認(rèn)渲染器?renderer
?、場景?scene
、相機(jī)?camera
?等。它還提當(dāng)前畫布?canvas
?在屏幕和視區(qū)中的坐標(biāo)位置大小。它是動態(tài)自適應(yīng)的,如果調(diào)整瀏覽器大小,將返回新的測量值,它適用于所有可能更改的狀態(tài)對象。
State 屬性值

選擇屬性
可以通過選擇屬性,避免對僅對關(guān)注的組件進(jìn)行不必要的重新渲染,需要注意的是無法響應(yīng)式地獲得?Three.js
?深層次的動態(tài)屬性。
從組件循環(huán)外部讀取狀態(tài)
交換默認(rèn)值

@react-three/drei
@react-three/drei
?是一個正在不斷擴(kuò)充的,用于?@react-three/fiber
?的由實(shí)用的輔助工具、完整的功能性方法以及現(xiàn)成的抽象構(gòu)成的庫??梢酝ㄟ^如下方法進(jìn)行安裝。下圖列出了當(dāng)前該倉庫中包含的所有組件和方法,本文案例中將通過?@react-three/drei
?的以下幾個組件來實(shí)現(xiàn)平滑滾動效果。
npm install @react-three/drei

Preload
WebGLRenderer
?只有材質(zhì)被觸發(fā)時才會進(jìn)行編譯,這可能會導(dǎo)致卡頓。此組件使用?gl.compile
?預(yù)編譯場景,確保應(yīng)用從一開始就具有響應(yīng)性。默認(rèn)情況下,gl.compile
?只會預(yù)加載可見對象,如果你提供了所有屬性,它們可能會被忽略。
useIntersect
它可以非常方便的檢測三維元素是否可見,當(dāng)對象進(jìn)入視圖或者處于視圖之外時,可以通過它獲得對象的可見性參考值,useIntersect
?依賴于?THREE.Object3D.onBeforeRender
?實(shí)現(xiàn),因此它僅適用于有效渲染的對象,如?mesh
、line
、sprite
等,在?group
?及?object3D
?的骨骼中無法生效。
ScrollControls 和 Scroll
ScrollControls
?可以在?canvas
?前方創(chuàng)建一個?HTML
?滾動容器,你放入滾動組件?<Scroll>
?中的所有元素都將受到影響。你可以使用?useScroll
?鉤子對滾動事件進(jìn)行監(jiān)聽并響應(yīng),它提供很多有用的數(shù)據(jù),例如當(dāng)前的滾動偏移量、增量以及用于范圍查找的函數(shù):range
、curve
?及?visible
。如果需要對滾動偏移做出響應(yīng),如對象進(jìn)入或移除視圖時添加淡入淡出效果等,則后面的方法非常有用。
ScrollControls 的可配置屬性:
可以像下面這樣使用:
Image
是一個自動開啟平鋪效果的基于著色器的圖片組件,圖片填充效果類似于?CSS
?中的?background-size: cover
。
給材質(zhì)增加透明度:

實(shí)現(xiàn)
現(xiàn)在,我們就應(yīng)用上述原理知識,實(shí)現(xiàn)預(yù)覽效果所示的?《塞爾達(dá)傳說:王國之淚》
?主題的平滑滾動頁面。
資源引入
原理篇幅???
?已經(jīng)詳細(xì)講解了本文用到的功能庫和組件,我們在代碼頂部像下面這樣引入它們。
場景初始化
專欄文章《Three.js 進(jìn)階之旅:物理效果-3D乒乓球小游戲》已經(jīng)詳細(xì)介紹過?React Three Fiber
?入門知識。使用?R3F
?初始化三維場景非常簡單,像下面這樣一行代碼就能完成場景初始化。本次實(shí)現(xiàn)不需要精細(xì)的三維效果,因此渲染器的抗鋸齒屬性?antialias
?可以設(shè)置為?false
。

??
?為了可以看見 canvas 畫布區(qū)域,在 css 中設(shè)置了一個漸變背景色。
頁面裝飾
接著,使用?R3F
?實(shí)現(xiàn)平滑的滾動效果之前,我們先來裝飾一下頁面,為了符合?《塞爾達(dá)傳說:王國之類》
?的主題,我在本頁面中添加了游戲主題背景、邊框、以及?希卡之石
?動畫。由于這些內(nèi)容不是本文的重點(diǎn),本文不再贅述,具體實(shí)現(xiàn)可以查看源碼???
?。
首屏頁面
首屏頁面主要有?2
?個元素,一個是背景圖、另一個是?ZELDA?圖片?logo
,當(dāng)頁面加載時背景圖有一個由大到小的縮放效果,鼠標(biāo)懸浮到圖片上時,當(dāng)前鼠標(biāo)所處圖片會變?yōu)楦吡翣顟B(tài)。它們我們可以使用原理部分了解到的?Image
?元素實(shí)現(xiàn)。

圖片組件封裝
頁面其他圖片采用的動畫效果也是類似的,為了可以復(fù)用,我們先對?Image
?元素封裝一下,將由大到小的縮放效果以及鼠標(biāo)懸浮的?hover
?高亮效果添加到每個?Image
?元素中:
然后我們再封裝一個名為?Images
?的?group
?元素,用來統(tǒng)一管理頁面上的所有圖片,分別設(shè)置每個圖片的鏈接、在頁面上的位置、大小、透明度等一些個性化屬性。
圖片組件使用
然后,使用?<ScrollControls>
?和?<Scroll>
?來直接生成滾動頁面,在其中添加上述封裝的?<Image>
?組件,也可以在其中添加一些裝飾性文字?王國之淚,同樣可以進(jìn)行滾動控制,使用?Preload
?進(jìn)行預(yù)加載,提升頁面渲染性能。
此時我們可以看看實(shí)現(xiàn)效果,首先是圖片元素進(jìn)入視區(qū)時由大到小的縮放動畫效果。中間的透明?logo
?圖片似乎有點(diǎn)問題,我們可以像下面這樣修復(fù)???
:
鼠標(biāo)懸浮高亮效果:

頁面平滑滾動:
其他頁面
到這里,其他頁面的實(shí)現(xiàn)就非常簡單了,我們只需按自己的頁面設(shè)計(jì),在?Images
?中像下面這樣排好頁面上所有需要平滑滾動的圖片即可,可以通過?position
、scale
?等屬性設(shè)置個性化調(diào)整圖片在頁面上的位置、大小、加載時機(jī)等,比如本文示例中第?2
?頁有?3
?張圖片、第?3
?頁有?1
?張圖片……
下圖是本文示例所有圖片的頁面布局,總共有?7
?頁,每頁圖片都有不同的排版樣式。

結(jié)束頁面
最后一張頁面,林克
?由小變大平滑滾動進(jìn)入視區(qū),與背景形成視差效果,是通過調(diào)整它的?position.z
?來實(shí)現(xiàn)這一效果的,大家在動手實(shí)踐時可以嘗試設(shè)置不同的值,以達(dá)到自己的預(yù)期效果。

??
?源碼地址:?https://github.com/dragonir/threejs-odessey
總結(jié)
本文中主要包含的知識點(diǎn)包括:
了解?
useFrame hook
?基本原理及使用它控制渲染順序和使用負(fù)索引。了解?
useThree hook
?基本原理、基本屬性值,使用它選擇屬性、從組件循環(huán)外部讀取狀態(tài)、交換默認(rèn)值等。了解?
@react-three/drei
?庫的基本組成,學(xué)習(xí)使用它提供的?Preload
、useIntersect
、ScrollControls
、Scroll
、及?Image
?等組件和方法。用?
CSS
?生成簡單的循環(huán)懸浮動畫。使用上述?
R3F
?知識原理,生成一個具有視差效果的平滑滾動頁面。
想了解其他前端知識或其他未在本文中詳細(xì)描述的?Web 3D?開發(fā)技術(shù)相關(guān)知識,可閱讀我往期的文章。如果有疑問可以在評論中留言,如果覺得文章對你有幫助,不要忘了一鍵三連哦 ??。

附錄
歡迎關(guān)注我的【Three.js 進(jìn)階之旅】系列專欄???
參考
[1].?threejs.org
[2].?React Three Filber
[3].?greensock 官網(wǎng)案例
