
Remix UI Tween API 全解析基于生成器的三次貝塞爾補間動畫【免費下載鏈接】remixThe fully-stacked web framework項目地址: https://gitcode.com/GitHub_Trending/re/remixtween是 Remix UIremix-run/uianimation模塊中基于 ES 生成器實現(xiàn)的補間動畫原語用于在指定時長內以三次貝塞爾緩動曲線將數值從from插值到to并通過requestAnimationFrame逐幀驅動。本指南以 Tween API 文檔 為主線結合 tween.ts 源碼講解其生成器協(xié)議、貝塞爾數學內核、內置緩動預設與自定義曲線并給出命令式動畫、組件內自動清理以及多屬性動畫的完整實戰(zhàn)方案。讀完本文你將能熟練用tween驅動 Canvas/WebGL、非 CSS 屬性與序列化復雜動畫并在 Remix UI 組件中安全接入handle.signal生命周期。認識tween一次一行得到一個動畫生成器tween的調用形態(tài)非常簡潔——傳入from、to、duration與curve四個選項返回一個可逐步推進的生成器generatorimport { tween, easings } from remix/ui/animation let animation tween({ from: 0, to: 100, duration: 1000, curve: easings.easeInOut, }) // Initialize generator animation.next() function animate(timestamp: number) { let { value, done } animation.next(timestamp) element.style.transform translateX(${value}px) if (!done) requestAnimationFrame(animate) } requestAnimationFrame(animate)注意兩個關鍵步驟先手動調用一次animation.next()不帶參數完成生成器初始化讓 tween 有機會先yield出起始值from之后每一幀調用animation.next(timestamp)把requestAnimationFrame回調收到的時間戳傳回生成器內部作為計算進度的依據。從 tween.ts 的實現(xiàn)看生成器內部維護startTime第一次收到時間戳時記錄與當前value然后進入while (true)循環(huán)先yield value交出當前插值結果等外部調用next(timestamp)時再計算elapsed從而形成“幀回調 - 生成器 - 新值”的雙向數據流。工作原理生成器協(xié)議與三次貝塞爾數學文檔用三條規(guī)則概括tween的行為每次迭代yield當前插值后的數值通過next(timestamp)接收當前時間戳當duration耗盡后返回done: true。其核心思想是“用三次貝塞爾曲線把線性的時間進度映射為緩動后的數值進度”這與 CSS 的cubic-bezier()時間函數完全一致。源碼將這一數學過程拆成三個函數cubicBezier(t, p1, p2)tween.ts#L38-L42三次貝塞爾求值公式B(t) 3(1-t)2t·p1 3(1-t)t2·p2 t3。由于 CSS 風格貝塞爾固定以(0,0)為起點、(1,1)為終點只需傳入兩個中間控制點的坐標cubicBezierDerivative(t, p1, p2)tween.ts#L51-L55貝塞爾導數B(t) 3(1-t)2·p1 6(1-t)t·(p2-p1) 3t2·(1-p2)為求根迭代提供斜率solveCubicBezierX(x1, x2, targetX)tween.ts#L9-L27已知 x 軸時間進度反解參數 t使用Newton-Raphson 迭代以t targetX作初始猜測通常 4~8 次迭代即可收斂源碼固定迭代 8 次并在斜率或誤差小于1e-6時提前退出最后把結果鉗制在[0, 1]區(qū)間。主生成器 tween 的推進邏輯為let elapsed timestamp - startTime let linearProgress Math.min(elapsed / duration, 1) // x 軸 時間y 軸 數值 let t solveCubicBezierX(x1, x2, linearProgress) let easedProgress cubicBezier(t, y1, y2) value from (to - from) * easedProgress if (linearProgress 1) { return to }可以看到linearProgress被鉗制在1以內當進度到達1時生成器直接return to此時done為true、value精確等于目標值不會出現(xiàn)過沖overshoot——這正是tween與下文物理彈簧spring的本質區(qū)別。內置緩動預設easingseasings是一個包含常用三次貝塞爾控制點的常量對象控制點數值與 CSS 時間函數一一對應定義見 tween.ts#L75-L81import { easings } from remix/ui/animation easings.linear // { x1: 0, y1: 0, x2: 1, y2: 1 } easings.ease // { x1: 0.25, y1: 0.1, x2: 0.25, y2: 1 } easings.easeIn // { x1: 0.42, y1: 0, x2: 1, y2: 1 } easings.easeOut // { x1: 0, y1: 0, x2: 0.58, y2: 1 } easings.easeInOut // { x1: 0.42, y1: 0, x2: 0.58, y2: 1 }預設控制點 (x1, y1, x2, y2)行為描述linear(0, 0, 1, 1)無緩動勻速ease(0.25, 0.1, 0.25, 1)CSS 默認 easeeaseIn(0.42, 0, 1, 1)慢啟動快結束easeOut(0, 0, 0.58, 1)快啟動慢結束easeInOut(0.42, 0, 0.58, 1)首尾都慢源碼用as const聲明所有控制點為字面量類型可安全地直接賦值給BezierCurve。自定義曲線直接書寫 CSS cubic-bezier 控制點當內置預設不夠用時可以像寫 CSScubic-bezier(x1, y1, x2, y2)一樣定義任意曲線。注意 CSS 語法允許 y 軸控制點超出[0,1]以產生回彈效果tween同樣支持let customCurve { x1: 0.68, y1: -0.55, x2: 0.265, y2: 1.55, } let animation tween({ from: 0, to: 100, duration: 500, curve: customCurve, })從類型定義看BezierCurve 僅約束四個控制點字段x1/x2通常落在0~1時間軸而y1/y2可以越界數值軸這為“帶輕微回彈”的緩動效果留出了空間。在組件中使用用handle.signal自動清理tween屬于命令式動畫在 Remix UI 組件中運行requestAnimationFrame循環(huán)時必須處理組件銷毀后的清理。文檔給出的模式是在每一幀tick開始時檢查handle.signal.aborted一旦組件卸載立即停止避免對已卸載 DOM 的無效寫入function AnimatedValue(handle: Handle) { let value 0 function animateTo(target: number) { let animation tween({ from: value, to: target, duration: 300, curve: easings.easeOut, }) animation.next() // Initialize function tick(timestamp: number) { if (handle.signal.aborted) return let result animation.next(timestamp) value result.value handle.update() if (!result.done) { requestAnimationFrame(tick) } } requestAnimationFrame(tick) } return () ( div div style{{ transform: translateX(${value}px) }}Moving/div button mix{[ on(click, () { animateTo(200) }), ]} Animate /button /div ) }要點拆解組件每次點擊按鈕都會從當前value起做 300ms 的easeOut補間handle.update()觸發(fā)一次 Remix UI 重渲染handle.signal是 Remix UI 提供給組件處理器的中止信號AbortSignaltick內首行檢查aborted即可在卸載時立即終止動畫循環(huán)通過mix與on(click, ...)綁定事件是 Remix UI 組件原生的聲明式事件寫法。多屬性動畫多個 tween 并行驅動tween只負責單個數值的插值組合多個屬性時創(chuàng)建多個生成器、在同一個requestAnimationFrame回調里并行推進即可let xAnimation tween({ from: 0, to: 100, duration: 500, curve: easings.easeOut }) let yAnimation tween({ from: 0, to: 50, duration: 500, curve: easings.easeOut }) let scaleAnimation tween({ from: 1, to: 1.5, duration: 500, curve: easings.easeOut }) xAnimation.next() yAnimation.next() scaleAnimation.next() function animate(timestamp: number) { let x xAnimation.next(timestamp) let y yAnimation.next(timestamp) let scale scaleAnimation.next(timestamp) element.style.transform translate(${x.value}px, ${y.value}px) scale(${scale.value}) if (!x.done || !y.done || !scale.done) { requestAnimationFrame(animate) } } requestAnimationFrame(animate)由于三個 tween 共享同一duration與曲線且都以首個時間戳為各自起點多值動畫天然同步循環(huán)終止條件需要同時檢查所有生成器的done。若希望每個屬性采用不同的時長或緩動曲線只需在創(chuàng)建時各自指定即可仍可在同一回調中并行推進。同樣的并行思路也出現(xiàn)在 animation/README.md 的示例里。API 參考tween(options)創(chuàng)建一個隨時間在數值間插值的生成器。完整選項類型TweenOptionsinterface TweenOptions { from: number // Starting value to: number // Ending value duration: number // Duration in milliseconds curve: BezierCurve // Easing curve } interface BezierCurve { x1: number // First control point X (0-1) y1: number // First control point Y x2: number // Second control point X (0-1) y2: number // Second control point Y }返回類型Generatornumber, number, number——yield當前插值動畫結束時return最終值即tonext()的返回值中done: true表示動畫完成。duration單位為毫秒首次收到時間戳后開始計時。easings包含預設貝塞爾曲線的對象即上文的五個預設。何時使用tween何時改用其他方案tween適合以下場景原文 When to Use 歸納由requestAnimationFrame驅動的命令式動畫Canvas/WebGL 動畫DOM 之外沒有 CSS transition 可用動畫非 CSS 屬性如數值狀態(tài)、圖表數據、滾動位置等復雜的有序動畫序列多個階段串聯(lián)時逐幀控制進度。而對于大多數 UI 動畫文檔明確建議優(yōu)先使用動畫 mixinanimateEntrance、animateExit、animateLayout或配合 Spring API 的 CSS transition——詳見 animation 模塊總覽 與 Spring API 文檔。二者定位差異清晰tween是時間驅動的貝塞爾補間嚴格按時長執(zhí)行、不會過沖spring是物理驅動的彈簧動畫基于duration bounce參數生成 CSSlinear()緩動并可迭代出帶過沖的進度序列。選擇原則可概括為能用 CSS transition 表達就用spring/mixin需要逐幀掌控數值變化尤其 Canvas、非 CSS 屬性、序列動畫就用tween。延伸閱讀Spring API 文檔物理彈簧動畫可字符串化為 CSS transition、展開為 WAAPI 參數或迭代為 0~1 進度animation 模塊 READMEentrance/exit/layout/spring/tween 全套原語總覽tween 源碼實現(xiàn)貝塞爾求解與生成器完整實現(xiàn)animation 導出入口tween、easings及類型TweenOptions、BezierCurve的統(tǒng)一出口【免費下載鏈接】remixThe fully-stacked web framework項目地址: https://gitcode.com/GitHub_Trending/re/remix創(chuàng)作聲明:本文部分內容由AI輔助生成(AIGC),僅供參考