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

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

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

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

總結(jié)
以上所述是小編給大家介紹的CSS Houdini實(shí)現(xiàn)動(dòng)態(tài)波浪紋效果,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺(jué)得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!
相關(guān)文章

純CSS實(shí)現(xiàn)波浪移動(dòng)效果的示例
本篇文章主要介紹了純CSS實(shí)現(xiàn)波浪移動(dòng)效果的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-15
純css3制作鼠標(biāo)懸停波浪形狀彈性下拉菜單特效源碼
純css3制作鼠標(biāo)懸停波浪形狀彈性下拉菜單特效源碼,當(dāng)鼠標(biāo)懸停波浪形菜單欄,彈出列表信息,鼠標(biāo)離開(kāi)自動(dòng)收縮。效果非常逼真,本段代碼可以在各個(gè)網(wǎng)頁(yè)使用,有需要的朋友可2017-12-18
css3實(shí)現(xiàn)逼真的波浪起伏動(dòng)畫特效源碼
這是一款基于css3實(shí)現(xiàn)逼真的波浪起伏動(dòng)畫特效源碼。畫面上3層波浪涌動(dòng)構(gòu)成此起彼伏、連綿不斷的波浪涌動(dòng)視覺(jué)效果2017-07-27
CSS3實(shí)現(xiàn)的波浪閃動(dòng)文字動(dòng)畫特效源碼
CSS3實(shí)現(xiàn)的波浪閃動(dòng)文字動(dòng)畫特效源碼是一款炫酷文字動(dòng)畫特效,總共有4種效果,有波浪文字效果,文字閃動(dòng)效果等多種效果,同時(shí)實(shí)現(xiàn)的這四類效果中,都可以到處相應(yīng)的.css文2017-01-25
純css3實(shí)現(xiàn)的音階波浪loading加載動(dòng)畫特效源碼
這是一款采用純css3實(shí)現(xiàn)的音階波浪loading加載動(dòng)畫特效源碼??沙尸F(xiàn)出由中間向兩邊上下波動(dòng)的loading加載效果2016-12-27- 最近在做項(xiàng)目的時(shí)候,發(fā)現(xiàn)文字下方有個(gè)波浪線,尋思著,能不能用css來(lái)實(shí)現(xiàn),減少資源,遂參考一些資料,后來(lái)真的實(shí)現(xiàn)了。所以就有了這篇文章了,本文詳細(xì)的介紹了利用CSS32016-11-20

純css3實(shí)現(xiàn)的文字波浪動(dòng)畫特效源碼
這是一款采用純css3實(shí)現(xiàn)的文字波浪動(dòng)畫特效源碼,畫面上的文字呈現(xiàn)出帶有3D立體凹凸?jié)u變效果的波浪動(dòng)畫,該特效沒(méi)有引入任何外部圖形元素,且漸變效果流暢自然2016-05-28
我們分享過(guò)許多各種各樣的CSS3菜單,應(yīng)該說(shuō)效果都比傳統(tǒng)的CSS菜單強(qiáng)悍。這次要分享的這款CSS3菜單有點(diǎn)特別,菜單的整體形狀類似波浪形,鼠標(biāo)滑過(guò)菜單項(xiàng)時(shí)也會(huì)改變背景色表2014-10-18
純CSS3實(shí)現(xiàn)3D波浪形動(dòng)畫有波浪起伏的效果
一款3D波浪形動(dòng)畫特效。利用一堆div加上CSS3對(duì)每個(gè)div的控制2014-06-04









