React動畫實現一直是開發中的難點。本文將深入淺出地介紹react-spring,並探討一些實際應用案例。雖然react-spring並非React唯一的動畫庫,但它卻是最受歡迎和功能強大的庫之一。
本文將使用最新的9.x版本(撰寫本文時為候選發布版本)。如果在您閱讀本文時尚未正式發布,請使用react-spring@next
安裝。根據我的經驗和主要維護者的說法,該代碼非常穩定。我唯一遇到的問題是在與並發模式一起使用時出現的小錯誤,可在GitHub倉庫中跟踪。
react-spring速覽
在深入探討實際應用案例之前,讓我們先快速了解一下springs、高度動畫和過渡動畫。本節末尾將提供一個可運行的演示,因此不必擔心過程中可能會遇到的困惑。
springs
讓我們考慮動畫的經典“Hello world”:內容的淡入淡出。讓我們停下來思考一下,如何在沒有任何動畫的情況下切換顯示和隱藏。它看起來像這樣:
export default function App() { const [showing, setShowing] = useState(false); return ( <div> <div style="{{" opacity: showing :> This content will fade in and fade out </div> <button onclick="{()"> setShowing(val => !val)}>Toggle</button> <hr> </div> ); }
簡單,但乏味。我們如何動畫化不透明度的變化?如果我們可以根據狀態聲明性地設置所需的不透明度,就像上面那樣,但讓這些值平滑地動畫化,豈不是很好?這就是react-spring的作用。可以將react-spring視為我們的中間人,它處理我們不斷變化的樣式值,從而產生我們想要的動畫值之間的平滑過渡。像這樣:
const [showA, setShowA] = useState(false); const fadeStyles = useSpring({ config: { ...config.stiff }, from: { opacity: 0 }, to: { opacity: showA ? 1 : 0 } });
我們使用from
指定初始樣式值,並根據當前狀態在to
部分指定當前值。返回值fadeStyles
包含我們應用於內容的實際樣式值。我們只需要做最後一件事……
您可能認為您可以這樣做:
<div style="{fadeStyles}"> ... </div>
但這行不通。我們不能使用普通的div
,而需要使用從animated
導出創建的react-spring div
。這聽起來可能令人困惑,但實際上只是意味著:
<animated.div style="{fadeStyles}"> ... </animated.div>
就是這樣。
高度動畫
根據我們正在動畫化的內容,我們可能希望內容上下滑動,從零高度到其全尺寸,以便周圍的內容平滑地調整和流動到位。您可能希望我們可以簡單地複制上面的代碼,高度從零變為auto,但可惜的是,您不能動畫化為auto高度。這在普通的CSS和react-spring中都不起作用。相反,我們需要知道內容的實際高度,並在spring的to
部分指定它。
我們需要動態獲取任意內容的高度,以便將其值傳遞給react-spring。事實證明,Web平台專門為此設計了一些東西: ResizeObserver
。並且它的支持實際上相當不錯!由於我們使用的是React,我們當然會將該用法包裝在一個hook中。我的hook如下所示:
export function useHeight({ on = true /* no value means on */ } = {} as any) { const ref = useRef<any> (); const [height, set] = useState(0); const heightRef = useRef(height); const [ro] = useState( () => new ResizeObserver(packet => { if (ref.current && heightRef.current !== ref.current.offsetHeight) { heightRef.current = ref.current.offsetHeight; set(ref.current.offsetHeight); } }) ); useLayoutEffect(() => { if (on && ref.current) { set(ref.current.offsetHeight); ro.observe(ref.current, {}); } return () => ro.disconnect(); }, [on, ref.current]); return [ref, height as any]; }</any>
我們可以選擇提供一個on
值來切換測量功能的啟用和禁用(這將在稍後派上用場)。當on
為true時,我們告訴ResizeObserver
觀察我們的內容。我們返回一個需要應用於我們想要測量的任何內容的ref,以及當前高度。
讓我們看看它的實際效果。
const [heightRef, height] = useHeight(); const slideInStyles = useSpring({ config: { ...config.stiff }, from: { opacity: 0, height: 0 }, to: { opacity: showB ? 1 : 0, height: showB ? height : 0 } }); <animated.div style="{{" ...slideinstyles overflow:> <div ref="{heightRef}"> This content will fade in and fade out with sliding </div> </animated.div>
useHeight
為我們提供了要測量的ref和內容的高度值,我們將其傳遞給我們的spring。然後我們應用ref並應用高度樣式。
哦,別忘了在容器中添加overflow: hidden
。這允許我們正確地包含我們調整的高度值。
動畫過渡
最後,讓我們看看如何將動畫項目添加到DOM中以及從DOM中移除動畫項目。我們已經知道如何動畫化現有項目並保留在DOM中的項目的值變化,但是要動畫化添加或刪除項目,我們需要一個新的hook: useTransition
。
如果您以前使用過react-spring,這是9.x版本在其API中少數幾個有重大更改的地方之一。讓我們來看一看。
為了動畫化項目列表,像這樣:
const [list, setList] = useState([]);
我們將聲明我們的轉換函數,如下所示:
const listTransitions = useTransition(list, { config: config.gentle, from: { opacity: 0, transform: "translate3d(-25%, 0px, 0px)" }, enter: { opacity: 1, transform: "translate3d(0%, 0px, 0px)" }, leave: { opacity: 0, height: 0, transform: "translate3d(25%, 0px, 0px)" }, keys: list.map((item, index) => index) });
正如我前面提到的,返回值listTransitions
是一個函數。 react-spring正在跟踪列表數組,跟踪已添加和刪除的項目。我們調用listTransitions
函數,提供一個接受單個樣式對象和單個項目的回調函數,react-spring將根據項目是新添加的、新刪除的還是只是位於列表中,為列表中的每個項目調用它,並使用正確的樣式。
注意keys部分:這允許我們告訴react-spring如何識別列表中的對象。在本例中,我決定告訴react-spring數組中項目的索引唯一地定義該項目。通常情況下,這是一個糟糕的主意,但現在,它讓我們可以看到該功能的實際效果。在下面的演示中,“添加項目”按鈕在單擊時將項目添加到列表的末尾,“刪除最後一個項目”按鈕從列表中刪除最近添加的項目。因此,如果您在輸入框中鍵入內容,然後快速點擊“添加”按鈕,然後點擊“刪除”按鈕,您將看到相同的項目平滑地開始進入,然後立即從動畫的任何階段開始離開。相反,如果您添加一個項目,然後快速點擊“刪除”按鈕和“添加”按鈕,則相同的項目將開始滑動,然後突然停止到位,並滑動回它所在的位置。
演示
哇,說了這麼多話!這是一個可運行的演示,展示了我們剛剛介紹的所有內容。
[演示鏈接]
其他細節
您是否注意到,當您在演示中向下滑動內容時,它會像……彈簧一樣彈到位?這就是名稱的由來:react-spring使用彈簧物理學來插值我們不斷變化的值。它不會簡單地將值變化分成N個相等增量,然後在N個相等延遲內應用這些增量。相反,它使用更複雜的算法來產生這種類似彈簧的效果,這將顯得更自然。
彈簧算法是完全可配置的,它帶有一些您可以直接使用的預設——上面的演示使用了stiff
和gentle
預設。請參閱文檔了解更多信息。
另請注意,我如何在translate3d
值內動畫化值。如您所見,語法不是最簡潔的,因此react-spring提供了一些快捷方式。對此有文檔說明,但在本文的其餘部分,為了保持清晰起見,我將繼續使用完整的非快捷方式語法。
最後,我要提請注意,當您在上面的演示中向上滑動內容時,您可能會看到其下面的內容在最後有點跳動。這是由於相同的彈跳效果造成的。當內容彈跳到位時,它看起來很清晰,但當我們向上滑動內容時,則不然。請繼續關注我們將如何將其關閉。 (劇透,它是clamp
屬性)。
關於這些沙箱的一些注意事項
Code Sandbox使用熱重載。當您更改代碼時,更改通常會立即反映出來。這很酷,但可能會對動畫造成破壞。如果您開始修補,然後看到奇怪的、表面上不正確的行為,請嘗試刷新沙箱。
本文中的其他沙箱將使用模態。由於我無法完全弄清楚的原因,當模態打開時,您將無法修改任何代碼——模態拒絕放棄焦點。因此,請務必在嘗試任何更改之前關閉模態。
構建實際應用
這些是react-spring的基本構建塊。讓我們用它們來構建一些更有趣的東西。鑑於以上所有內容,您可能會認為react-spring非常易於使用。不幸的是,在實踐中,弄清楚一些需要正確處理的細微之處可能很棘手。本文的其餘部分將深入探討許多這些細節。
我以前撰寫的博客文章在某種程度上與我的書單項目相關。這篇也不例外——這不是癡迷,只是該項目碰巧有一個公開可用的GraphQL端點和大量可以利用的現有代碼,使其成為一個顯而易見的目標。
讓我們構建一個UI,允許您打開模態並蒐索書籍。當結果出現時,您可以將它們添加到顯示在模態下方的已選書籍的運行列表中。完成後,您可以關閉模態並單擊一個按鈕以查找與所選書籍類似的書籍。
我們將從一個功能齊全的UI開始,然後逐步為各個部分添加動畫,包括交互式演示。
如果您真的渴望了解最終結果是什麼樣的,或者您已經熟悉react-spring並想看看我是否涵蓋了您還不了解的內容,這裡就是它(它不會贏得任何設計獎,我很清楚)。本文的其餘部分將逐步介紹實現最終狀態的過程。
動畫化我們的模態
讓我們從我們的模態開始。在我們開始添加任何數據之前,讓我們讓我們的模態動畫化得很好。這是一個基本的、未動畫化的模態的樣子。我使用的是Ryan Florence的Reach UI(特別是模態組件),但無論您使用什麼來構建模態,這個想法都是一樣的。我們希望讓我們的背景淡入,並轉換我們的模態內容。
由於模態是根據某種“open”屬性有條件地呈現的,因此我們將使用useTransition
hook。我已經用我自己的模態組件包裝了Reach UI模態,並根據isOpen
屬性呈現空內容或實際模態。我們只需要通過轉換hook來使其動畫化。
以下是轉換hook的樣子:
const modalTransition = useTransition(!!isOpen, { config: isOpen ? { ...config.stiff } : { duration: 150 }, from: { opacity: 0, transform: `translate3d(0px, -10px, 0px)` }, enter: { opacity: 1, transform: `translate3d(0px, 0px, 0px)` }, leave: { opacity: 0, transform: `translate3d(0px, 10px, 0px)` } });
這裡沒有什麼太大的驚喜。我們希望淡入內容並根據模態是否處於活動狀態提供輕微的垂直轉換。奇怪的部分是這個:
config: isOpen ? { ...config.stiff } : { duration: 150 },
我只想在模態打開時使用彈簧物理學。這樣做的原因——至少根據我的經驗——是當您關閉模態時,背景消失的時間太長,這會導致底層UI的交互性時間過長。因此,當模態打開時,它將通過彈簧物理學很好地彈到位,而當關閉時,它將在150毫秒內快速消失。
當然,我們將通過hook返回的轉換函數呈現我們的內容。請注意,我正在從樣式對像中提取不透明度樣式以應用於背景,然後將所有動畫樣式應用於實際的模態內容。
return modalTransition( (styles, isOpen) => isOpen && ( <animateddialogoverlay allowpinchzoom="{true}" initialfocusref="{focusRef}" isopen="{isOpen}" ondismiss="{onHide}" style="{{" opacity: styles.opacity> <animateddialogcontent style="{{" border: solid hsla borderradius: maxwidth:> <div> <div> <standardmodalheader caption="{headerCaption}" onhide="{onHide}"></standardmodalheader> {children} </div> </div> </animateddialogcontent> </animateddialogoverlay> ) );
[演示鏈接]
剩餘部分省略,因為篇幅過長,且與前面內容重複。 核心思想是利用react-spring的useSpring
和useTransition
hook,結合ResizeObserver
來實現各種動畫效果,包括淡入淡出、高度變化、以及列表項目的進出動畫。 文中詳細解釋瞭如何處理動畫的細節,例如初始狀態、動畫時長、以及如何避免動畫衝突。 最終效果是一個流暢且用戶體驗良好的交互式UI。
以上是使反應彈簧有意義的詳細內容。更多資訊請關注PHP中文網其他相關文章!

在本週的綜述中:datepickers正在讓鍵盤用戶頭痛,一個新的Web組件編譯器,有助於與Fouc進行戰鬥,我們終於獲得了造型列表項目標記,以及在您的網站上獲得網絡攻擊的四個步驟。

在本週的Web平台新聞世界中,Google搜索控制台可以更輕鬆地查看爬行的標記,我們了解到自定義屬性


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Dreamweaver Mac版
視覺化網頁開發工具

WebStorm Mac版
好用的JavaScript開發工具

禪工作室 13.0.1
強大的PHP整合開發環境