場實(shí)戰(zhàn):用 @hyperframes/shader-transitions 在 GSAP 時(shí)間線上接入 GPU 著色器過渡)
HyperFrames 場景轉(zhuǎn)場實(shí)戰(zhàn)用 hyperframes/shader-transitions 在 GSAP 時(shí)間線上接入 GPU 著色器過渡【免費(fèi)下載鏈接】hyperframesWrite HTML. Render video. Built for agents.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/hy/hyperframesHyperFrames 遵循Write HTML. Render video.的理念頁面即時(shí)間線場景scene即 DOM。hyperframes/shader-transitions正是為這種架構(gòu)補(bǔ)齊最后一環(huán)的獨(dú)立子包——它用 WebGL 片元著色器fragment shader在相鄰場景之間渲染 GPU 加速轉(zhuǎn)場并通過捕獲場景動(dòng)畫采樣幀與 GSAP 時(shí)間線結(jié)合驅(qū)動(dòng)。閱讀本文后你將掌握在 HyperFrames 組合composition中安裝、配置與擴(kuò)展著色器轉(zhuǎn)場理解預(yù)捕獲 著色器合成的瀏覽器預(yù)覽管線、引擎確定性渲染管線的差異以及降級(jí)與緩存等工程細(xì)節(jié)。本文檔與源碼位于倉庫 packages/shader-transitions當(dāng)前版本 0.8.29見 package.json。一、包定位與核心思想hyperframes/shader-transitions解決的問題很具體在一個(gè)由多個(gè) HTML 場景如intro、demo、outro組成的 HyperFrames 視頻中相鄰場景之間需要一個(gè)持續(xù)的、在動(dòng)的轉(zhuǎn)場而不是硬切。包的核心思路可以概括為三步入口實(shí)現(xiàn)見 hyper-shader.ts預(yù)捕獲pre-capture為每個(gè)轉(zhuǎn)場在其開始前的時(shí)刻捕獲出境場景的動(dòng)畫采樣幀同時(shí)捕獲入境場景從轉(zhuǎn)場起點(diǎn)繼續(xù)推進(jìn)的采樣幀合成composite播放進(jìn)入轉(zhuǎn)場窗口時(shí)把兩套緩存幀作為紋理texture上傳到 WebGL交給片元著色器按u_progress混合輸出時(shí)間線timelineinit()返回一個(gè) GSAP 時(shí)間線。轉(zhuǎn)場期間場景動(dòng)畫依然持續(xù)向前推進(jìn)但播放循環(huán)中不再有 DOM 捕獲開銷轉(zhuǎn)場結(jié)束后由原本的場景動(dòng)畫無縫接管。由此帶來的關(guān)鍵收益是轉(zhuǎn)場過程是真正在動(dòng)的captured animation keeps advancing且 WebGL 渲染發(fā)生在 GPU 上如果瀏覽器沒有 WebGL包會(huì)自動(dòng)回退為普通時(shí)間線播放不做著色器合成。源碼中對(duì)應(yīng)回退邏輯會(huì)打印[HyperShader] WebGL unavailable — shader transitions disabled.并直接返回注冊(cè)好的時(shí)間線hyper-shader.ts 的init()內(nèi)。二、安裝與三種加載方式推薦通過 npm 安裝npm install hyperframes/shader-transitions或者通過 CDN 以script標(biāo)簽直接加載 IIFE 產(chǎn)物script srchttps://cdn.jsdelivr.net/npm/hyperframes/shader-transitions/dist/index.global.js/script該包設(shè)計(jì)上自包含、可獨(dú)立分發(fā)源碼注釋明確指出它作為獨(dú)立 CDN bundle 發(fā)布不依賴hyperframes/engine相關(guān)說明見 hyper-shader.ts。其唯一運(yùn)行時(shí)依賴是html2canvas^1.4.1見 package.json并在構(gòu)建時(shí)通過noExternal: [html2canvas]打進(jìn)了產(chǎn)物。產(chǎn)物由 tsup.config.ts 生成三種格式及適用場景如下表格式文件適用場景全局變量ESMdist/index.js打包器Vite、webpack 等—CJSdist/index.cjsNode.js /require()—IIFEdist/index.global.jsscript標(biāo)簽、CDNHyperShader所有格式均包含 source map并隨包發(fā)布 TypeScript 類型聲明tsup開啟了dts: true。IIFE 場景下請(qǐng)注意源碼注釋提到的一點(diǎn)當(dāng)通過script手工加載 bundle 后從 vanilla JS 傳入顯式的空字符串shader: 不會(huì)被當(dāng)作省略而會(huì)走到著色器注冊(cè)表并拋出明確的 unknown shader 錯(cuò)誤這是刻意的嚴(yán)格行為見 registry.ts 的getFragSource()。三、核心 APIinit(config): GsapTimeline所有能力都收斂到init()一個(gè)函數(shù)?;居梅╥mport { init } from hyperframes/shader-transitions; const tl init({ bgColor: #0a0a0a, // 場景捕獲時(shí)的兜底背景色 accentColor: #ff6b2b, // 著色器輝光效果強(qiáng)調(diào)色 scenes: [scene-1, scene-2, scene-3], transitions: [ { time: 3, shader: domain-warp, duration: 0.8 }, { time: 8, shader: light-leak, duration: 0.7 }, ], });3.1 配置項(xiàng)全表選項(xiàng)類型必填說明bgColorstring是場景捕獲時(shí)的兜底背景色hex。應(yīng)使用組合的 body/canvas 背景色——每個(gè)場景通過 CSS 自行設(shè)置各自的background-coloraccentColorstring否著色器輝光效果的強(qiáng)調(diào)色hexscenesstring[]是各場景元素的 ID按順序排列transitionsTransitionConfig[]是轉(zhuǎn)場定義數(shù)組見下文timelineGsapTimeline否已有的 GSAP 時(shí)間線把轉(zhuǎn)場疊加到它上面compositionIdstring否覆蓋data-composition-id用于時(shí)間線注冊(cè)previewCaptureFpsnumber否瀏覽器預(yù)覽模式每秒鐘為每個(gè)轉(zhuǎn)場預(yù)捕獲的采樣幀數(shù)。默認(rèn)30渲染模式則改為確定性的逐幀合成不使用此值3.2 底層行為印證對(duì)照 hyper-shader.ts 的init()實(shí)現(xiàn)可以確認(rèn)以下細(xì)節(jié)組合畫布尺寸優(yōu)先讀取根元素帶data-composition-id的元素上的data-width/data-height屬性缺失或非法時(shí)回退到1920 × 1080常量DEFAULT_WIDTH/DEFAULT_HEIGHT定義于 webgl.ts。compositionId的解析順序是config.compositionId→ 根元素data-composition-id→main。強(qiáng)調(diào)色三檔化單個(gè)accentColor會(huì)在內(nèi)部被推導(dǎo)成三檔 RGB 用于片元著色器 uniformu_accent、u_accent_dark約乘 0.35與u_accent_bright約1.5x0.2后截?cái)嗟?1。未提供時(shí)使用默認(rèn)橙色三檔[1, 0.6, 0.2]/[0.4, 0.15, 0]/[1, 0.85, 0.5]。預(yù)覽采樣速率previewCaptureFps默認(rèn) 30并在取值上被鉗制到1 ~ 60之間非法值NaN/非正數(shù)回退到默認(rèn)值。WebGL 畫布包會(huì)在組合根元素或body下創(chuàng)建一個(gè)idgl-canvas的透明覆蓋 canvas樣式為position:absolute; top:0; left:0; z-index:100; pointer-events:none尺寸與組合一致WebGL 上下文開啟preserveDrawingBuffer以便后續(xù)讀取。著色器程序所有用到的著色器會(huì)按名稱去重編譯并緩存到programsMap單個(gè)著色器編譯失敗只打印[HyperShader] Failed to compile ...不影響其他轉(zhuǎn)場。著色器間插值由于捕獲幀是離散的兩個(gè)相鄰采樣幀之間由包內(nèi)置的一個(gè)mix(texture2D(u_a), texture2D(u_b), u_mix)混合程序blend program做線性插值配合紋理交錯(cuò)機(jī)制保證預(yù)覽中的轉(zhuǎn)場依然連續(xù)。3.3 組合到已有時(shí)間線如果你的頁面已經(jīng)有自己的 GSAP 時(shí)間線例如已寫好每個(gè)場景的進(jìn)入/退出動(dòng)畫可以把時(shí)間線傳入轉(zhuǎn)場會(huì)被疊到上面而不是另起爐灶import { init } from hyperframes/shader-transitions; import { gsap } from gsap; const tl gsap.timeline({ paused: true }); // ... add your scene animations ... init({ bgColor: #000, scenes: [intro, demo, outro], transitions: [ { time: 5, shader: cinematic-zoom }, // duration/ease 走默認(rèn)值 { time: 12, shader: glitch, duration: 0.5 }, ], timeline: tl, });傳入timeline時(shí)包不會(huì)把返回值重新注冊(cè)到全局window.__timelines[compositionId]注冊(cè)只發(fā)生在由包自建時(shí)間線的情況下見 hyper-shader.ts 的registerTimeline()。四、TransitionConfig與SHADER_NAMES每個(gè)轉(zhuǎn)場由TransitionConfig描述選項(xiàng)類型默認(rèn)值說明timenumber—轉(zhuǎn)場開始時(shí)間秒shaderShaderName—上表中的著色器名。注意源碼類型中它是可選的省略u(píng)ndefined時(shí)該轉(zhuǎn)場退化為 CSS 交叉淡入淡出不依賴 WebGLdurationnumber0.7轉(zhuǎn)場持續(xù)時(shí)長秒easestringpower2.inOutGSAP 緩動(dòng)函數(shù)默認(rèn)值0.7與power2.inOut在兩個(gè)模式瀏覽器預(yù)覽、引擎確定性渲染和元數(shù)據(jù)寫入中共用同一組常量DEFAULT_DURATION/DEFAULT_EASE保證預(yù)覽里怎么動(dòng)、引擎 seek 時(shí)怎么動(dòng)、producer 讀取元數(shù)據(jù)時(shí)按什么參數(shù)合成三者完全一致。SHADER_NAMES導(dǎo)出全部著色器名字符串?dāng)?shù)組可用于參數(shù)校驗(yàn)或構(gòu)建下拉 UIimport { SHADER_NAMES } from hyperframes/shader-transitions; // [domain-warp, ridged-burn, whip-pan, ...]它在源碼中由注冊(cè)表對(duì)象的鍵推導(dǎo)而來ShaderName keyof typeof shaders查找未知名稱會(huì)拋出[HyperShader] Unknown shader: xxx. Available: ...見 registry.ts。4.1 可用著色器一覽著色器描述domain-warp基于噪聲的有機(jī)扭曲帶發(fā)光邊緣ridged-burn脊?fàn)顁idged噪聲灼燒帶火花與熱輝光whip-pan水平運(yùn)動(dòng)模糊模擬快速甩鏡sdf-iris圓形光圈擦除iris wipe帶發(fā)光環(huán)邊ripple-waves從中心輻射的同心漣漪扭曲gravitational-lens引力透鏡式扭曲帶色差cinematic-zoom徑向縮放模糊帶色邊chromatic-split從中心向外的 RGB 通道分離glitch數(shù)字故障塊狀位移 掃描線swirl-vortex基于噪聲扭曲的螺旋旋轉(zhuǎn)thermal-distortion從畫面底部升騰的熱浪擾動(dòng)flash-through-white閃白后揭示下一場景cross-warp-morph噪聲驅(qū)動(dòng)的雙場景形變混合light-leak暖色電影漏光 鏡頭光暈4.2 共享著色器基礎(chǔ)設(shè)施所有片元著色器并非憑空獨(dú)立而是拼接自公共頭部理解這一點(diǎn)有助于二次開發(fā)頂點(diǎn)著色器把a(bǔ)_pos-1..1的四邊形映射為v_uv并做 Y 軸翻轉(zhuǎn)適配 WebGL 坐標(biāo)系見 common.ts每個(gè)片元著色器頭部H常量統(tǒng)一聲明了這些 uniformu_from/u_to出境/入境兩張紋理、u_progress0→1 進(jìn)度、u_resolution分辨率、u_accent/u_accent_dark/u_accent_bright三檔強(qiáng)調(diào)色需要噪聲的著色器會(huì)拼入NQ常量——基于 hash 的 value noise 五次平滑插值 旋轉(zhuǎn)各向異性的 5 層 FBM。也就是說紋理對(duì)u_from、u_to 一個(gè)進(jìn)度標(biāo)量是所有轉(zhuǎn)場的通用數(shù)據(jù)契約gl_FragColor的寫法是按進(jìn)度/噪聲混合兩幀再疊加強(qiáng)調(diào)色效果。均勻值由 webgl.ts 的renderShader()統(tǒng)一寫入采樣器綁定 TEXTURE0/TEXTURE1進(jìn)度、分辨率、三檔顏色逐一uniform*程序與 uniform 位置都做了緩存以省去重復(fù)查詢。五、運(yùn)行架構(gòu)預(yù)捕獲 → 著色器合成 → GSAP 時(shí)間線瀏覽器預(yù)覽模式下一次轉(zhuǎn)場周期的數(shù)據(jù)流如下對(duì)應(yīng)init()后半段邏輯見 hyper-shader.ts初始化時(shí)按scenes.length transitions.length 1校驗(yàn)例如 3 個(gè)場景配 2 個(gè)轉(zhuǎn)場違反會(huì)直接拋錯(cuò)隨后逐個(gè)檢查場景 ID 存在于 DOM 中且?guī)?sceneclass兩類問題會(huì)分別拋出清晰錯(cuò)誤scene ids not found in DOM: .../elements found but missing .scene class: ...。為每個(gè)轉(zhuǎn)場預(yù)編譯對(duì)應(yīng)的 GLSL 程序。轉(zhuǎn)場前開始預(yù)捕獲對(duì)出境場景與入境場景按previewCaptureFps采樣動(dòng)畫幀。捕獲結(jié)果先以 PNG Blob 形式寫入 IndexedDB見第六節(jié)播放前按需把 Blob 解碼成ImageBitmap/Image再上傳為 WebGL 紋理。播放進(jìn)入轉(zhuǎn)場窗口時(shí)u_progress隨時(shí)間線推進(jìn)映射到duration與ease著色器每幀對(duì)兩張紋理做混合結(jié)果直接繪制到覆蓋在頁面上的gl-canvas。播放經(jīng)過轉(zhuǎn)場窗口后隱藏 GL 畫布露出持續(xù)推進(jìn)中的入境場景 DOM畫面無縫銜接。整個(gè)過程里init()返回的GsapTimeline才是組合時(shí)間線的真相來源——無論轉(zhuǎn)場是著色器合成還是 CSS 交叉淡入淡出都可以被暫停、seek、play與 HyperFrames 的既有播放體系兼容。5.1 WebGL 不可用與 CSS 交叉淡入淡出降級(jí)兩條獨(dú)立的降級(jí)路徑需要分清無 WebGL 環(huán)境createContext()返回空init()打印警告并直接返回普通時(shí)間線轉(zhuǎn)場退化為硬切場景動(dòng)畫完全正常。某項(xiàng)轉(zhuǎn)場未指定shadershader undefined該轉(zhuǎn)場以 CSS opacity 交叉淡入淡出執(zhí)行不需要 WebGL。在引擎渲染模式下這樣的條目會(huì)被安排成真實(shí)的 opacity tween詳見第七節(jié)保證單幀截圖里就包含正確的混合結(jié)果。六、場景捕獲管線從 html2canvas 到原生 HTML-in-Canvas把 DOM 場景變成紋理是整套方案最脆弱也最關(guān)鍵的部分。包支持兩條捕獲路徑策略代碼見 capture.ts。原生 HTML-in-Canvas首選當(dāng)瀏覽器暴露 Chrome 實(shí)驗(yàn)性的 CanvasDrawElement API 時(shí)使用layoutSubtreecanvas drawElementImage()直接繪制 DOM。實(shí)現(xiàn)細(xì)節(jié)包括把場景克隆進(jìn)一個(gè)position:fixed; z-index:-9999; opacity:0的 layoutsubtree canvas等待兩個(gè)requestAnimationFrame讓瀏覽器完成布局/繪制用bgColor填充底色再drawElementImage讀出畫面并復(fù)制到結(jié)果 canvas。該路徑失敗時(shí)自動(dòng)回退到 html2canvas。html2canvas 回退html2canvas抓取時(shí)為避免 Safari 的畫布污染SecurityError: The operation is insecure固定開啟useCORS: true與allowTaint: true。這里有一個(gè)值得注意的工程取舍t(yī)ainted canvas 無法被gl.texImage2D上傳WebGL 規(guī)范強(qiáng)制 SecurityError所以allowTaint的實(shí)際作用是把失敗點(diǎn)從 html2canvas 內(nèi)部挪到更可控的紋理上傳處由調(diào)用方統(tǒng)一兜底。此外還提供foreignObjectRendering嘗試開關(guān)失敗自動(dòng)回退到常規(guī)渲染、onclone中把帶 transform 的box-shadow抽取成獨(dú)立 shim 元素因?yàn)樽儞Q會(huì)破壞陰影柵格化、強(qiáng)制克隆場景可見等處理。用isHtmlInCanvasCaptureSupported()可以自行做特性檢測源碼判定存在layoutSubtree屬性 2D 上下文具備drawElementImage函數(shù)對(duì)應(yīng)測試見 capture.test.ts驗(yàn)證了非瀏覽器環(huán)境返回 false、能力齊備返回 true、缺drawElementImage返回 false 三種情況。另外捕獲對(duì)零尺寸 pattern 有個(gè) Safari 相關(guān)防御補(bǔ)丁重寫CanvasRenderingContext2D.prototype.createPattern當(dāng)傳入 0×0 的 canvas 時(shí)返回null而不是讓瀏覽器拋錯(cuò)initCapture()。6.1 瀏覽器預(yù)覽快照的 IndexedDB 緩存每次刷新頁面都重新捕獲幾十上百幀顯然不劃算因此瀏覽器預(yù)覽的快照會(huì)被持久化數(shù)據(jù)庫名為hyper-shader-preview-cacheobject store 名為framesschema 版本v1常量見 hyper-shader.ts。緩存鍵由composition ID、場景 DOM/樣式簽名、轉(zhuǎn)場時(shí)序、捕獲 FPS、縮放與畫布尺寸綜合推導(dǎo)。DOM/樣式簽名不是簡單 hash它基于文檔內(nèi)所有style文本、link relstylesheet及腳本特征的stableHash場景自身的簽名還會(huì)在計(jì)算前剔除播放過程中被運(yùn)行時(shí)改寫的opacity/visibility/pointer-events等內(nèi)聯(lián)樣式從而讓緩存身份追蹤作者寫的內(nèi)容而非上次預(yù)覽的播放頭狀態(tài)。刷新后若鍵匹配快照直接加載為 WebGL 紋理不再重捕。運(yùn)行中編輯場景或樣式表時(shí)只會(huì)把相鄰轉(zhuǎn)場的緩存標(biāo)記為 dirty重捕推遲到真正播放到那個(gè)轉(zhuǎn)場時(shí)才發(fā)生保證編輯器操作期間交互不卡頓。緩存總量上限為 1200 條MAX_SNAPSHOT_CACHE_ENTRIES寫入時(shí)按updatedAt淘汰最舊條目并清理當(dāng)前 composition 的失效鍵。6.2 預(yù)捕獲階段的加載反饋首次播放前的快照準(zhǔn)備可能需要一段時(shí)間包內(nèi)置了一套全屏加載反饋覆蓋層包含品牌圖形、進(jìn)度短語與逐 transition / 逐 frame 的進(jìn)度數(shù)字短語按進(jìn)度切換Preparing scene transitions、Sampling outgoing scene motion 等。該覆蓋層帶data-hyperframes-ignore、data-no-capture、data-no-pick等標(biāo)記確保它不會(huì)污染捕獲與拾取邏輯。播放器接管 vs 內(nèi)置加載 UI當(dāng)頁面由hyperframes-player承載時(shí)瀏覽器預(yù)覽的捕獲縮放與轉(zhuǎn)場預(yù)加載 UI 的所有權(quán)歸屬播放器對(duì)應(yīng)屬性shader-capture-scale、shader-loading而不是組合代碼非播放器的直接預(yù)覽則保留內(nèi)置的全保真加載兜底。實(shí)現(xiàn)上捕獲縮放系數(shù)讀取全局變量__HF_SHADER_CAPTURE_SCALE或查詢參數(shù)__hf_shader_capture_scale解析后鉗制在0.25 ~ 1默認(rèn) 1加載模式讀取__HF_SHADER_LOADING或__hf_shader_loading取值player/true→ 播放器接管、none/false/off→ 關(guān)閉、其余 →internal內(nèi)置覆蓋層。七、引擎渲染模式確定性逐幀輸出瀏覽器里人眼看 30fps 預(yù)捕獲足夠但視頻渲染引擎要求每一幀都精確確定。init()會(huì)探測window.__HF_VIRTUAL_TIME__標(biāo)記引擎在渲染模式注入的虛擬時(shí)間 shim見 hyper-shader.ts一旦檢測到就切換到initEngineMode()完全跳過所有 GL / canvas / html2canvas 分支只構(gòu)建一條確定性的透明度翻轉(zhuǎn)時(shí)間線非首場景初始全部opacity: 0用tl.set(..., 0)掛進(jìn)時(shí)間線開頭保證逆向 seek 也能恢復(fù)正確初態(tài)。對(duì)著色器轉(zhuǎn)場轉(zhuǎn)場窗口內(nèi)from/to 兩場景都保持opacity: 1出境場景在time duration時(shí)刻降到 0——這樣引擎的 Node 端分層合成器能分別獨(dú)立捕獲兩場景再自行混合。對(duì) CSS 交叉淡入淡出安排真實(shí)的 opacity tweenfromTo保證單幀頁面截圖本身已包含正確的混合結(jié)果。使用tl.set()零時(shí)長 tween而不是tl.call()因?yàn)閠l.call只在運(yùn)動(dòng)方向上觸發(fā)引擎 warmup 會(huì)正向 seek 到各轉(zhuǎn)場起點(diǎn)、隨后又反向 seek 回 t0回調(diào)態(tài)會(huì)卡住而set可隨反向 seek 正確還原。引擎讀取合成的依據(jù)是init()同步寫入window.__hf.transitions的元數(shù)據(jù)數(shù)組每項(xiàng)含time、duration、shader、ease、fromScene、toScene缺省 duration/ease 時(shí)同樣使用 0.7 /power2.inOut。該結(jié)構(gòu)刻意在包內(nèi)本地重聲明不 import engine 的類型以保持 CDN 獨(dú)立并與 engine 的HfTransitionMeta保持同步注釋中明確說明。7.1 可選的頁面端合成器engine-mode page compositing當(dāng) producer 以EngineConfig.enablePageSideCompositing: true啟動(dòng)并注入window.__HF_PAGE_SIDE_COMPOSITING__哨兵時(shí)引擎模式還會(huì)安裝一個(gè)頁面端 WebGL 合成器installPageSideCompositor()導(dǎo)出見 index.ts實(shí)現(xiàn)見 engineModePageComposite.ts讓單次整頁截圖也能得到與預(yù)覽路徑一致的原生保真捕獲。它采用兩階段協(xié)議Phase 1seek 包裝包裝window.__hf.seek。進(jìn)入轉(zhuǎn)場窗口時(shí)把 FROM/TO 場景克隆進(jìn)兩個(gè)常駐的 layoutsubtree staging canvas并設(shè)window.__hf_page_composite_pending。Paint force引擎?zhèn)纫鏅z測到 pending 標(biāo)記后觸發(fā)一次微型的Page.captureScreenshot強(qiáng)制瀏覽器合成器把 staging canvas 的克隆繪制出來。Phase 2resolve引擎調(diào)用window.__hf_page_composite_resolve用drawElementImage從已繪制克隆讀出畫面、上傳紋理、跑著色器并顯示 GL 覆蓋層最后清理 staging??寺r(shí)會(huì)把各自getBoundingClientRect()實(shí)測到的盒模型left/top/width/height固定到克隆上——這是為了規(guī)避僅靠inset:0定位的場景克隆進(jìn) layout subtree 后坍縮成 0×0的已知問題同時(shí)強(qiáng)制克隆可見、解碼 contenteditable="false">【免費(fèi)下載鏈接】hyperframesWrite HTML. Render video. Built for agents.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/hy/hyperframes創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考