消息称老熟妇乱视频一区二区 ,国产又色又爽又黄刺激在线视频,精品久久无码中文字幕,国产精品人人妻人人爽人人牛

歡迎來(lái)到海淘科技官網(wǎng) 官方微信 官方微博 平面活動(dòng)官網(wǎng)
微信

網(wǎng)絡(luò)傳播媒介服務(wù)提供商

熱線電話

021-62677988

海淘新聞
首頁(yè) > 新聞列表 > 建站基礎(chǔ)知識(shí):用CSS Houdini畫(huà)一片星空

建站基礎(chǔ)知識(shí):用CSS Houdini畫(huà)一片星空

發(fā)布時(shí)間: 2018-04-26 09:12

海淘科技與你分享《建站基礎(chǔ)知識(shí):用CSS Houdini畫(huà)一片星空 

要問(wèn)2018最讓人興奮的CSS技術(shù)是什么,CSS Houdini當(dāng)之無(wú)愧,甚至可以去掉2018這個(gè)限定。其實(shí)這個(gè)技術(shù)在2016年就出來(lái)了,但是在今年3月發(fā)布的Chrome 65才正式支持。

CSS Houdini可以做些什么?谷歌開(kāi)發(fā)者文檔列了幾個(gè)Demo,我們先來(lái)看一下這幾個(gè)Demo。

給textarea加一個(gè)方格背:


使用以下CSS代碼:



textarea {

background-image: paint(checkerboard);

}


給div添加一個(gè)鉆石形狀背景:


使用以下CSS:


div {

--top-width: 80;

--top-height: 20;

-webkit-mask-image: paint(demo);

}


點(diǎn)擊圓圈擴(kuò)散動(dòng)畫(huà):


這3個(gè)例子都是用了Houdini里面的CSS Paint API。

第1個(gè)例子如果使用傳統(tǒng)的CSS屬性,我們最多可能就是使用漸變來(lái)做顏色的變化,但是做不到這種一個(gè)格子一個(gè)格子的顏色變化的,而第2個(gè)例子也是沒(méi)有辦法直接用CSS畫(huà)個(gè)鉆石的形狀。這個(gè)時(shí)候你可能會(huì)想到會(huì)SVG/Canvas的方法,SVG和Canvas的特色是矢量路徑,可以畫(huà)出各種各樣的矢量圖形,而Canvas還能控制任意像素點(diǎn),所以用這兩種方式也是可以畫(huà)出來(lái)的。

但是Canvas和HTML相結(jié)合的時(shí)候就會(huì)顯得有點(diǎn)笨拙,就像第2個(gè)例子畫(huà)一個(gè)鉆石的形狀,用Canvas你需要利用類(lèi)似于BFC定位的方式,把Cavans調(diào)到合適的定位,還要注意z-index的覆蓋關(guān)系,而使用SVG可能會(huì)更簡(jiǎn)單一點(diǎn),可以設(shè)置background-image為一張鉆石的SVG圖片,但是無(wú)法像Canavas一樣很方便地做一些變量控制,例如隨時(shí)改一下鉆石邊框的顏色粗細(xì)等。

而第1個(gè)例子給textarea加格子背景,只能使用background-image 加SVG的方式,但是你不知道這個(gè)textarea有多大,SVG的格子需要準(zhǔn)備多少個(gè)呢?當(dāng)然你可能會(huì)說(shuō)誰(shuí)會(huì)給textarea加一個(gè)這樣的背景呢。但這只是一個(gè)示例,其它的場(chǎng)景可能也會(huì)遇到類(lèi)似的問(wèn)題。

第3個(gè)例子點(diǎn)擊圓圈擴(kuò)散動(dòng)畫(huà),這個(gè)也可以在div里面absolute定位一個(gè)canvas元素,但是我們又遇到另外一個(gè)問(wèn)題:無(wú)法很方便復(fù)用,假設(shè)這種圈圈擴(kuò)散效果在其它地方也要用到,那就得在每個(gè)地方都寫(xiě)一個(gè)canvas元素并初始化。

所以傳統(tǒng)的方式存在以下問(wèn)題:

需要調(diào)好和其它html元素的定位和z-index關(guān)系等

編輯框等不能方便地改背景,不能方便地做變量控制

不能方便地進(jìn)行復(fù)用

其實(shí)還有另外一個(gè)更重要的問(wèn)題就是性能問(wèn)題,用Cavans畫(huà)這種效果時(shí)需要自己控制好幀率,一不小心電腦CPU風(fēng)扇可能就要呼嘯起來(lái),特別是不能把握重繪的時(shí)機(jī),如果元素大小沒(méi)有變化是不需要重繪,如果元素被拉大了,那么需要進(jìn)行重繪,或者當(dāng)鼠標(biāo)hover的時(shí)候做動(dòng)畫(huà)才需要重繪。

CSS Houdini在解決這種自定義圖形圖像繪制的問(wèn)題提供了很好的解決方案,可以用Canvas畫(huà)一個(gè)你想要的圖形,然后注冊(cè)到CSS系統(tǒng)里面,就能在CSS屬性里面使用這個(gè)圖形了。以畫(huà)一個(gè)星空為例,一步步說(shuō)明這個(gè)過(guò)程。

畫(huà)一個(gè)黑夜的夜空

CSS Houdini只能工作在localhost域名或者是https的環(huán)境,否則的話相關(guān)API是不可見(jiàn)(undefined)的。如果沒(méi)有https環(huán)境的話,可以裝一個(gè)http-server的npm包,然后在本地啟動(dòng),訪問(wèn)localhost:8080就可以了,新建一個(gè)index.html,寫(xiě)入:



通過(guò)在JS調(diào)用CSS.paintWorklet.addModule注冊(cè)一個(gè)CSS圖形starry-sky,然后在CSS里面就可以使用這個(gè)圖形,寫(xiě)在background-image、border-image或者mask-image等屬性里面。如上面代碼的:


body {

background-image: paint(starry-sky);

}


注冊(cè)paint worket的時(shí)候需要給它一個(gè)獨(dú)立的JS,作為這個(gè)worklet的工作環(huán)境,這個(gè)環(huán)境里面是沒(méi)有window/document等對(duì)象的和Web Worker一樣。如果你不想寫(xiě)管理太多JS文件,可以借助blob,blob是可以存放任何類(lèi)型的數(shù)據(jù)的,包括JS文件。

Worklet需要的starry-sky.js的代碼如下所示:


class StarrySky {

paint (ctx, paintSize, properties) {

// 使用Canvas的API進(jìn)行繪制

ctx.fillRect(0, 0, paintSize.width, paintSize.height);

}

}


// 注冊(cè)這個(gè)屬性

registerPaint('starry-sky', StarrySky);

寫(xiě)一個(gè)類(lèi),實(shí)現(xiàn)paint接口,這個(gè)接口會(huì)傳一個(gè)canvas的context變量、當(dāng)前畫(huà)布的大小即當(dāng)前DOM元素的大小,以及當(dāng)前DOM元素的CSS屬性properties。

在paint函數(shù)里面調(diào)用canvas的繪制函數(shù)fillRect進(jìn)行填充,默認(rèn)填充色為黑色。訪問(wèn)index.html,就會(huì)看到整個(gè)頁(yè)面變成黑色了。我們的Hello World的CSS Houdini Painter就跑起來(lái)了,沒(méi)錯(cuò),就是這么簡(jiǎn)單。

但是有一點(diǎn)需要強(qiáng)調(diào)的是,瀏覽器實(shí)現(xiàn)并不是給那個(gè)DOM元素添加一個(gè)Canvas然后隱藏起來(lái),這個(gè)Paint Worket實(shí)際上是直接影響了當(dāng)前DOM元素重繪過(guò)程,相當(dāng)于我們給它添加了一個(gè)重繪的步驟,下文會(huì)繼續(xù)提及。

如果不想獨(dú)立寫(xiě)一個(gè)JS,用blob可以這樣:


let blobURL = URL.createObjectURL( new Blob([ '(',

function(){

class StarrySky {

paint (ctx, paintSize, properties) {

ctx.fillRect(0, 0, paintSize.width, paintSize.height);

}

}

registerPaint('starry-sky', StarrySky);

}.toString(),

')()' ], { type: 'application/javascript' } )

);

CSS.paintWorklet.addModule(blobURL);


畫(huà)星星

Cavans星星效果網(wǎng)上找一個(gè)就好了,例如這個(gè)Codepen,代碼如下:



paint (ctx, paintSize, poperties) {

let xMax= paintSize.width;

let yMax = paintSize.height;
// 黑色夜空

ctx.fillRect(0, 0, xMax, yMax);

// 星星的數(shù)量

let hmTimes = xMax + yMax;

for (let i = 0; i <= hmTimes; i++) {

// 星星的xy坐標(biāo),隨機(jī)

let x = Math.floor((Math.random() * xMax) + 1);

let y = Math.floor((Math.random() * yMax) + 1);

// 星星的大小

let size = Math.floor((Math.random() * 2) + 1);

// 星星的亮暗

let opacityOne = Math.floor((Math.random() * 9) + 1);

let opacityTwo = Math.floor((Math.random() * 9) + 1);

let hue = Math.floor((Math.random() * 360) + 1);

ctx.fillStyle = `hsla(${hue}, 30%, 80%, .${opacityOne + opacityTwo})`

效果如下:


為什么它要用fillRect來(lái)畫(huà)星星呢,星星不應(yīng)該是圓的么?因?yàn)槿绻胊rc的話性能會(huì)明顯降低。由于星星比較小,所以使用了這種方式,當(dāng)然改成arc也是可以的,因?yàn)槲覀冎皇钱?huà)一次就好了。

控制星星的密度

現(xiàn)在要做一個(gè)可配參數(shù)控制星星的密度,就好像border-radius可以控制一樣。借助CSS變量,給body添加一個(gè)自定義屬性--star-density:


body {

--star-density: 0.8;

background-image: paint(starry-sky);

}


規(guī)定密度系數(shù)從0到1變化,通過(guò)paint函數(shù)的properties參數(shù)獲取到屬性。但是我們發(fā)現(xiàn)body/html的自定義屬性無(wú)法獲取,可以繼承給body的子元素,但無(wú)法在body上獲取,所以改成畫(huà)在body:before上面:


body:before {

content: "";

position: absolute;

left: 0;

top: 0;

width: 100%;

height: 100%;

--star-density: 0.5;

background-image: paint(starry-sky);

}


然后給class StarrySky添加一個(gè)靜態(tài)方法:


class StarrySky {

static get inputProperties() {

return ['--star-density'];

}

}


告知我們需要獲取哪些CSS屬性,可以是自定義的,也可以是常規(guī)的CSS屬性。然后在paint方法的properties里面就可以拿到屬性值:


class StarrySky {

paint (ctx, paintSize, properties) {

// 獲取自定義屬性值

let starDensity = +properties.get('--star-density').toString() || 1;

// 最大只能為1

starDensity > 1 && (starDensity = 1);

// 星星的數(shù)量剩以這個(gè)系數(shù)

let hmTimes = Math.round((xMax + yMax) * starDensity);

}

}


讓星星的數(shù)量剩以傳進(jìn)來(lái)的系數(shù)進(jìn)而達(dá)控制密度的目的。上面設(shè)置星星的數(shù)量為最大值的一半,效果如下:


重繪

當(dāng)拉頁(yè)面的時(shí)候會(huì)發(fā)現(xiàn)所有星星的位置都發(fā)生了變化,這是因?yàn)橛|發(fā)了重繪。

在paint函數(shù)里面添加一個(gè)console.log,拉動(dòng)頁(yè)面的時(shí)候就可以觀察到瀏覽器在不斷地執(zhí)行paint函數(shù)。因?yàn)檫@個(gè)CSS屬性是寫(xiě)在body:befoer上面的,占滿(mǎn)了body,body大小改變就會(huì)觸發(fā)重繪。而如果寫(xiě)在一個(gè)寬度固定的div里面,拉動(dòng)頁(yè)面不會(huì)觸發(fā)重繪,觀察到paint函數(shù)沒(méi)有執(zhí)行。如果改了div或者body的任何一個(gè)CSS屬性也會(huì)觸發(fā)重繪。所以這個(gè)很方便,不需要我們自己去監(jiān)聽(tīng)resize之類(lèi)的DOM變化。

頁(yè)面拉大時(shí),右邊新拉出來(lái)的空間星星沒(méi)有畫(huà)大,所以本身需要重繪。而重繪給我們?cè)斐傻膯?wèn)題是星星的位置發(fā)生變化,正常情況下應(yīng)該是頁(yè)面拉大拉小,星星的位置應(yīng)該是要不變的。所以需要記錄一下星星的一些相關(guān)信息。

記錄星星的數(shù)據(jù)

可以在SkyStarry這個(gè)類(lèi)里面添加一個(gè)成員變量stars,保存所有star的信息,包括位置和透明度等,在paint的時(shí)候判斷一下stars的長(zhǎng)度,如果為0則進(jìn)行初始化,否則使用直接上一次初始化過(guò)的星星,這樣就能保證每次重繪都是用的同樣的星星了。但是在實(shí)際的操作過(guò)程中,發(fā)現(xiàn)一個(gè)問(wèn)題,它會(huì)初始化兩次starry-sky.js,在paint的時(shí)候也會(huì)隨機(jī)切換,如下圖所示:


這樣就造成了有兩個(gè)stars的數(shù)據(jù),在重繪過(guò)程中來(lái)回切換。原因可能是因?yàn)镃SS Houdini的本意并不想讓你保存實(shí)例數(shù)據(jù),但是既然它設(shè)計(jì)成一個(gè)類(lèi),使用類(lèi)的實(shí)例數(shù)據(jù)應(yīng)該也是合情合理的。這個(gè)問(wèn)題我想到的一個(gè)解決方法是把random函數(shù)變成可控的,只要隨機(jī)化種子一樣,那么生成的random系列就是一樣的,而這個(gè)隨機(jī)化種子由CSS變量傳進(jìn)來(lái)。所以就不能用Math.random了,自己實(shí)現(xiàn)一個(gè)random,如下代碼所示:


random () {

let x = Math.sin(this.seed++) * 10000;

return x - Math.floor(x);

}


只要初始化seed一樣,那么就會(huì)生成一樣的random系列。seed和星星密度類(lèi)似,由CSS變量控制:


body:before {

--starry-sky-seed: 1;

--star-density: 0.5;

background-image: paint(starry-sky);

}


然后在paint函數(shù)里面通過(guò)properties拿到seed:


paint (ctx, paintSize, properties) {

if (!this.stars) {

let starOpacity = +properties.get('--star-opacity').toString();

// 得到隨機(jī)化種子,可以不傳,默認(rèn)為0

this.seed = +(properties.get('--starry-sky-seed').toString() || 0);

this.addStars(paintSize.width, paintSize.height, starDensity);

}

}


通過(guò)addStars函數(shù)添加星星,這個(gè)函數(shù)調(diào)用上面自定義的random函數(shù):


random () {

let x = Math.sin(this.seed++) * 10000;

return x - Math.floor(x);

}

addStars (xMax, yMax, starDensity = 1) {

starDensity > 1 && (starDensity = 1);

// 星星的數(shù)量

let hmTimes = Math.round((xMax + yMax) * starDensity);

this.stars = new Array(hmTimes);

for (let i = 0; i < hmTimes; i++) {

this.stars[i] = {

x: Math.floor((this.random() * xMax) + 1),

y: Math.floor((this.random() * yMax) + 1),

size: Math.floor((this.random() * 2) + 1),

// 星星的亮暗

opacityOne: Math.floor((this.random() * 9) + 1),

opacityTwo: Math.floor((this.random() * 9) + 1),

hue: Math.floor((this.random() * 360) + 1)

};

}

}


這段代碼由Math.random改成this.random保證只要隨機(jī)化種子一樣,生成的所有數(shù)據(jù)也都是一樣的。這樣就能解決上面提到的初始化兩次數(shù)據(jù)的問(wèn)題,因?yàn)榉N子是一樣的,所以?xún)纱蔚臄?shù)據(jù)也是一樣的。

但是這樣有點(diǎn)單調(diào),每次刷新頁(yè)面星星都是固定的,少了點(diǎn)靈氣??梢越o這個(gè)隨機(jī)化種子做下優(yōu)化,例如實(shí)現(xiàn)單個(gè)小時(shí)內(nèi)是一樣的,過(guò)了一個(gè)小時(shí)后刷新頁(yè)面就會(huì)變。通過(guò)以下代碼可以實(shí)現(xiàn):


const ONE_HOUR = 36000 * 1000;

this.seed = +(properties.get('--starry-sky-seed').toString() || 0)

+ Date.now() / ONE_HOUR >> 0;


這樣拉動(dòng)頁(yè)面的時(shí)候星星就不會(huì)變了。

但是在從小拉大的時(shí)候,右邊會(huì)沒(méi)有星星:


因?yàn)榈谝淮蔚漠?huà)布沒(méi)那么大,以后又沒(méi)有更新星星的數(shù)據(jù),所以右邊就空了。

增量更新星星數(shù)據(jù)

不能全部更新星星的數(shù)據(jù),不然第4步就白做了。只能把右邊沒(méi)有的給它補(bǔ)上。所以需要記錄一下兩次畫(huà)布的大小,如果第二次的畫(huà)布大了,則增加星星,否則刪掉邊界外的星星。

所以需要有一個(gè)變量記錄上一次畫(huà)布的大小:


class StarrySky {

constructor () {

// 初始化

this.lastPaintSize = this.paintSize = {

width: 0,

height: 0

};

this.stars = [];

}

}


把相關(guān)的操作抽成一個(gè)函數(shù),包括從CSS變量獲取設(shè)置,增量更新星星等,這樣可以讓主邏輯變得清晰一點(diǎn):


paint (ctx, paintSize, properties) {

// 更新當(dāng)前paintSize

this.paintSize = paintSize;

// 獲取CSS變量設(shè)置,把密度、seed等存放到類(lèi)的實(shí)例數(shù)據(jù)

this.updateControl(properties);

// 增量更新星星

this.updateStars();

// 黑色夜空

for (let star of this.stars) {

// 畫(huà)星星,略

}

}


增量更新星星需要做兩個(gè)判斷,一個(gè)為是否需要?jiǎng)h除掉一些星星,另一個(gè)為是否需要添加,根據(jù)畫(huà)布的變化:


updateStars () {

// 如果當(dāng)前的畫(huà)布比上一次的要小,則刪掉一些星星

if (this.lastPaintSize.width > this.paintSize.width ||

this.lastPaintSize.height > this.paintSize.height) {

this.removeStars();

}

// 如果當(dāng)前畫(huà)布變大了,則增加一些星星

if (this.lastPaintSize.width < this.paintSize.width ||

this.lastPaintSize.height < this.paintSize.height) {

this.addStars();

}

this.lastPaintSize = this.paintSize;

}


刪除星星removeStar的實(shí)現(xiàn)很簡(jiǎn)單,只要判斷x, y坐標(biāo)是否在當(dāng)前畫(huà)布內(nèi),如果是的話則保留:


removeStars () {

let stars = []

for (let star of stars) {

if (star.x <= this.paintSize.width &&

star.y <= this.paintSize.height) {

stars.push(star);

}

}

this.stars = stars;

}


添加星星的實(shí)現(xiàn)也是類(lèi)似的道理,判斷x, y坐標(biāo)是否在上一次的畫(huà)布內(nèi),如果是的話則不添加:


addStars () {

let xMax = this.paintSize.width,

yMax = this.paintSize.height;

// 星星的數(shù)量

let hmTimes = Math.round((xMax + yMax) * this.starDensity);

for (let i = 0; i < hmTimes; i++) {

let x = Math.floor((this.random() * xMax) + 1),

y = Math.floor((this.random() * yMax) + 1);

// 如果星星落在上一次的畫(huà)布內(nèi),則跳過(guò)

if (x < this.lastPaintSize.width && y < this.lastPaintSize.height) {

continue;

}

this.stars.push({

x: x,

y: y,

size: Math.floor((this.random() * 2) + 1),

// 星星的亮暗

});

}

}


這樣當(dāng)拖動(dòng)頁(yè)面的時(shí)候就會(huì)觸發(fā)重繪,重繪的時(shí)候就會(huì)調(diào)paint更新星星。

讓星星閃起來(lái)

通過(guò)做星星透明度的動(dòng)畫(huà),可以讓星星閃起來(lái)。如果用Cavans標(biāo)簽,可以借助window.requestAnimationFrame注冊(cè)一個(gè)函數(shù),然后用當(dāng)前時(shí)間減掉開(kāi)始的時(shí)間模以一個(gè)值就得到當(dāng)前的透明度系數(shù)。使用Houdini也可以使用這種方式,區(qū)別是我們可以把動(dòng)態(tài)變化透明度系數(shù)當(dāng)作當(dāng)前元素的CSS變量或者叫自定義屬性,然后用JS動(dòng)態(tài)改變這個(gè)自定義屬性,就能夠觸發(fā)重繪,這個(gè)已在第3點(diǎn)重繪部分提到。

給元素添加一個(gè)--star-opacity的屬性:


body:before {

--star-opacity: 1;

--star-density: 0.5;

--starry-sky-seed: 1;

background-image: paint(starry-sky);

}


在星星的時(shí)候,每個(gè)星星的透明度再乘以這個(gè)系數(shù):


// 獲取透明度系數(shù)

this.starOpacity = +properties.get('--star-opacity').toString();

for (let star of this.stars) {

// 每個(gè)星星的透明度都乘以這個(gè)系數(shù)

let opacity = +('.' + (star.opacityOne + star.opacityTwo)) * this.starOpacity;

ctx.fillStyle = `hsla(${star.hue}, 30%, 80%, ${opacity})`;

ctx.fillRect(star.x, star.y, star.size, star.size);

}


然后在requestAnimationFrame動(dòng)態(tài)改變這個(gè)CSS屬性:


let start = Date.now();

// before無(wú)法獲取,所以需要改成正常元素

let node = document.querySelector('.starry-sky');

window.requestAnimationFrame(function changeOpacity () {

let now = Date.now();

// 每隔一1s,透明度從0.5變到1

node.style.setProperty('--star-opacity', (now - start) % 1000 / 2 + 0.5);

window.requestAnimationFrame(changeOpacity);

});


這樣就能重新觸發(fā)paint函數(shù)重新渲染了,但是這個(gè)效果其實(shí)是有問(wèn)題的,因?yàn)榈糜幸粋€(gè)alternate輪流交替的效果,即0.5變到1,再?gòu)?變到0.5,而不是每次都是0.5到1。 模擬CSS animation的alternate這個(gè)也好解決,可以規(guī)定奇數(shù)秒就是變大,而偶數(shù)秒就是變小,這個(gè)好實(shí)現(xiàn),略。

但實(shí)際上可以不用這么麻煩,因?yàn)楦淖僀SS屬性直接用animation就可以了,如下代碼所示:


body:before {

--star-opacity: 1;

--star-density: 0.5;

--starry-sky-seed: 1;

background-image: paint(starry-sky);

animation: shine 1s linear alternate infinite;

}

@keyframes shine {

from {

--star-opacity: 1;

}

to {

--star-opacity: 0.6;

}

}


這樣也能觸發(fā)重繪,但是我們發(fā)現(xiàn)它只有在from和to這兩個(gè)點(diǎn)觸發(fā)了重繪,沒(méi)有中間過(guò)渡的過(guò)程??梢酝茰y(cè)因?yàn)樗J(rèn)為--star-opacity的屬性值不是一個(gè)數(shù)字,而是一個(gè)字符串,所以這兩關(guān)鍵幀就沒(méi)有中間的過(guò)渡效果了。因此我們得告訴它這是一個(gè)整型,不是一個(gè)字符串。類(lèi)型化CSS對(duì)象模型(Typed CSSOM)提供了這個(gè)API。

類(lèi)型化CSS對(duì)象模型一個(gè)很大的作用就是把所有的CSS單位都用一個(gè)相應(yīng)的對(duì)象來(lái)表示,提供加減乘除等運(yùn)算,如:


// 10 px

let length = CSS.px(10);

// 在循環(huán)里面改length的值,不用自己去拼字符串

div.attributeStyleMap.set('width', length.add(CSS.px(1)))


這樣的好處是不用自己去拼字符串,另外還提供了轉(zhuǎn)換,如transform的值轉(zhuǎn)成matrix,度數(shù)轉(zhuǎn)成rad的形式等等。

它還提供了注冊(cè)自定義類(lèi)型屬性的能力,使用以下API:


CSS.registerProperty({

name: '--star-opacity',

// 指明它是一個(gè)數(shù)字類(lèi)型

syntax: '',

inherits: false,

initialValue: 1

});


這樣注冊(cè)之后,CSS系統(tǒng)就知道--star-opacity是一個(gè)number類(lèi)型,在關(guān)鍵幀動(dòng)畫(huà)里面就會(huì)有一個(gè)漸變的過(guò)渡效果。

類(lèi)型CSS對(duì)象模型在Chrome 66已經(jīng)正式支持,但是registerProperty API仍然沒(méi)有開(kāi)放,需要打開(kāi)chrome://flags,搜索web platform,從disabled改成enabled就可以使用。

這個(gè)給我們提供了做動(dòng)畫(huà)新思路,CSS animation 加 Canvas的模式,CSS animation負(fù)責(zé)改變屬性數(shù)據(jù)并觸發(fā)重繪,而Canvas去獲取動(dòng)態(tài)變化的數(shù)據(jù)更新視圖。所以它是一個(gè)數(shù)據(jù)驅(qū)動(dòng)的動(dòng)畫(huà)模式,這也是當(dāng)前做動(dòng)畫(huà)的一個(gè)流行方式。

在我們這個(gè)例子里面,由于星星數(shù)太多,1s有60幀,每幀都要計(jì)算和繪制1000個(gè)星星,CPU使用率達(dá)到90%多,所以這個(gè)性能有問(wèn)題,如果用Cavans標(biāo)簽可以使用雙緩沖技術(shù),CSS Houdini好像沒(méi)有這個(gè)東西。但是可以換一個(gè)思路,改成做整體的透明度動(dòng)畫(huà),不用每個(gè)星星都算一下。

如下代碼所示:


body {

background-color: #000;

}

body:before {

background-image: paint(starry-sky);

animation: shine 1s linear alternate infinite;

}

@keyframes shine {

from {

opacity: 1;

}

to {

opacity: 0.6;

}

}


這個(gè)的效果和每個(gè)星星都單獨(dú)算是一樣的,CPU消耗12%左右,這個(gè)應(yīng)該還是可以接受的。

效果如下圖所示:


如果用Canvas標(biāo)簽,可以設(shè)置globalAlpha全局透明度屬性,而使用CSS Houdini我們直接使用opacity就行了。

一個(gè)完整的Demo:CSS Houdini Starry Sky,需要使用Chrome,因?yàn)槟壳爸挥蠧hrome支持。

總的來(lái)說(shuō),CSS Houdini的Paint Worket提供了CSS和Canvas的粘合,讓我們可以用Canvas畫(huà)出想要的CSS效果,并借助CSS自定義屬性進(jìn)行控制,通過(guò)使用JS或者CSS的animation/transition改變自定義屬性的值觸發(fā)重繪,從而產(chǎn)生動(dòng)畫(huà)效果,這也是數(shù)據(jù)驅(qū)動(dòng)的開(kāi)發(fā)思想。并討論了在畫(huà)這個(gè)星空的過(guò)程中遇到的一些問(wèn)題,以及相關(guān)的解決方案。

本文只是介紹了CSS Houdini里面的Paint Worket和Typed CSSOM,它還有另外一個(gè)Layout Worklet,利用它可以自行實(shí)現(xiàn)一個(gè)flex布局或者其它自定義布局,這樣的好處是:一方面當(dāng)有新的布局出現(xiàn)的時(shí)候可以借助這個(gè)API進(jìn)行polyfill就不用擔(dān)心沒(méi)有實(shí)現(xiàn)的瀏覽器不兼容,另一方面可以發(fā)揮想象力實(shí)現(xiàn)自己想要的布局,這樣在布局上可能會(huì)百花齊放了,而不僅僅使用W3C給的那幾種布局。

綜上是上海網(wǎng)站建設(shè)公司——海淘科技與你分享的《建站基礎(chǔ)知識(shí):用CSS Houdini畫(huà)一片星空 》。更多建站基礎(chǔ)知識(shí)供你查閱,可直接點(diǎn)擊:建站基礎(chǔ)知識(shí)

相關(guān)文章:

版權(quán)所有 @ 2007-2023上海海淘信息科技有限公司 滬ICP備11050025號(hào)-4