CSS Houdini 號稱 CSS 領域最令人振奮的革新。CSS 本身長期欠缺語法特性,可拓展性幾乎為零,并且新特性的支持效率太低,兼容性差。而 Houdini 直接將 CSS 的 API 暴露給開發者,以往完全黑盒的瀏覽器解析流開始對外開放,開發者可以自定義屬于自己的 CSS 屬性。
背景
我們知道,瀏覽器在渲染頁面時,首先會解析頁面的 HTML 和 CSS,生成渲染樹(rendering tree),再經由布局(layout)和繪制(painting),呈現出整個頁面內容。在 Houdini 出現之前,這個流程上我們能操作的空間少之甚少,尤其是 layout 和 painting 環節,可以說是完全封閉,極大地限制了 CSS 的靈活性。社區中 sass、less、stylus 等 CSS 預處理技術的出現大多都源于這個原因,它們都希望通過預編譯,突破 CSS 的局限性,讓 CSS 擁有更強大的組織和編寫能力。所以慢慢地,我們都不再手寫 CSS,更方便、更靈活的 CSS 擴展語言成了 web 開發的主角。看到這樣的情況,CSS Houdini 終于坐不住了。
什么是 CSS Houdini?
CSS Houdini 對外開放了瀏覽器解析流程的一系列 API,這些 API 允許開發者介入瀏覽器的 CSS engine 運作,帶來了更多的 CSS 解決方案。
CSS Houdini 主要提供了以下幾個 API:
CSS Properties and Values API
允許在 CSS 中定義變量和使用變量,是目前兼容性最好的一個 API;
Layout API
允許開發者編寫自己的 Layout Module,自定義諸如 display 這類的布局屬性;
Painting API
允許開發者編寫自己的 Paint Module,自定義諸如 background-image 這類的繪制屬性。
基礎:三步用上 Painting API
1、HTML 中通過 Worklets 載入樣式的自定義代碼:
<div class="rect"></div><script> if ("paintWorklet" in CSS) { CSS.paintWorklet.addModule("paintworklet.js"); }</script>
Worklets 也是 Houdini 提供的 API 之一,負責加載和執行樣式的自定義 JS 代碼。它類似于 Web Worker,是一個運行于主代碼之外的獨立工作進程,但比 Worker 更為輕量,負責 CSS 渲染任務最為合適。
2、新建一個 paintworklet.js,利用 registerPaint 方法注冊一個 paint 類 rect,定義 paint 屬性的繪制邏輯:
registerPaint( "rect", class { static get inputProperties() { return ["--rect-color"]; } paint(ctx, geom, properties) { const color = properties.get("--rect-color")[0]; ctx.fillStyle = color; ctx.fillRect(0, 0, geom.width, geom.height); } });
上邊定義了一個名為 rect 的 paint 屬性類,當 rect 被使用時,會實例化 rect 并自動觸發 paint 方法執行渲染。paint 方法中,我們獲取節點 CSS 定義的 --rect-color 變量,并將元素的背景填充為指定顏色。ctx 參數是一個 Canvas 的 Context 對象,因此 paint 的邏輯跟 Canvas 的繪制方式一樣。
3、CSS 中使用的時候,只需要調用 paint 方法:
.rect { width: 100vw; height: 100vh; background-image: paint(rect); --rect-color: rgb(255, 64, 129);}
這是一個自定義 CSS 背景色屬性的簡單實現,看得出利用 CSS Houdini,我們可以像操作 canvas 一樣靈活自如地實現我們想要的樣式功能。
進階:實現動態波紋
根據上述步驟,我們演示一下如何用 CSS Painting API 實現一個動態波浪的效果:
<!-- index.html --><div id="wave"></div><style> #wave { width: 20%; height: 70vh; margin: 10vh auto; background-color: #ff3e81; background-image: paint(wave); }</style><script> if ("paintWorklet" in CSS) { CSS.paintWorklet.addModule("paintworklet.js"); const wave = document.querySelector("#wave"); let tick = 0; requestAnimationFrame(function raf(now) { tick += 1; wave.style.cssText = `--animation-tick: ${tick};`; requestAnimationFrame(raf); }); }</script>// paintworklet.jsregisterPaint('wave', class { static get inputProperties() { return ['--animation-tick']; } paint(ctx, geom, properties) { let tick = Number(properties.get('--animation-tick')); const { width, height } = geom; const initY = height * 0.4; tick = tick * 2; ctx.beginPath(); ctx.moveTo(0, initY + Math.sin(tick / 20) * 10); for (let i = 1; i <= width; i++) { ctx.lineTo(i, initY + Math.sin((i + tick) / 20) * 10); } ctx.lineTo(width, height); ctx.lineTo(0, height); ctx.lineTo(0, initY + Math.sin(tick / 20) * 10); ctx.closePath(); ctx.fillStyle = 'rgba(255, 255, 255, 0.5)'; ctx.fill(); }})
paintworklet 中,利用 sin 函數繪制波浪線,由于 AnimationWorklets 尚處于實驗階段,開放較少,這里我們在 worklet 外部用 requestAnimationFrame API 來做動畫驅動,讓波浪紋動起來。完成后能看到下邊這樣的效果。
然而事實上這個效果略顯僵硬,sin 函數太過于規則了,現實中的波浪應該是不規則波動的,這種不規則主要體現在兩個方面:
1)波紋高度(Y)隨位置(X)變化而不規則變化
把圖按照 x-y 正交分解之后,我們希望的不規則,可以認為是固定某一時刻,隨著 x 軸變化,波紋高度 y 呈現不規則變化;
2)固定某點(X 固定),波紋高度(Y)隨時間推進而不規則變化
動態過程需要考慮時間維度,我們希望的不規則,還需要體現在時間的影響中,比如風吹過的前一秒和后一秒,同一個位置的波浪高度肯定是不規則變化的。
提到不規則,有朋友可能想到了用 Math.random 方法,然而這里的不規則并不適合用隨機數來實現,因為前后兩次取的隨機數是不連續的,而前后兩個點的波浪是連續的。這個不難理解,你見過長成鋸齒狀的波浪嗎?又或者你見過上一刻 10 米高、下一刻就掉到 2 米的波浪嗎?
為了實現這種連續不規則的特征,我們棄用 sin 函數,引入了一個包 simplex-noise。由于影響波高的有兩個維度,位置 X 和時間 T,這里需要用到 noise2D 方法,它提前在一個三維的空間中,構建了一個連續的不規則曲面:
// paintworklet.jsimport SimplexNoise from 'simplex-noise';const sim = new SimplexNoise(() => 1);registerPaint('wave', class { static get inputProperties() { return ['--animation-tick']; } paint(ctx, geom, properties) { const tick = Number(properties.get('--animation-tick')); this.drawWave(ctx, geom, 'rgba(255, 255, 255, 0.4)', 0.004, tick, 15, 0.4); this.drawWave(ctx, geom, 'rgba(255, 255, 255, 0.5)', 0.006, tick, 12, 0.4); } /** * 繪制波紋 */ drawWave(ctx, geom, fillColor, ratio, tick, amp, ih) { const { width, height } = geom; const initY = height * ih; const speedT = tick * ratio; ctx.beginPath(); for (let x = 0, speedX = 0; x <= width; x++) { speedX += ratio * 1; var y = initY + sim.noise2D(speedX, speedT) * amp; ctx[x === 0 ? 'moveTo' : 'lineTo'](x, y); } ctx.lineTo(width, height); ctx.lineTo(0, height); ctx.lineTo(0, initY + sim.noise2D(0, speedT) * amp); ctx.closePath(); ctx.fillStyle = fillColor; ctx.fill(); }})
修改峰值和偏置項等參數,可以再畫多一個不一樣的波浪紋,效果如下,完工!
總結
以上所述是小編給大家介紹的CSS Houdini實現動態波浪紋效果,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!
新聞熱點
疑難解答