CSS Houdini實現(xiàn)動態(tài)波浪紋效果
CSS Houdini 號稱 CSS 領(lǐng)域最令人振奮的革新。CSS 本身長期欠缺語法特性,可拓展性幾乎為零,并且新特性的支持效率太低,兼容性差。而 Houdini 直接將 CSS 的 API 暴露給開發(fā)者,以往完全黑盒的瀏覽器解析流開始對外開放,開發(fā)者可以自定義屬于自己的 CSS 屬性。
背景
我們知道,瀏覽器在渲染頁面時,首先會解析頁面的 HTML 和 CSS,生成渲染樹(rendering tree),再經(jīng)由布局(layout)和繪制(painting),呈現(xiàn)出整個頁面內(nèi)容。在 Houdini 出現(xiàn)之前,這個流程上我們能操作的空間少之甚少,尤其是 layout 和 painting 環(huán)節(jié),可以說是完全封閉,極大地限制了 CSS 的靈活性。社區(qū)中 sass、less、stylus 等 CSS 預(yù)處理技術(shù)的出現(xiàn)大多都源于這個原因,它們都希望通過預(yù)編譯,突破 CSS 的局限性,讓 CSS 擁有更強大的組織和編寫能力。所以慢慢地,我們都不再手寫 CSS,更方便、更靈活的 CSS 擴展語言成了 web 開發(fā)的主角。看到這樣的情況,CSS Houdini 終于坐不住了。
什么是 CSS Houdini?
CSS Houdini 對外開放了瀏覽器解析流程的一系列 API,這些 API 允許開發(fā)者介入瀏覽器的 CSS engine 運作,帶來了更多的 CSS 解決方案。

CSS Houdini 主要提供了以下幾個 API:
CSS Properties and Values API
允許在 CSS 中定義變量和使用變量,是目前兼容性最好的一個 API;
Layout API
允許開發(fā)者編寫自己的 Layout Module,自定義諸如 display 這類的布局屬性;
Painting API
允許開發(fā)者編寫自己的 Paint Module,自定義諸如 background-image 這類的繪制屬性。
基礎(chǔ):三步用上 Painting API
1、HTML 中通過 Worklets 載入樣式的自定義代碼:
<div class="rect"></div>
<script>
if ("paintWorklet" in CSS) {
CSS.paintWorklet.addModule("paintworklet.js");
}
</script>
Worklets 也是 Houdini 提供的 API 之一,負責(zé)加載和執(zhí)行樣式的自定義 JS 代碼。它類似于 Web Worker,是一個運行于主代碼之外的獨立工作進程,但比 Worker 更為輕量,負責(zé) CSS 渲染任務(wù)最為合適。
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 屬性類,當(dāng) rect 被使用時,會實例化 rect 并自動觸發(fā) paint 方法執(zhí)行渲染。paint 方法中,我們獲取節(jié)點 CSS 定義的 --rect-color 變量,并將元素的背景填充為指定顏色。ctx 參數(shù)是一個 Canvas 的 Context 對象,因此 paint 的邏輯跟 Canvas 的繪制方式一樣。
3、CSS 中使用的時候,只需要調(diào)用 paint 方法:
.rect {
width: 100vw;
height: 100vh;
background-image: paint(rect);
--rect-color: rgb(255, 64, 129);
}
這是一個自定義 CSS 背景色屬性的簡單實現(xiàn),看得出利用 CSS Houdini,我們可以像操作 canvas 一樣靈活自如地實現(xiàn)我們想要的樣式功能。
進階:實現(xiàn)動態(tài)波紋
根據(jù)上述步驟,我們演示一下如何用 CSS Painting API 實現(xiàn)一個動態(tài)波浪的效果:
<!-- 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.js
registerPaint('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 函數(shù)繪制波浪線,由于 AnimationWorklets 尚處于實驗階段,開放較少,這里我們在 worklet 外部用 requestAnimationFrame API 來做動畫驅(qū)動,讓波浪紋動起來。完成后能看到下邊這樣的效果。

然而事實上這個效果略顯僵硬,sin 函數(shù)太過于規(guī)則了,現(xiàn)實中的波浪應(yīng)該是不規(guī)則波動的,這種不規(guī)則主要體現(xiàn)在兩個方面:
1)波紋高度(Y)隨位置(X)變化而不規(guī)則變化

把圖按照 x-y 正交分解之后,我們希望的不規(guī)則,可以認為是固定某一時刻,隨著 x 軸變化,波紋高度 y 呈現(xiàn)不規(guī)則變化;
2)固定某點(X 固定),波紋高度(Y)隨時間推進而不規(guī)則變化
動態(tài)過程需要考慮時間維度,我們希望的不規(guī)則,還需要體現(xiàn)在時間的影響中,比如風(fēng)吹過的前一秒和后一秒,同一個位置的波浪高度肯定是不規(guī)則變化的。
提到不規(guī)則,有朋友可能想到了用 Math.random 方法,然而這里的不規(guī)則并不適合用隨機數(shù)來實現(xiàn),因為前后兩次取的隨機數(shù)是不連續(xù)的,而前后兩個點的波浪是連續(xù)的。這個不難理解,你見過長成鋸齒狀的波浪嗎?又或者你見過上一刻 10 米高、下一刻就掉到 2 米的波浪嗎?
為了實現(xiàn)這種連續(xù)不規(guī)則的特征,我們棄用 sin 函數(shù),引入了一個包 simplex-noise。由于影響波高的有兩個維度,位置 X 和時間 T,這里需要用到 noise2D 方法,它提前在一個三維的空間中,構(gòu)建了一個連續(xù)的不規(guī)則曲面:
// paintworklet.js
import 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();
}
})
修改峰值和偏置項等參數(shù),可以再畫多一個不一樣的波浪紋,效果如下,完工!

總結(jié)
以上所述是小編給大家介紹的CSS Houdini實現(xiàn)動態(tài)波浪紋效果,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
如果你覺得本文對你有幫助,歡迎轉(zhuǎn)載,煩請注明出處,謝謝!
相關(guān)文章
本篇文章主要介紹了純CSS實現(xiàn)波浪移動效果的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-15
純css3制作鼠標懸停波浪形狀彈性下拉菜單特效源碼,當(dāng)鼠標懸停波浪形菜單欄,彈出列表信息,鼠標離開自動收縮。效果非常逼真,本段代碼可以在各個網(wǎng)頁使用,有需要的朋友可2017-12-18
這是一款基于css3實現(xiàn)逼真的波浪起伏動畫特效源碼。畫面上3層波浪涌動構(gòu)成此起彼伏、連綿不斷的波浪涌動視覺效果2017-07-27
CSS3實現(xiàn)的波浪閃動文字動畫特效源碼是一款炫酷文字動畫特效,總共有4種效果,有波浪文字效果,文字閃動效果等多種效果,同時實現(xiàn)的這四類效果中,都可以到處相應(yīng)的.css文2017-01-25
純css3實現(xiàn)的音階波浪loading加載動畫特效源碼
這是一款采用純css3實現(xiàn)的音階波浪loading加載動畫特效源碼??沙尸F(xiàn)出由中間向兩邊上下波動的loading加載效果2016-12-27- 最近在做項目的時候,發(fā)現(xiàn)文字下方有個波浪線,尋思著,能不能用css來實現(xiàn),減少資源,遂參考一些資料,后來真的實現(xiàn)了。所以就有了這篇文章了,本文詳細的介紹了利用CSS32016-11-20
這是一款采用純css3實現(xiàn)的文字波浪動畫特效源碼,畫面上的文字呈現(xiàn)出帶有3D立體凹凸?jié)u變效果的波浪動畫,該特效沒有引入任何外部圖形元素,且漸變效果流暢自然2016-05-28
我們分享過許多各種各樣的CSS3菜單,應(yīng)該說效果都比傳統(tǒng)的CSS菜單強悍。這次要分享的這款CSS3菜單有點特別,菜單的整體形狀類似波浪形,鼠標滑過菜單項時也會改變背景色表2014-10-18
一款3D波浪形動畫特效。利用一堆div加上CSS3對每個div的控制2014-06-04









