航毛玻璃模糊效果實(shí)現(xiàn)與滾動(dòng)狀態(tài)切換實(shí)戰(zhàn))
1. 項(xiàng)目思路與組件選型為什么用HdsNavigation實(shí)現(xiàn)1.1 HdsNavigation是什么先把這個(gè)組件說清楚。HdsNavigation并不是某個(gè)特定框架專屬的導(dǎo)航欄而是很多前端項(xiàng)目里對(duì)“頭部導(dǎo)航”的一種通用抽象封裝常見于以組件庫為主導(dǎo)的中后臺(tái)系統(tǒng)或者移動(dòng)端H5頁面里。它通常負(fù)責(zé)承載標(biāo)題、返回按鈕、菜單入口、操作按鈕并且暴露一些狀態(tài)給外部控制比如滾動(dòng)偏移量、透明度、圖標(biāo)切換等。我在實(shí)際項(xiàng)目里第一次接觸HdsNavigation是做一個(gè)內(nèi)容型的資訊應(yīng)用。頁面結(jié)構(gòu)很簡(jiǎn)單頂部是導(dǎo)航條下面是可滾動(dòng)的文章流。產(chǎn)品經(jīng)理提了個(gè)需求用戶在頁面里向上滑動(dòng)時(shí)頂部導(dǎo)航欄不能只是傻傻停在那里而是要隨著內(nèi)容滑過出現(xiàn)一種“毛玻璃”模糊效果像是把背后文字微微虛化讓導(dǎo)航欄看起來更高級(jí)又不遮擋閱讀。當(dāng)時(shí)第一反應(yīng)是直接重寫一個(gè)自定義頭部但后來發(fā)現(xiàn)既然已經(jīng)有了HdsNavigation的封裝倒不如把它的能力吃透在它的基礎(chǔ)上擴(kuò)展。這也是為什么這個(gè)案例有參考價(jià)值——它不只是一個(gè)CSS效果而是“組件 滾動(dòng)行為 視覺反饋”三件事的聯(lián)動(dòng)。HdsNavigation在上手時(shí)并不復(fù)雜核心就是一個(gè)容器組件它可以掛在頁面最頂部?jī)?nèi)部渲染導(dǎo)航結(jié)構(gòu)。不過它真正的價(jià)值在于預(yù)留了滾動(dòng)狀態(tài)的接口。只要你愿意它就能感知它所處滾動(dòng)的階段并反饋給外部讓開發(fā)者有機(jī)會(huì)去切換樣式。這種設(shè)計(jì)思路能幫我們少寫很多臟邏輯。1.2 頂部模糊效果的實(shí)現(xiàn)原理頂部模糊效果的視覺底層是CSS的backdrop-filter: blur()。這個(gè)屬性會(huì)在元素背后的區(qū)域進(jìn)行模糊處理視覺上好像磨砂玻璃一樣。很多導(dǎo)航欄都利用它把背景做成半透明比如.nav-blur { background-color: rgba(255, 255, 255, 0.7); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); }這里backdrop-filter是核心它讓導(dǎo)航欄后面的內(nèi)容被模糊而不是把導(dǎo)航欄自己的子元素模糊。默認(rèn)情況下導(dǎo)航欄背景是實(shí)色的當(dāng)內(nèi)容滑動(dòng)過來時(shí)背景會(huì)被實(shí)色蓋子完全蓋住沒有層次感。而加了模糊之后背景變成半透明同時(shí)又讓后面的內(nèi)容看不清保證標(biāo)題可讀。聽起來很簡(jiǎn)單但難點(diǎn)在于“什么時(shí)候加模糊”和“什么時(shí)候不加”。如果一開始就給導(dǎo)航欄加上模糊效果頁面處在頂部時(shí)背景可能是空白視覺上會(huì)顯得導(dǎo)航欄和內(nèi)容之間有一條模糊的白帶非常尷尬。常見的處理思路是當(dāng)滾動(dòng)偏移量超過一個(gè)閾值比如20px或60px就切換成模糊背景沒超過閾值就保持透明或純色背景。這種交互已經(jīng)被各大App驗(yàn)證過了比如很多新聞客戶端下拉到頂部時(shí)導(dǎo)航欄干凈一旦往下滑動(dòng)導(dǎo)航欄立刻“霧化”效果非常自然。1.3 方案對(duì)比滾動(dòng)事件監(jiān)聽 vs IntersectionObserver實(shí)現(xiàn)這個(gè)滾動(dòng)狀態(tài)切換主要有兩條路。第一條直接在HdsNavigation所在的頁面注冊(cè)scroll事件讀取window.scrollY或container.scrollTop然后根據(jù)數(shù)值范圍去切換一個(gè)CSS類名。這種方式最直白兼容性也最好幾乎任何前端項(xiàng)目都能用。但缺點(diǎn)是滾動(dòng)事件觸發(fā)頻率極高如果不加節(jié)流或防抖頁面會(huì)頻繁執(zhí)行DOM操作造成性能壓力。第二條使用IntersectionObserver去觀察某個(gè)“哨兵元素”比如一個(gè)放在頁面頂部、高度為1px的透明塊當(dāng)這個(gè)哨兵元素退出可視區(qū)域時(shí)就表示內(nèi)容已經(jīng)開始滑動(dòng)此時(shí)把導(dǎo)航欄切到模糊狀態(tài)。這個(gè)方案的好處是邏輯更直觀性能更好不用監(jiān)聽滾動(dòng)事件。不過它的觀察回調(diào)會(huì)延遲一小拍而且部分老瀏覽器對(duì)IntersectionObserver的支持需要polyfill。從我個(gè)人的項(xiàng)目經(jīng)歷看如果滾動(dòng)容器是window直接用滾動(dòng)事件反而更穩(wěn)如果滾動(dòng)容器是一個(gè)內(nèi)部元素比如HdsNavigation里嵌了內(nèi)容面板那用IntersectionObserver更容易控制。后面我會(huì)把兩種方式的代碼都貼出來方便你按場(chǎng)景選擇。2. 核心實(shí)現(xiàn)與實(shí)操步驟2.1 環(huán)境準(zhǔn)備與組件安裝先別急著寫代碼把環(huán)境搞對(duì)。HdsNavigation在大多數(shù)情況下是隨組件庫一起安裝的比如通過包管理器npm install hds-navigation # 或者 yarn add hds-navigation裝完之后在入口文件引入組件和樣式import HdsNavigation from hds-navigation; import hds-navigation/dist/hds-navigation.css;需要注意不同版本的組件庫依賴的框架版本不同。我用的項(xiàng)目是Vue3 Vite所以直接以ES模塊的方式導(dǎo)入。如果你用的是React往往還需要在組件外層包一層HdsNavigation /并傳入?yún)?shù)。但無論框架怎么變它的核心行為都是一致的渲染頂部導(dǎo)航區(qū)域暴露接收外部狀態(tài)的能力。如果你的HdsNavigation是內(nèi)部封裝好的基礎(chǔ)組件那大概率你已經(jīng)有了一個(gè)叫Navigation的組件它的props里可能包含title、leftIcon、rightIcon等。此時(shí)你不需要額外安裝只需要知道它有默認(rèn)插槽或者屬性用來嵌套你自己的滑動(dòng)內(nèi)容區(qū)域。為了統(tǒng)一演示我這里假設(shè)HdsNavigation支持這樣的基本用法HdsNavigation :titlepageTitle :transparentisTop :blurisBlurred /transparent控制是否透明blur控制是否啟用模糊。如果組件本身不支持這些prop我們可以通過樣式類名去控制畢竟我們要的無非是“類A”和“類B”切換。2.2 搭建基本滑動(dòng)布局先搭建一個(gè)最簡(jiǎn)單的頁面骨架。為了演示效果內(nèi)容區(qū)域需要一個(gè)超高的列表讓頁面可以滾動(dòng)。template div classpage HdsNavigation title文章詳情 classnav :class{ nav--blur: isBlurred } / main classcontent div v-fori in 30 :keyi classcard 這是第 {{ i }} 個(gè)可滑動(dòng)內(nèi)容的卡片 /div /main /div /template這里有兩個(gè)關(guān)鍵點(diǎn)第一導(dǎo)航欄使用fixed定位方式懸浮在頁面頂部第二內(nèi)容區(qū)域正常撐開高度形成滾動(dòng)。fixed定位在這里非常關(guān)鍵因?yàn)槿绻麑?dǎo)航欄使用absolute或者靜態(tài)定位它會(huì)隨著內(nèi)容往下滾動(dòng)走根本看不到模糊效果。除此之外我們要保證導(dǎo)航欄的層級(jí)高于內(nèi)容給它加一個(gè)z-index: 999。有人會(huì)問為什么不用position: stickysticky確實(shí)可以讓頭部吸頂?shù)幸粋€(gè)問題當(dāng)滾動(dòng)容器不是window而是某個(gè)內(nèi)部元素時(shí)sticky的參照對(duì)象是父容器容易出各種邊界問題。而fixed雖然會(huì)脫離文檔流但勝在穩(wěn)定配合一個(gè)占位符把導(dǎo)航欄原本的高度撐開就行。2.3 實(shí)現(xiàn)滾動(dòng)監(jiān)聽與狀態(tài)切換接下來是核心滾動(dòng)監(jiān)聽。先看最簡(jiǎn)單的window滾動(dòng)方案export default { data() { return { isBlurred: false, scrollThreshold: 30, }; }, mounted() { this.scrollHandler this.handleScroll.bind(this); window.addEventListener(scroll, this.scrollHandler, { passive: true }); this.handleScroll(); }, beforeUnmount() { window.removeEventListener(scroll, this.scrollHandler); }, methods: { handleScroll() { const scrollY window.scrollY || window.pageYOffset; this.isBlurred scrollY this.scrollThreshold; }, }, };這里要注意幾點(diǎn)第一passive: true一定要加它告訴瀏覽器滾動(dòng)事件不會(huì)被preventDefault()阻斷瀏覽器可以放心地走優(yōu)化路徑滾動(dòng)更流暢不會(huì)出現(xiàn)掉幀。第二在頁面初始化時(shí)需要先調(diào)用一次handleScroll()刷新一下初始狀態(tài)否則刷新后直接滑到中部導(dǎo)航欄可能還是透明的屬于一個(gè)經(jīng)典的狀態(tài)不同步問題。如果滾動(dòng)容器是某個(gè)內(nèi)部元素比如HdsNavigation內(nèi)容區(qū)外面包了一層overflow-y: auto的div那監(jiān)聽對(duì)象就不是window而是這個(gè)內(nèi)部元素// 假設(shè) containerRef 是滾動(dòng)容器 containerRef.value.addEventListener(scroll, this.scrollHandler, { passive: true });獲取滾動(dòng)距離時(shí)使用containerRef.value.scrollTop而不是window.scrollY。再看IntersectionObserver方案。先在你的頁面頂部放一個(gè)哨兵元素div refsentinel classsentinel/div然后注冊(cè)觀察器mounted() { this.observer new IntersectionObserver( (entries) { entries.forEach((entry) { // 當(dāng)哨兵元素離開可視區(qū)域時(shí)isBlurred 設(shè)為 true this.isBlurred !entry.isIntersecting; }); }, { threshold: 0 } ); this.observer.observe(this.$refs.sentinel); }, beforeUnmount() { this.observer.disconnect(); }這個(gè)方案在移動(dòng)端H5里表現(xiàn)尤其好因?yàn)闆]有頻繁的scroll回調(diào)適合對(duì)性能敏感的場(chǎng)景。但它的缺點(diǎn)也很明顯如果你希望模糊效果從滾動(dòng)開始的第一幀就立刻出現(xiàn)可能IntersectionObserver會(huì)有極短的延遲視覺上不如滾動(dòng)監(jiān)聽那么絲滑。所以很多團(tuán)隊(duì)干脆兩個(gè)都上用滾動(dòng)監(jiān)聽做主邏輯用requestAnimationFrame做批量狀態(tài)更新。2.4 頂部模糊樣式的關(guān)鍵CSS到這里就該把視覺部分處理到位了。假設(shè)我們給HdsNavigation加了一個(gè)類nav--blur那么樣式的核心是.nav { position: fixed; top: 0; left: 0; right: 0; height: 56px; z-index: 999; background-color: rgba(255, 255, 255, 0.85); transition: background-color 0.2s ease; } .nav--blur { background-color: rgba(255, 255, 255, 0.65); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); box-shadow: 0 1px 8px rgba(0, 0, 0, 0.06); }這里有幾個(gè)細(xì)節(jié)首先是transition只對(duì)background-color做過渡因?yàn)閎ackdrop-filter用transition過渡效果很差容易造成性能問題。其次是rgba透明度不要選太透的值否則導(dǎo)航欄后面的文字會(huì)穿透過來標(biāo)題變得難讀一般0.6到0.8之間比較保險(xiǎn)。最后是box-shadow這個(gè)不是必須的但在模糊導(dǎo)航和內(nèi)容之間加一道細(xì)陰影能增加層次感讓導(dǎo)航欄更像懸浮在內(nèi)容之上。如果HdsNavigation內(nèi)部有自己的背景樣式可能需要用!important或者更深的類選擇器去覆蓋。當(dāng)然我更建議直接在組件上通過CSS變量去定制比如.nav { --nav-background: rgba(255, 255, 255, 0.85); --nav-blur-radius: 0px; background-color: var(--nav-background); } .nav--blur { --nav-background: rgba(255, 255, 255, 0.65); --nav-blur-radius: 12px; backdrop-filter: blur(var(--nav-blur-radius)); }這樣寫的好處是后續(xù)如果想調(diào)整透明度和模糊程度只需要改兩個(gè)CSS變量完全不用動(dòng)JS。3. 深入細(xì)節(jié)參數(shù)調(diào)節(jié)與性能優(yōu)化3.1 模糊半徑與背景透明度調(diào)節(jié)很多同學(xué)做完基礎(chǔ)版本后發(fā)現(xiàn)模糊效果要么太重要么輕得跟沒有一樣。問題往往出在參數(shù)上。blur值越大后面內(nèi)容越看不清楚但字體的邊緣也會(huì)顯得發(fā)虛所以文章頁面我一般控制在10px到16px之間。如果偏小比如6px基本等同于透明背景模糊意義不大。透明度參數(shù)更是玄學(xué)。背景顏色使用rgba(255, 255, 255, 0.7)代表紅色、綠色、藍(lán)色通道都是255也就是白色alpha通道0.7表示70%不透明。這個(gè)值偏大時(shí)背景幾乎純白模糊不容易被察覺偏小時(shí)文字疊上來會(huì)感覺臟。一個(gè)經(jīng)驗(yàn)法則壁紙式背景或圖片內(nèi)容多透明度可以低一些比如0.5讓內(nèi)容若隱若現(xiàn)純文字閱讀類內(nèi)容透明度不要低于0.65保證可讀性。如果產(chǎn)品想要更接近iOS原生的毛玻璃效果可以在背景上疊加一個(gè)漸變層。比如.nav--blur { background: linear-gradient( to bottom, rgba(255, 255, 255, 0.75) 0%, rgba(255, 255, 255, 0.55) 100% ); }這樣視覺上會(huì)有一點(diǎn)點(diǎn)從實(shí)到虛的過渡幾乎沒有人能注意到具體是漸變但整體質(zhì)感會(huì)提升。3.2 防抖節(jié)流與passive事件滾動(dòng)事件是高頻事件很容易在1秒內(nèi)觸發(fā)幾十次甚至上百次。如果每次觸發(fā)都做一次樣式切換或類名切換瀏覽器雖然能扛住但在低端安卓機(jī)上會(huì)出現(xiàn)肉眼可見的卡頓。這時(shí)候節(jié)流和requestAnimationFrame就該上場(chǎng)了。最簡(jiǎn)單的方式是節(jié)流methods: { handleScroll() { if (this.ticking) return; this.ticking true; window.requestAnimationFrame(() { this.updateState(); this.ticking false; }); }, updateState() { const scrollY window.scrollY || window.pageYOffset; this.isBlurred scrollY this.scrollThreshold; }, }因?yàn)閞equestAnimationFrame會(huì)把狀態(tài)更新放在下一次重繪前瀏覽器能夠把同一幀內(nèi)的多次scroll回調(diào)合并成一次這比單純用setTimeout做節(jié)流要平滑得多。這個(gè)技巧在滑動(dòng)列表的很多場(chǎng)景里都適用不只是導(dǎo)航欄狀態(tài)切換。另外在添加事件監(jiān)聽時(shí)一定要加上passive: true讓滾動(dòng)事件不再被瀏覽器視為“可能被阻止默認(rèn)行為”。如果你忘了加某些版本的瀏覽器會(huì)在主線程上額外做一次檢查間接造成滾動(dòng)變卡。這里順便提一句如果你在scroll事件里調(diào)用了preventDefault()瀏覽器會(huì)警告你但有了passive后這個(gè)調(diào)用會(huì)被忽略所以務(wù)必確認(rèn)自己不需要阻止默認(rèn)滾動(dòng)行為。3.3 移動(dòng)端Fixed與Sticky的坑移動(dòng)端實(shí)現(xiàn)頂部模糊導(dǎo)航最大的坑還是fixed定位和iOS的兼容問題。在iOS低版本上position: fixed元素在輸入框聚焦時(shí)可能會(huì)被頂上去或者出現(xiàn)閃爍。不過現(xiàn)代iOS問題已經(jīng)少了但如果你的HdsNavigation里包含搜索框要格外小心。另外如果頁面整體是overflow-y: auto的容器而不是window滾動(dòng)fixed元素默認(rèn)相對(duì)視口定位不會(huì)跟著容器跑。這時(shí)候如果導(dǎo)航欄要跟隨某個(gè)容器反而要用sticky。sticky這個(gè)定位很有意思它既保留在文檔流中又在滾動(dòng)到頂部時(shí)“粘住”。但要注意它只對(duì)其最近的滾動(dòng)祖先生效而且父容器不能設(shè)置overflow: hidden或overflow: auto之外的值否則會(huì)失效。我踩過一次比較隱蔽的坑給內(nèi)容區(qū)父節(jié)點(diǎn)設(shè)置了overflow-x: hidden來阻止橫向滾動(dòng)結(jié)果sticky導(dǎo)航頭無論如何都吸不住。檢查了半天才發(fā)現(xiàn)是overflow-x: hidden破壞了sticky的生效條件。所以我的建議是如果你的導(dǎo)航需要跟隨某個(gè)具體容器滾動(dòng)并且該容器高度有限用sticky如果是全頁面滾動(dòng)穩(wěn)妥方案是fixed。在使用fixed時(shí)務(wù)必給body或者h(yuǎn)tml設(shè)置一個(gè)高度或最小高度否則內(nèi)容高度不夠撐不起滾動(dòng)模糊效果難免會(huì)看起來很呆板。3.4 結(jié)合HdsNavigation API的進(jìn)階用法有些版本的HdsNavigation會(huì)提供onscroll回調(diào)或者scroll-direction參數(shù)如果你恰好用的是這種帶狀態(tài)感知的組件完全不用自己監(jiān)聽滾動(dòng)了。它會(huì)在內(nèi)部幫你計(jì)算滾動(dòng)方向。比如上滑時(shí)導(dǎo)航欄不但可以模糊甚至可以整體隱藏只保留一個(gè)收起的手勢(shì)下滑時(shí)再以模糊狀態(tài)出現(xiàn)。這種組件通常有一個(gè)scrollMode配置項(xiàng)取值可能是normal | hide | blur。當(dāng)設(shè)置成blur時(shí)組件內(nèi)部會(huì)自動(dòng)實(shí)現(xiàn)我上面描述的所有邏輯。如果你遇到這種情況那直接開箱即用就好。但更多情況下我們用的是基礎(chǔ)版HdsNavigation沒有這些高級(jí)配置。此時(shí)我們可以通過組件暴露的slot或者自定義屬性來擴(kuò)展。比如在HdsNavigation里放一個(gè)動(dòng)態(tài)類名HdsNavigation :classnavClass /然后navClass返回一個(gè)對(duì)象根據(jù)isBlurred決定nav--blur是否存在。這種耦合方式雖然不夠優(yōu)雅但在不熟悉組件源碼時(shí)是成本最低的辦法。再進(jìn)階一點(diǎn)你可以利用Vue的provide/inject或React的Context把滾動(dòng)狀態(tài)傳給HdsNavigation內(nèi)部任意子組件。這樣右側(cè)的“分享按鈕”在模糊模式下可以改變底色左側(cè)的“返回圖標(biāo)”可以換顏色整個(gè)導(dǎo)航欄的視覺聯(lián)動(dòng)就會(huì)非常完整。4. 常見問題與排查技巧實(shí)錄4.1 滾動(dòng)容器的選擇window還是元素很多人在頁面上明明滾動(dòng)了但isBlurred一直不變化第一反應(yīng)是監(jiān)聽代碼寫錯(cuò)了。其實(shí)大概率是監(jiān)聽錯(cuò)了容器。如果你是在一個(gè)高度為100vh的div里面做overflow-y: auto然后監(jiān)聽window的scroll那肯定拿不到新的scrollTop因?yàn)閣indow本身沒有滾動(dòng)。這種時(shí)候應(yīng)該先檢查目標(biāo)元素是否真的出現(xiàn)了滾動(dòng)條再?zèng)Q定監(jiān)聽誰。一個(gè)快速判斷方法在瀏覽器控制臺(tái)執(zhí)行document.querySelector(.content).scrollTop;如果內(nèi)容沒有移動(dòng)說明滾動(dòng)條不在.content上而是可能在外層或者根元素上。用這個(gè)思路排查很快就能定位。4.2 backdrop-filter兼容性backdrop-filter的兼容性說實(shí)話已經(jīng)不是瓶頸但在部分安卓WebView里表現(xiàn)比較差。如果發(fā)現(xiàn)某些手機(jī)上模糊效果完全沒生效多半是內(nèi)核不支持這個(gè)屬性。最簡(jiǎn)單的兜底方案是在模糊導(dǎo)航的樣式后面加一層不依賴backdrop-filter的高透明度背景色至少保證文字可讀.nav--blur { background-color: rgba(255, 255, 255, 0.9); /* 兜底 */ backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }這樣即使模糊失效視覺上也只是少了一層磨砂感不會(huì)完全錯(cuò)亂。如果你是強(qiáng)迫癥非要在老機(jī)器上還原模糊那只能考慮用圖片預(yù)渲染或者用半透明漸變?nèi)ツM不過我覺得沒必要畢竟連iOS和主流安卓都已經(jīng)支持了。4.3 導(dǎo)航欄跳動(dòng)問題另一個(gè)高頻問題導(dǎo)航欄在滾動(dòng)到閾值邊界的一瞬間背景和陰影突然變化看起來像跳了一下。這個(gè)跳動(dòng)通常來自兩個(gè)原因一是沒有加transition導(dǎo)致背景顏色瞬間切換二是box-shadow從無到有瞬間撐出陰影造成了視覺上的位移感。解決方法是把陰影和背景的過渡一起加上并且把陰影值寫得更溫和。比如.nav { transition: background-color 0.25s ease, box-shadow 0.25s ease; }如果還是感覺跳可以把閾值從單個(gè)數(shù)值改成一個(gè)區(qū)間比如在0到40px范圍內(nèi)漸進(jìn)式調(diào)整透明度。這個(gè)操作可以用CSS變量配合JS實(shí)現(xiàn)const progress Math.min(scrollY / 40, 1); this.navOpacity 0.85 - progress * 0.2;代碼明明不復(fù)雜但觀感會(huì)非常順滑。4.4 與路由切換、頁面緩存的沖突單頁應(yīng)用里HdsNavigation往往放在布局組件中。當(dāng)路由切換時(shí)滾動(dòng)位置會(huì)保留但狀態(tài)變量可能會(huì)被重置。比如說從A頁面滑到一半此時(shí)導(dǎo)航欄是模糊狀態(tài)路由跳轉(zhuǎn)到B頁面B頁面是空表單滾動(dòng)高度為0但組件的isBlurred仍然為true導(dǎo)致頁面一進(jìn)來導(dǎo)航欄就是模糊的。這種情況下必須在路由切換時(shí)主動(dòng)重置狀態(tài)最簡(jiǎn)單的方式是監(jiān)聽路由watch: { $route() { this.isBlurred false; window.scrollTo(0, 0); } }如果使用了keep-alive還需要在activated鉤子里重新計(jì)算當(dāng)前滾動(dòng)位置避免緩存頁面恢復(fù)時(shí)狀態(tài)錯(cuò)亂。這個(gè)屬于經(jīng)驗(yàn)型問題做多了才會(huì)遇見。5. 結(jié)尾一些個(gè)人經(jīng)驗(yàn)分享做了這個(gè)功能之后最大的體會(huì)是導(dǎo)航欄模糊效果本質(zhì)上是一個(gè)“滾動(dòng)環(huán)境感知”問題。我們不能只盯著CSS寫沒寫對(duì)還要看滾動(dòng)容器、組件狀態(tài)、路由生命周期這些周邊因素。如果哪一個(gè)環(huán)節(jié)沒有配合好視覺上的小問題就會(huì)變成邏輯上的大問題。最后分享一個(gè)小技巧如果你做的頁面結(jié)構(gòu)很復(fù)雜無法確定滾動(dòng)容器到底是哪個(gè)你可以在所有可能的元素上臨時(shí)打上console.log(scrollTop:, el.scrollTop)在滾動(dòng)時(shí)快速定位。定位完成后再把日志刪除整個(gè)過程耗時(shí)不超過五分鐘卻能省去后面很多的調(diào)試時(shí)間。我這個(gè)項(xiàng)目后來還順手實(shí)現(xiàn)了“滾動(dòng)超過頁面高度后返回頂部按鈕的漸隱漸顯”因?yàn)橐呀?jīng)把滾動(dòng)容器和狀態(tài)管理理清楚了所以加功能非常順手。約等于一份代碼覆蓋好幾處交互場(chǎng)景這大概就是做清晰架構(gòu)的回報(bào)。