Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
然CSS并不是一種很復(fù)雜的技術(shù),但就算你是一個(gè)使用CSS多年的高手,仍然會(huì)有很多CSS用法/屬性/屬性值你從來沒使用過,甚至從來沒聽說過。
對(duì)于CSS的color
屬性,相信所有Web開發(fā)人員都使用過。如果你并不是一個(gè)特別有經(jīng)驗(yàn)的程序員,我相信你未必知道color
屬性除了能用在文本顯示,還可以用作其它地方。
你可以先看一下下面的演示:
HTML代碼
<img alt="Example alt text" ><ul> <li>Example list item</li></ul><ol> <li>Example list item</li></ol><hr>
CSS代碼
body { color: yellow; background: #444; font-size: 20px; font-family: Arial, sans-serif; text-align: center;}ul { border: solid 2px; text-align: left;}ol { text-align: left;}hr { border-color: inherit;}
請(qǐng)注意,上面的代碼里只使用了一個(gè)color
屬性,就是在body
元素上,設(shè)置成了yellow
。但是,你也看到了,所有這個(gè)頁面上的東西都變成了黃色,包括:
無法顯示的圖片的alt
文字
list元素的邊框
無序list元素前面的小點(diǎn)
有序list元素前面的數(shù)字
還有hr
元素
有趣的是,這個(gè)hr
元素,缺省情況下它并不從body
上繼承color
的屬性,但我使用border-color: inherit
強(qiáng)制讓它繼承。這是個(gè)很詭異的特征。
在CSS規(guī)范里是這樣說的:
這個(gè)屬性聲明了元素文本內(nèi)容的前景色(foreground color)。除此之外,它的值還被用于其它地方間接的引用….比如,其它可以接受顏色值的屬性。
我無法想象出還有什么地方的屬性能用“前景色”來描述,如果你知道,請(qǐng)?jiān)谠u(píng)論里告訴我。
2.CSS里的visibility屬性有個(gè)collapse屬性值:collapse
對(duì)于CSS里的visibility
屬性,相信你用過不下幾百次。大多時(shí)候,你會(huì)把它的值設(shè)置成visible
(這是所有頁面元素的缺省值),或者是hidden
。后者相當(dāng)于display: none
,但仍然占用頁面空間。
其實(shí)visibility可以有第三種值,就是collapse。當(dāng)一個(gè)元素的visibility屬性被設(shè)置成collapse值后,對(duì)于一般的元素,它的表現(xiàn)跟hidden
是一樣的。但例外的是,如果這個(gè)元素是table相關(guān)的元素,例如table行,table group,table列,table column group,它的表現(xiàn)卻跟display: none
一樣,也就是說,它們占用的空間也會(huì)釋放。
但遺憾的是,各種瀏覽器對(duì)collapse
值的處理方式不一樣。看一下下面的演示:
HTML代碼
<table cellspacing="0" class="table"> <tr> <th>Fruits</th> <th>Vegetables</th> <th>Rocks</th> </tr> <tr> <td>Apple</td> <td>Celery</td> <td>Granite</td> </tr> <tr> <td>Orange</td> <td>Cabbage</td> <td>Flint</td> </tr></table><p><button>collapse行1</button></p><p><button>hide行1</button></p><p><button>重置</button></p>
CSS代碼
body { text-align: center; padding-top: 20px; font-family: Arial, sans-serif;}table { border-collapse: separate; border-spacing: 5px; border: solid 1px black; width: 500px; margin: 0 auto;}th, td { text-align: center; border: solid 1px black; padding: 10px;}.vc { visibility: collapse;}.vh { visibility: hidden;}button { margin-top: 5px;}
Javascript代碼
var btns=document.getElementsByTagName('button'), rows=document.getElementsByTagName('tr');btns[0].addEventListener('click', function () { rows[1].className='vc';}, false);btns[1].addEventListener('click', function () { rows[1].className='vh';}, false);btns[2].addEventListener('click', function () { rows[1].className='';}, false);
演示
CSS-Tricks的Almanac建議說不要使用這個(gè)值,因?yàn)闉g覽器的不統(tǒng)一。
據(jù)我的觀察:
在谷歌瀏覽器里,使用collapse
值和使用hidden
值沒有什么區(qū)別。 (See this bug report and comments)
在火狐瀏覽器、Opera和IE11里,使用collapse
值的效果就如它的字面意思:table的行會(huì)消失,它的下面一行會(huì)補(bǔ)充它的位置。
說實(shí)話,估計(jì)這個(gè)值很少人會(huì)使用它,但你要知道確實(shí)可以使用這樣的一個(gè)值,如果以前不知道它,那么,現(xiàn)在,在有些罕見的地方,你也許就會(huì)變得聰明一點(diǎn)了。
3.CSS的background簡寫方式里新增了新的屬性值
在CSS2.1里,background
屬性的簡寫方式包含五種屬性值 – background-color
, background-image
, background-repeat
, background-attachment
, and background-position
。從CSS3開始,又增加了3個(gè)新的屬性值,加起來一共8個(gè)。下面是按順序分別代表的意思:
background: [background-color] [background-image] [background-repeat] [background-attachment] [background-position] / [ background-size] [background-origin] [background-clip];
注意里面的反斜杠,它更font
和border-radius里簡寫方式使用的反斜杠的用法相似。反斜杠可以在支持這種寫法的瀏覽器里在position
后面接著寫background-size。除此之外,你開可以增加另外兩個(gè)描述它的屬性值: background-origin和 background-clip
.它的語法用起來像下面這個(gè)樣子:
.example { background: aquamarine url(img.png) no-repeat scroll center center / 50% content-box content-box;}
你可以用下面的演示檢測(cè)你的瀏覽器是否支持這種寫法:
關(guān)于瀏覽器的支持情況,大概所有的現(xiàn)代瀏覽器都支持這些新屬性值,但對(duì)于一些非常老舊的瀏覽器(IE6/7/8),最好在代碼里提供一些萬一不支持的補(bǔ)救機(jī)制。
4.CSS的clip屬性只在絕對(duì)定位的元素上才會(huì)生效
之前說到了background-clip
,你可能會(huì)想到clip
屬性。它的用法是下面這個(gè)樣子:
.example { clip: rect(110px, 160px, 170px, 60px);}
它的作用是按指定的尺寸、規(guī)定的大小裁剪元素。很多簡單,但唯一你需要注意的事情是,clip
只會(huì)在絕對(duì)定位的元素上生效。所有,你必須這樣做:
.example { position: absolute; clip: rect(110px, 160px, 170px, 60px);}
在下面的演示中,你可以看到當(dāng)元素在絕對(duì)定位/相對(duì)定位的切換中表現(xiàn)出來的效果:
但是,你也可以將元素的position
設(shè)置成position: fixed
,因?yàn)椋鶕?jù)css官方規(guī)范,fixed
的元素也屬于‘a(chǎn)bsolutely positioned’元素。
5.元素豎向的百分比設(shè)定是相對(duì)于容器的寬度,而不是高度
這是一個(gè)很讓人困惑的CSS特征,我之前也談到過它。我們大家都知道,當(dāng)按百分比設(shè)定一個(gè)元素的寬度時(shí),它是相對(duì)于父容器的寬度計(jì)算的,但是,對(duì)于一些表示豎向距離的屬性,例如padding-top
,padding-bottom
,margin-top
,margin-bottom
等,當(dāng)按百分比設(shè)定它們時(shí),依據(jù)的也是父容器的寬度,而不是高度。
下面是一個(gè)實(shí)例演示,你可以調(diào)整容器的寬度,但你會(huì)發(fā)現(xiàn),黃塊塊的padding-bottom
的距離也會(huì)隨之寬度而變大或變小。
HTML代碼
<div class="wrapper" id="w"> <div class="box" id="b"></div></div><input type="range" min="120" max="400" value="400" class="range" id="r"><output>寬度是: <span id="op">400px</span></output><output>黃塊塊的Padding bottom是:<br><span id="op2">10%</span></output>
CSS代碼
body { font-family: Arial, sans-serif; padding-top: 30px; text-align: center;}.wrapper { width: 400px; margin: 0 auto; border: solid 1px black;}.box { width: 100px; height: 100px; background: gold; margin-left: auto; margin-right: auto; padding-top: 10%; padding-bottom: 10%; margin-bottom: 5%;}.range { display: block; margin: 20px auto;}output { text-align: center; display: block; font-weight: bold; padding-bottom: 20px;}output span { font-weight: normal;}
實(shí)例演示
上面的代碼中,我們對(duì)內(nèi)部子元素聲明了3個(gè)豎向的距離,都是百分比形式。當(dāng)移動(dòng)滑塊時(shí),我們的js代碼只需修改了容器的寬度。但是,這個(gè)這三個(gè)屬性高度都跟隨著變化,可以看出,它們的百分比計(jì)算是基于容器的寬度,而不是高度的。
6.border屬性比你想象的要復(fù)雜
我們很多人都用過這樣的寫法:
.example { border: solid 1px black;}
這里的border
屬性的用法實(shí)際上是一種簡寫的形式,它分別設(shè)置了border-style
, border-width
, 和border-color
— 用一句代碼表示它們?nèi)齻€(gè)。
但不要忘記,border-style
, border-width
, 和border-color
也都有自己的簡寫形式。所以,border-width
可以寫成這樣:
.example { border-width: 2px 5px 1px 0;}
這樣,四個(gè)邊的寬度被一次設(shè)定。border-color
和 border-style
屬性也可以這樣做。下面的這個(gè)實(shí)例演示就是用的這種寫法:
HTML代碼
<div class="box">
CSS代碼
body { font-family: Arial, sans-serif;}.box { width: 150px; height: 150px; margin: 20px auto; border-color: peachpuff chartreuse thistle firebrick; border-style: solid dashed dotted double; border-width: 10px 3px 1px 8px;}p { text-align: center;}
演示
其實(shí),這些每個(gè)屬性還可以繼續(xù)細(xì)化,被拆分成border-left-style
, border-top-width
, border-bottom-color
….
但是,你無法用border
的簡寫分別對(duì)四個(gè)邊設(shè)置不同的值,只能分開來設(shè)置。所以,border
是一個(gè)簡寫里還有簡寫的屬性。
7.text-decoration屬性變成了屬性簡寫
我相信有些小知識(shí)會(huì)讓你大吃一驚。
跟著最新的CSS規(guī)范,text-decoration
現(xiàn)在的寫法是這樣的:
a { text-decoration: overline aqua wavy;}
text-decoration
屬性現(xiàn)在需要用三種屬性值來表示了:text-decoration-line
, text-decoration-color
, and text-decoration-style
.
但不幸的是,目前只有火狐瀏覽器實(shí)現(xiàn)了對(duì)這些新屬性的支持。
你可以用火狐瀏覽器試一試下面的演示:
HTML代碼
<a href="#" id="a">IT'S LIKE WATER, PEOPLE
(You should see a wavy line on top. Currently works only in Firefox)
CSS代碼
body { padding: 30px; text-align: center; font-family: Arial, sans-serif;}a, a:visited { color: blue; background: aqua; -moz-text-decoration-color: aqua; -moz-text-decoration-line: overline; -moz-text-decoration-style: wavy; text-decoration-color: aqua; text-decoration-line: overline; text-decoration-style: wavy;}
演示
在這演示中,我們沒有使用簡寫形式,而是分開描述每個(gè)屬性。這是可以更好的保證瀏覽器的向后兼容,使那些目前不支持這種寫法的瀏覽器也不受影響。
8.border-width屬性可以使用預(yù)定義常量值
也許對(duì)與你來說這并不是一個(gè)什么新鮮信息。除了可以使用標(biāo)準(zhǔn)寬度值(例如5px或1em)外,border-width
屬性可以接受預(yù)定義的常量值:medium
, thin
, 和 thick
事實(shí)上,如果你不給border-width
屬性賦值,那它的缺省值是“medium”。下面的演示就是用了預(yù)定義常量值:
HTML代碼
<div class="example">
CSS代碼
body { font-family: Arial, sans-serif; text-align: center;}.example { width: 100px; height: 100px; margin: 20px auto; background: aqua; border: solid thick red;}
演示
在瀏覽器使用這些預(yù)定義常量值時(shí),CSS規(guī)范里并沒有規(guī)定都應(yīng)該是什么寬度,但從我的觀察看,它們的值分別是 1px, 3px, 和 5px.
9.為什么沒有人使用border-image
之前我曾經(jīng)寫過一篇關(guān)于CSS的border-image
屬性的文章。現(xiàn)在幾乎所有的現(xiàn)代瀏覽器都支持這個(gè)屬性——除了IE10及以下IE版本。
看起來這是一個(gè)非常漂亮的CSS功能,它可以讓你用圖片修飾元素的邊框。下面是一個(gè)實(shí)例演示,你可以拖拽調(diào)整里面的方塊的大小,看看有什么邊框圖案的變化。
HTML代碼
<div class="bi"><p><上面的方塊使用了圖片描邊。在這個(gè)方塊的右下角,有一個(gè)可以調(diào)整這個(gè)方塊大小的小三角,點(diǎn)住它,拖動(dòng)它調(diào)整方塊大小,看看有什么效果。.</strong></p><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p></div>
CSS代碼
body { font-family: Arial, sans-serif; text-align: center;}.bi { border: 45px solid transparent; -webkit-border-image: url(http://www.webhek.com/wordpress/wp-content/uploads/2014/07/bg-pawprints-all.jpg) 45 round; -moz-border-image: url(http://www.webhek.com/wordpress/wp-content/uploads/2014/07/bg-pawprints-all.jpg) 45 round; border-image: url(http://www.webhek.com/wordpress/wp-content/uploads/2014/07/bg-pawprints-all.jpg) 45 round; font-family: Arial, Helvetica, sans-serif; color: #222; width: 500px; margin: 30px auto 30px auto; overflow: hidden; resize: both;}
演示
但不幸的是,這么好的一個(gè)功能,卻沒有看到多少人使用它,也許是我的眼界太窄。如果你在哪看到過有人使用border-image
屬性,或你在真正項(xiàng)目中使用了它,請(qǐng)留言告訴我。
10.你知道table里的empty-cells屬性嗎?
css里的empty-cells
屬性是所有瀏覽器都支持的,甚至包括IE8,它的用法是下面這個(gè)樣子:
table { empty-cells: hide;}
估計(jì)你從語義上已經(jīng)猜出它的作用了。它是為HTML table服務(wù)的。它會(huì)告訴瀏覽器,當(dāng)一個(gè)table單元格里沒有東西時(shí),就隱藏它。下面的演示里,你可以點(diǎn)擊里面按鈕,它會(huì)切換empty-cells
的屬性值,看看table的顯示有什么變化。
HTML代碼
<table cellspacing="0" id="table"> <tr> <th>Fruits</th> <th>Vegetables</th> <th>Rocks</th> </tr> <tr> <td></td> <td>Celery</td> <td>Granite</td> </tr> <tr> <td>Orange</td> <td></td> <td>Flint</td> </tr></table> <button id="b" data-ec="hide">切換EMPTY-CELLS</button>
CSS代碼
body { text-align: center; padding-top: 20px; font-family: Arial, sans-serif;}table { border: solid 1px black; border-collapse: separate; border-spacing: 5px; width: 500px; margin: 0 auto; empty-cells: hide; background: lightblue;}th, td { text-align: center; border: solid 1px black; padding: 10px;}button { margin-top: 20px;}
js代碼
var b=document.getElementById('b'), t=document.getElementById('table');b.onclick=function () { if (this.getAttribute('data-ec')==='hide') { t.style.emptyCells='show'; this.setAttribute('data-ec', 'show'); } else { t.style.emptyCells='hide'; this.setAttribute('data-ec', 'hide'); }};
演示
在上面的演示中,我為能讓單元格的邊框顯示出來,在單元格的邊框間添加了空隙。有時(shí)候這個(gè)屬性不會(huì)有任何視覺效果,因?yàn)槟惚仨氉屇憷锩嬗锌梢姷臇|西。
11.font-style的oblique屬性值
對(duì)與css的font-style
屬性,我估計(jì)大家每次見到的都是使用“normal”或 “italic”兩個(gè)屬性值。但事實(shí)上,你還可以讓它賦值為“oblique”。請(qǐng)看下面的演示:
HTML代碼
<h1>Oblique Text</h1><h1>Italic Text</h1>
CSS代碼
h1 { font-weight: normal; font-family: Georgia, serif; font-style: oblique; text-align: center; font-size: 50px;}h1:nth-child(2) { font-style: italic;}p { font-family: Arial, sans-serif; text-align: center;}
演示
這是什么意思?為什么“oblique”和斜體”italic”的效果是一樣的?
CSS規(guī)范中是這樣描述“oblique”的:
“…讓一種字體標(biāo)識(shí)為斜體(oblique),如果沒有這種格式,就使用italic字體。”
這里描述所用的“oblique”和“italic”都是傾斜的意思。“oblique”在維基百科里的解釋就是一種排版術(shù)語,就是一種傾斜的文字,但不是斜體。
因?yàn)椤皁blique”對(duì)于font-style
來說是一種合法的屬性值,它可和italic進(jìn)行互換,除非真有一種字體只提供了oblique體而沒有提供斜體。
但我似乎從來沒有聽說過哪種字形提供過oblique字體,也許我錯(cuò)了。研究發(fā)現(xiàn),一種字庫好像不能同時(shí)提供斜體和oblique兩種字體,因?yàn)閛blique基本上是一種模仿的斜體,而不是真正的斜體。
所以,如果我沒有猜錯(cuò)的話,如果一種字庫里沒有提供斜體字,那當(dāng)使用CSS的font-style: italic
時(shí),瀏覽器實(shí)際上是按font-style: oblique
顯示的。
12.word-wrap和overflow-wrap是等效的
word-wrap
并不是一個(gè)很常用的CSS屬性,但在特定的環(huán)境中確實(shí)非常有用的。我們經(jīng)常使用的一個(gè)例子是讓頁面中顯示一個(gè)長url時(shí)換行,而不是撐破頁面,下面是一個(gè)例子。
HTML代碼
<p class="p" id="p">supercalifragilisticexpialidocious</p><button id="b" data-ww="break-word">TOGGLE word-wrap</button>
CSS代碼
body { font-family: Arial, sans-serif; text-align: center;}.p { font-size: 24px; text-align: center; width: 200px; margin: 20px auto; border: solid 1px black; min-height: 60px; word-wrap: break-word;}button { display: block; margin: 0 auto;}
JS代碼
var p=document.getElementById('p'), b=document.getElementById('b');b.onclick=function () { if (this.getAttribute('data-ww')==='break-word') { p.style.wordWrap='normal'; this.setAttribute('data-ww', 'normal'); } else { p.style.wordWrap='break-word'; this.setAttribute('data-ww', 'break-word'); }};
演示
因?yàn)檫@個(gè)屬性最初是由微軟發(fā)明的,所以,所有的瀏覽器都支持這個(gè)屬性。
盡管有所有的瀏覽器都支持,但W3C決定要用overflow-wrap
替換word-wrap
,我想可能是他們認(rèn)為word-wrap
用詞不當(dāng)。overflow-wrap
跟word-wrap
具有相同的屬性值,但現(xiàn)在,word-wrap
被當(dāng)作overflow-wrap
的備選寫法。
雖然已經(jīng)有不少的瀏覽器支持overflow-wrap
這種寫法,但看起來沒必要使用overflow-wrap
來讓老的瀏覽器不支持。所有的瀏覽器都會(huì)繼續(xù)支持word-wrap
這種寫法。
這其中有多少是以前不知道的?
帶尬猴!
你是否在使用 Fabric.js 時(shí)希望能在選中元素后自定義元素樣式或選框(控制角和輔助線)的樣式?
如果是的話,可以放心往下讀。
本文將手把腳和你一起過一遍 Fabric.js 在對(duì)象元素選中后常用的樣式設(shè)置。
我將對(duì)象元素選中后的設(shè)置分成3類進(jìn)行講解:
創(chuàng)建一個(gè)畫布和一個(gè)圓形。為什么是圓形而不是矩形?等下你就知道了。
<canvas id="c" style="border: 1px solid #ccc;"></canvas>
<script>
const canvas=new fabric.Canvas('c', {
width: 400,
height: 400,
})
let circle=new fabric.Circle({
top: 80,
left: 80,
radius: 50,
fill: '#ffde7d' // 淡黃色
})
canvas.add(circle)
</script>
一個(gè)蛋黃出現(xiàn)了。上面這段代碼是 Fabric.js 的基礎(chǔ)。如果不太了解或者忘記語法了,可以查看 《Fabric.js 從入門到膨脹》。
我最近也在整理 Fabric.js 的常用方法,有興趣的可以看看 《Fabric.js中文教程》
本文所說的控制角和輔助邊請(qǐng)看下圖。翻譯能力有限,將就理解下吧~
控制角就是選中元素后周邊出現(xiàn)的幾個(gè)方形。
默認(rèn)情況下,控制角是空心的。也就是只有邊框,沒有填充色。
如果想要做成實(shí)心的控制角,只需將元素的 transparentCorners 屬性設(shè)置為 true 即可。
// 省略部分代碼
let circle=new fabric.Circle({
transparentCorners: false,
// 其他配置...
})
元素的 cornerColor 屬性可以控制控制角的顏色。
// 省略部分代碼
let circle=new fabric.Circle({
transparentCorners: false,
cornerColor: 'pink',
// 其他配置...
})
此時(shí)控制角的邊框顏色和填充顏色都變成了粉紅色。
如果你想單獨(dú)設(shè)置控制角的邊框顏色也行!要設(shè)置的屬性叫 cornerStrokeColor。
// 省略部分代碼
let circle=new fabric.Circle({
transparentCorners: false,
cornerColor: 'pink',
cornerStrokeColor: 'blue',
// 其他配置...
})
想修改控制角的大小,可以修改 cornerSize 的值。
// 省略部分代碼
let circle=new fabric.Circle({
cornerSize: 30,
// 其他配置...
})
和前面的例子對(duì)比,將 cornerSize 設(shè)置成 30 之后,控制角明顯大了很多。
控制角那幾個(gè)小把手的邊框是可以設(shè)置成虛線的。要調(diào)整的參數(shù)是 cornerDashArray ,該參數(shù)的值是一個(gè)數(shù)值型數(shù)組。
虛線的規(guī)則主要分以下幾種情況:
為了方便演示,我先將控制角的尺寸設(shè)置得大一點(diǎn)。
// 省略部分代碼
let circle=new fabric.Circle({
cornerSize: 30,
cornerDashArray: [4],
// 其他配置...
})
// 省略部分代碼
let circle=new fabric.Circle({
cornerSize: 30,
cornerDashArray: [4, 10],
// 其他配置...
})
// 省略部分代碼
let circle=new fabric.Circle({
cornerSize: 30,
cornerDashArray: [4, 10, 20],
// 其他配置...
})
控制角除了是正方形外,還可以將它設(shè)置成圓形。只需將 cornerStyle 設(shè)置為 circle 即可。
// 省略部分代碼
let circle=new fabric.Circle({
cornerStyle: 'circle',
// 其他配置...
})
前面說完控制角,接下來講講輔助邊
我們可以通過 selectionBackgroundColor 屬性設(shè)置輔助邊的顏色。
// 省略部分代碼
let circle=new fabric.Circle({
borderColor: 'red',
// 其他配置...
})
設(shè)置輔助邊粗細(xì)的屬性名叫 borderScaleFactor。
// 省略部分代碼
let circle=new fabric.Circle({
borderScaleFactor: 4,
// 其他配置...
})
設(shè)置輔助邊虛線規(guī)則使用的屬性是 borderDashArray。使用規(guī)則和 cornerDashArray 是一樣的。
// 省略部分代碼
let circle=new fabric.Circle({
borderDashArray: [10, 20, 30],
// 其他配置...
})
設(shè)置內(nèi)邊距的屬性是 padding,這名字和 css 的內(nèi)邊距是一樣的。
在 Fabric.js 中,給元素設(shè)置了內(nèi)邊距,會(huì)影響控制角和輔助邊到元素邊緣的距離。
padding 接受一個(gè)數(shù)值,不需要傳入單位。
// 省略部分代碼
let circle=new fabric.Circle({
padding: 20,
// 其他配置...
})
和前面的例子對(duì)比一下,將 padding 設(shè)置為 20 后,輔助邊和元素之間的距離明顯增大了。
這里所說的背景色和 css 里面的背景色不是同一回事。
本文要介紹 Fabric.js 的背景色有2種。一種是元素自身的背景色,另一種是選中后的背景色。
在 Fabric.js 里,背景色和填充色是兩回事。
填充色是基礎(chǔ),忘了的話可以查看 《Fabric.js 從入門到膨脹》 ,本文不再講解。
Fabric.js 是以矩形的方式去計(jì)算元素占位面積的,這也很好理解,比較方便嘛。所以使用 backgroundColor 設(shè)置背景顏色就能看到元素占據(jù)多大面積了。
// 省略部分代碼
let circle=new fabric.Circle({
fill: '#ffde7d',
backgroundColor: '#f6416c',
// 其他配置...
})
而 selectionBackgroundColor 屬性是設(shè)置元素選中后的背景色。
但需要注意,如果你同時(shí)設(shè)置了 backgroundColor 和 selectionBackgroundColor,重疊的部分 backgroundColor 的優(yōu)先級(jí)更高。
那什么地方才是不重疊的地方呢?那就是設(shè)置了 padding 的地方。
// 省略部分代碼
let circle=new fabric.Circle({
fill: '#ffde7d',
backgroundColor: '#f6416c',
padding: 20,
selectionBackgroundColor: '#00b8a9',
// 其他配置...
})
元素移動(dòng)時(shí)會(huì)先進(jìn)入選中狀態(tài)。此時(shí)會(huì)產(chǎn)生控制角和輔助線。
你可以使用 borderOpacityWhenMoving 設(shè)置控制角和輔助線的透明度。這個(gè)屬性接受 0 ~ 1 的值。
0 表示完全透明,1 表示完全不透明。
注意,borderOpacityWhenMoving 設(shè)置的是『移動(dòng)時(shí)』控制角和輔助邊的透明度。 重點(diǎn)詞是 『移動(dòng)時(shí)』。
// 省略部分代碼
let circle=new fabric.Circle({
borderOpacityWhenMoving: 0.1,
// 其他配置...
})
本例將 borderOpacityWhenMoving 設(shè)置為 0.1 ,所以移動(dòng)時(shí)就只能隱隱約約看到控制角和輔助邊了。
我把能否選中、局部控制操作等內(nèi)容放在“狀態(tài)”章節(jié)里。
如果你不希望元素被選中,可以將元素的 selectable 屬性設(shè)置為 false。
// 省略部分代碼
let circle=new fabric.Circle({
borderOpacityWhenMoving: 0.1,
// 其他配置...
})
如果圖形不是矩形,在選中元素后,輔助邊和圖形之間會(huì)有一個(gè)空白區(qū)。也就是前面用 backgroundColor 填充的那部分。
箭頭所指的4個(gè)地方都是空白區(qū)域。
默認(rèn)情況下,你可以點(diǎn)擊空白區(qū)選中或者拖拽圖形。
但如果你希望只能點(diǎn)擊圖形區(qū)域才能選中圖形的話,可以將圖形的 perPixelTargetFind 屬性設(shè)置為 true。
// 省略部分代碼
let circle=new fabric.Circle({
perPixelTargetFind: true,
// 其他配置...
})
可以通過 hasControls 屬性設(shè)置控制角的顯示和隱藏。
如果將 hasControls 設(shè)置為 false ,就會(huì)將控制角隱藏起來,你也就無法通過控制角去縮放和旋轉(zhuǎn)元素了。
// 省略部分代碼
let circle=new fabric.Circle({
hasControls: false,
// 其他配置...
})
同樣你也可以將輔助邊隱藏起來,只需將 hasBorders 屬性設(shè)置為 false 即可。
// 省略部分代碼
let circle=new fabric.Circle({
hasBorders: false,
// 其他配置...
})
前面將 hasControls 屬性設(shè)置為 false 后就可以隱藏所有控制角。
其實(shí) Fabric.js 還提供了2個(gè)方法可以單獨(dú)設(shè)置指定控制角的可見性:
這兩個(gè)方法的作用是一樣的,只是使用方式上有點(diǎn)不同。
需要注意的是,一旦把控制角隱藏起來,就意味著不能通過被隱藏的控制角去縮放和旋轉(zhuǎn)元素了。
在使用者兩個(gè)方法之前,你需要了解一堆屬性:tl, tr, br, bl, ml, mt, mr, mb, mtr,它們分別對(duì)應(yīng)9個(gè)控制點(diǎn),如下圖所示。
setControlsVisibility() 方法接收一個(gè)對(duì)象參數(shù),在這對(duì)象中可以描述要顯示或者隱藏哪些控制角。
比如我想把左上角和右下角隱藏。
// 省略部分代碼
let circle=new fabric.Circle({
// 其他配置...
})
circle.setControlsVisibility({
tl: false,
br: false
})
setControlVisible() 方法一次只能設(shè)置1個(gè)控制角的可見性,它接收2個(gè)參數(shù)。第一個(gè)參數(shù)是要操作的控制角,第二個(gè)參數(shù)是控制角的顯示狀態(tài)。
比如我想將左下角的控制角隱藏起來。
// 省略部分代碼
let circle=new fabric.Circle({
// 其他配置...
})
circle.setControlsVisibility('bl', false)
可以使用 isControlVisible(controlKey) 方法獲取控制角當(dāng)前的可見性。
// 省略部分代碼
let circle=new fabric.Circle({
// 其他配置...
})
circle.setControlsVisibility('bl', false)
console.log(circle.isControlVisible('bl')) // 返回 false
console.log(circle.isControlVisible('br')) // 返回 true
Fabric.js 還提供了2個(gè)方法可以捕捉到當(dāng)前被選中的對(duì)象。這2個(gè)方法分別叫 getActiveObject() 和 getActiveObjects() 。需要在 canvas 對(duì)象中調(diào)用的。
getActiveObject() 和 getActiveObjects() 從名字來看就已經(jīng)知道,末尾沒加 s 的就是返回當(dāng)前選中的元素;末尾加了 s 的就是返回當(dāng)前選中的所有元素(比如通過框選操作選擇了一堆元素)。
選中元素時(shí),getActiveObject() 會(huì)返回的當(dāng)前元素對(duì)象,而 getActiveObjects() 則返回一個(gè)數(shù)組集合。
沒選中元素時(shí),getActiveObject() 會(huì)返回 null,而 getActiveObjects() 會(huì)返回一個(gè)空數(shù)組。
可以通過這兩個(gè)方法獲取當(dāng)前選中的對(duì)象再做其他操作(比如修改填充顏色、描邊顏色、描邊粗細(xì)等)。
? Fabric.js 元素選中狀態(tài)的事件與樣式
我最近在整理 Fabric.js 常用方法,有興趣的可以看看 《Fabric.js中文教程》
《Fabric.js 拖放元素進(jìn)畫布》
《Fabric.js 限制邊框?qū)挾瓤s放》
《Fabric.js 監(jiān)聽元素相交(重疊)》
《Fabric.js 橡皮擦的用法(包含恢復(fù)功能)》
《Fabric.js 噴霧筆刷 從入門到放肆》
《Fabric.js 設(shè)置容器類名要注意這幾點(diǎn)》
點(diǎn)贊 + 關(guān)注 + 收藏=學(xué)會(huì)了
者|廖偉華(大貘)
出品|阿里巴巴新零售淘系技術(shù)部
導(dǎo)讀:給網(wǎng)站添加暗黑模式是隨著macOS中的暗黑模式(Dark Mode)出現(xiàn)之后的一個(gè)熱門話題。社區(qū)中有關(guān)于這方面的討論也很多,都在圍繞著怎么給網(wǎng)站添加暗黑模式。今天在這篇文章再次和大家一起聊聊這個(gè)已久的話題,不同的是,這篇文章將和大家從不同的角度來聊怎么給網(wǎng)站添加暗黑模式。感興趣的同學(xué),請(qǐng)繼續(xù)往下閱讀。
所謂的暗黑模式并不是現(xiàn)在才有的,這個(gè)事實(shí)已經(jīng)存在很久了。如果你很早就接觸過電腦的話,你可能會(huì)發(fā)現(xiàn)你使用過的電腦屏幕經(jīng)歷過好幾個(gè)過程,看起來會(huì)像下面這樣:
是不是覺得既熟悉又陌生。既然如此,為什么今年會(huì)成為設(shè)計(jì)或者說Web端的一個(gè)熱點(diǎn)呢?
其實(shí)這一切都應(yīng)該歸功于Apple公司,在macOS系統(tǒng)中提出了dark和light兩種視覺模式,即暗色(dark)和高亮(light)兩種皮膚,而且這兩種皮膚是系統(tǒng)級(jí)別的,我們可以通過系統(tǒng)上的切換,讓整個(gè)電腦上只要支持dark/light模式的應(yīng)用都可以輕易切換。
那么為什么要從系統(tǒng)級(jí)別去做這個(gè)事情呢?這是有原因的。系統(tǒng)面對(duì)的用戶群體中朋部分人士在身體上存有一定的缺陷,比如說色盲的用戶群體。也就是說,這種暗黑模式或者高亮模式對(duì)于有色盲的用戶群體是非常友好的。既然如此,為了讓自己的Web網(wǎng)站或者Web應(yīng)用能向系統(tǒng)級(jí)別靠齊,就有了網(wǎng)站級(jí)別的暗黑模式。
你可能在很多網(wǎng)站的右上角看到了一個(gè)提供暗黑和高亮模式的切換按鈕。
給Web網(wǎng)站或者Web應(yīng)用添加暗黑模式的基本原理我想大家應(yīng)該很清楚,事實(shí)上也非常的簡單。
正如上圖所示,給同一個(gè)Web網(wǎng)站或Web應(yīng)用提供多套皮膚,用戶根據(jù)自己的喜歡進(jìn)行選擇。那么給網(wǎng)站添加暗黑模式是同一個(gè)原理,就是給網(wǎng)站同時(shí)提供兩套皮膚,即theme1.css和theme2.css。
早期我們可能會(huì)借助于JavaScript腳本,根據(jù)用戶的選擇在一個(gè)<link />標(biāo)簽上進(jìn)行兩個(gè)主題文件(即.css文件)切換來實(shí)現(xiàn):
<!-- HTML -->
<link type="text/css" rel="stylesheet" media="all" href="../theme1.css" id="theme_css" />
// Script
document.getElementById('buttonID').addEventListener('click', function(){ document.getElementById('theme_css').href='../theme2.css';
})
這可能是一種比較古老的實(shí)現(xiàn)方案。也是大家最為熟悉的方案。
時(shí)至今日,給Web網(wǎng)站或Web應(yīng)用程序?qū)崿F(xiàn)暗黑模式已有多種模式。可以是純CSS的方式,也可以是CSS和JavaScript結(jié)合的模式。那么接下來,我們來看看具體的實(shí)現(xiàn)方式。
CSS有一個(gè)特別強(qiáng)大的特性,那就是媒體查詢@media,CSS的@media規(guī)則可以用于有條件地將樣式應(yīng)用于文檔以及其他各種上下文和語言,如HTML和JavaScript。在W3C的Media Queries Level 5【1】引入了“用戶首選媒體特性”,即Web網(wǎng)站或應(yīng)用程序檢測(cè)用戶顯示內(nèi)容的首先方式的方法。(https://drafts.csswg.org/mediaqueries-5/)
比如prefers-reduced-motion這個(gè)媒體查詢就可以檢測(cè)頁面上的動(dòng)畫,假設(shè)設(shè)備開啟了“Reduce motion”選項(xiàng),就可以通過該媒體查詢選項(xiàng)讓頁面上的元素是否具有動(dòng)效:
如果用戶開啟減少動(dòng)效的喜好,那么就不要在元素上使用動(dòng)效:
@media (prefers-reduced-motion: no-preference) { button { animation: vibrate 0.3s linear infinite both; }}
如果用戶沒有在系統(tǒng)級(jí)別設(shè)置該選項(xiàng)的話,可以像下面這樣讓按鈕有動(dòng)效:
@media (prefers-reduced-motion: no-preference) {
button {
animation: vibrate 0.3s linear infinite both;
}
}
Web上的其他具有動(dòng)效的元素都可以像上面之樣使用, 上面只是用button為例。
如果Web網(wǎng)站有很多元素具有動(dòng)效的話,還可以將所有與動(dòng)效相關(guān)的CSS放在一個(gè)獨(dú)立的文件中,然后通過link的media屬性來加載:
<link rel="stylesheet" href="animations.css" media="(prefers-reduced-motion: no-preference)" />
為了說明JavaScript如何控制preferences-reduced-motion。這里假設(shè)你在項(xiàng)目中使用了 Web Animation API【2】。當(dāng)用戶開啟了偏好設(shè)置,CSS規(guī)則會(huì)被瀏覽器動(dòng)態(tài)觸發(fā),這樣一來我五一需要自己監(jiān)聽變化,然后手動(dòng)停止與動(dòng)畫相關(guān)的東西:(https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API)
const mediaQuery=window.matchMedia('(prefers-reduced-motion: reduce)');
mediaQuery.addEventListener('change', ()=> {
console.log(mediaQuery.media, mediaQuery.matches);
// ...
});
如果你有強(qiáng)迫癥,強(qiáng)迫減少網(wǎng)站上所有有動(dòng)效停下來,還可以像下面這樣的簡單粗暴的操作:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.001s !important;
transition-duration: 0.001s !important;
}
}
有關(guān)于prefers-reduced-motion更詳細(xì)的介紹可以閱讀@Thomas Steiner的博文《 Move Ya! Or maybe, don't, if the user prefers-reduced-motion!》【3】。(https://developers.google.com/web/updates/2019/03/prefers-reduced-motion)
似乎上面的內(nèi)容偏離了我們今天要聊的主題,大家不用著急。只不過是拿prefers-reduced-motion這個(gè)媒體查詢來拋磚引玉而以。在CSS中通過媒體查詢的prefers-color-scheme特性和prefers-reduced-motion類似,不同是,該特性是用于檢測(cè)用戶是否要求頁面使用light還是dark主題。該媒體查詢常見的值有:
也就是說,通過prefers-color-scheme媒體查詢要讓暗黑模式(dark)開啟深色系主題,可以像下面這樣使用:
@media (prefers-color-scheme: dark) {
:root {
--background-color: #111416
--text-color: #ccc;
--link-color: #f96;
}
}
當(dāng)然在非dark模式下,你的樣式可能像下面這樣:
:root {
--background-color: #fff;
--text-color: #333;
--link-color: #b52;
}
body {
background-color: var(--background-color);
color: var(--text-color);
}
a {
color: var(--link-color);
}
注意上面提供的示例代碼僅僅是最基本的顏色配置方案,但也可以說完成了近90%的工作。但細(xì)節(jié)決定成敗。如果要讓你的Web網(wǎng)站或應(yīng)用程序在light和dark模式切換下能有較好的效果,還需要注意其他的一些細(xì)節(jié),比如說img、svg等元素的細(xì)節(jié)處理。有關(guān)于細(xì)節(jié)方面的,稍后我們會(huì)再討論,暫且不表。
正如上面的示例所示,我們是通過CSS的媒體查詢特性來檢測(cè)dark模式,即通過檢查媒體查詢是否首選。那么顏色方案是否匹配還需要檢查當(dāng)前瀏覽器是否支持dark模式。
我們可以像下面這樣來檢測(cè)瀏覽器是否支持dark模式:
if (window.matchMedia('(prefers-color-scheme)').media !=='not all') {
console.log('瀏覽器支持dark模式!(^_^)');
}
至于哪些瀏覽已支持prefers-color-scheme特性,我們可以通過Caniuse來查詢【4】:(https://caniuse.com/prefers-color-scheme/embed)
前面的示例簡單的向大家演示了如何給Web網(wǎng)站或應(yīng)用程序設(shè)置暗黑模式。但有很多細(xì)節(jié)我們需要去注意。
在一個(gè)應(yīng)用中只dark(暗色系)和light(亮色系)只能是二選一,永遠(yuǎn)不可能兩者共存。為什么要提這個(gè)呢?我們從加載策略來做衡量。如果我們不管三七二十一,直接將所有樣式(普通樣式、亮色系樣式和暗色系樣式)都用一個(gè).css文件加載的話會(huì)強(qiáng)迫用戶在關(guān)鍵的渲染路徑中下載CSS(包括你不想要的模式代碼也加載進(jìn)來了)。為此,為了優(yōu)化加載速度和給用戶提供更好的體驗(yàn),我們可以將CSS分成三個(gè)部分,以延遲非關(guān)鍵的CSS:
其中dark.css和light.css可以通過<link media="" />有條件的加載。加上并不是所有瀏覽器都已支持prefers-color-scheme特性,所以我們?cè)诩虞d通用樣式style.css規(guī)則的基礎(chǔ)上動(dòng)態(tài)默認(rèn)加載light.css。即,不支持該特性的瀏覽器會(huì)按下面的順序加載CSS:style.css ? light.css ? dark.css;如果支持該特性的瀏覽器則會(huì)按下面的順序加載CSS:style.css ? dark.css ? light.css。具體的代碼如下:
<!-- Script --><script> if (window.matchMedia('(prefers-color-scheme: dark)').media==='not all') { document.documentElement.style.display='none'; document.head.insertAdjacentHTML( 'beforeend', '<link rel="stylesheet" href="/light.css" onload="document.documentElement.style.display=\'\'">' ); }</script><!-- HTML --><link rel="stylesheet" href="/style.css"><link rel="stylesheet" href="/dark.css" media="(prefers-color-scheme: dark)"><link rel="stylesheet" href="/light.css" media="(prefers-color-scheme: no-preference), (prefers-color-scheme: light)">
按照該規(guī)則,前面的CSS示例代碼,我們就可以按下面這樣的文件來劃分:
// dark.css
:root {
--background-color: #111416
--text-color: #ccc;
--link-color: #f96;
}
// light.css
:root {
--background-color: #fff;
--text-color: #333;
--link-color: #b52;
}
// style.css
body {
background-color: var(--background-color);
color: var(--text-color);
}
a {
color: var(--link-color);
}
這里使用了CSS自定義屬性,該示例再次向大家演示了CSS自定義屬性的強(qiáng)大之處。
CSS Color Adjustment Module Level 1【5】提供了另一個(gè)新屬性color-scheme。該特性會(huì)告訴瀏覽器該應(yīng)用的顏色主題和允許用戶代理的特殊變體樣式表,而且它還可以讓W(xué)eb中的部分區(qū)域的渲染在dark和light之間切換,比如讓瀏覽器渲染渲染的表單域是個(gè)黑色背景和高亮文本。(https://drafts.csswg.org/css-color-adjust-1/)
來看一個(gè)簡單的示例代碼:
/* dark.css */
:root {
--color: rgb(250, 250, 250);
--background-color: rgb(5, 5, 5);
--link-color: rgb(0, 188, 212);
--main-headline-color: rgb(233, 30, 99);
--accent-background-color: rgb(0, 188, 212);
--accent-color: rgb(5, 5, 5);
}
/* light.css */
:root {
--color: rgb(5, 5, 5);
--background-color: rgb(250, 250, 250);
--link-color: rgb(0, 0, 238);
--main-headline-color: rgb(0, 0, 192);
--accent-background-color: rgb(0, 0, 238);
--accent-color: rgb(250, 250, 250);
}
/* style.css */
:root {
color-scheme: light dark;
}
body {
color: var(--color);
background-color: var(--background-color);
}
同樣的,還可以HTML的meta標(biāo)簽來設(shè)置:
<meta name="supported-color-schemes" content="light dark">
到目前為止,支持 color-scheme 的瀏覽器還較少【6】:(https://caniuse.com/#feat=mdn-html_elements_meta_name_color-scheme)
俗話說,百聞不如一見,這里向大家展示一個(gè)由@Thomas Steiner提供的案例【7】:(https://twitter.com/tomayac)
上面這個(gè)案例和以往提供的案例有所不同。該案例按前面所講的分成三個(gè)獨(dú)立的樣式文件:style.css、dark.css和light.css。嘗試切換暗黑模式并重新加載頁面,你會(huì)發(fā)現(xiàn)不匹配的樣式文件仍然會(huì)被加載,只是優(yōu)先級(jí)有所差異,這樣做它們就不會(huì)與站點(diǎn)當(dāng)前所需的資源競爭。
當(dāng)網(wǎng)站是在light模式下,樣式文件加載優(yōu)先級(jí)是style.css ? light.css ? dark.css,即 dark.css 權(quán)重最低(Lowest):
當(dāng)網(wǎng)站在dark模式下,樣式文件加載優(yōu)先級(jí)是style.css ? dark.css ? light.css,即 light.css權(quán)重最低(Lowest):
當(dāng)瀏覽器不支持prefers-color-scheme而且設(shè)置light為默認(rèn)模式,那么樣式加載優(yōu)先級(jí)會(huì)和高亮模式一樣:
特別聲明,上面三圖截圖來自于《Hello darkness, my old friend》【8】一文。(https://web.dev/prefers-color-scheme/)
上面示例還做了另一個(gè)細(xì)節(jié)上的優(yōu)化。和其他媒體查詢更改一樣,可以通過JavaScript的訂閱來更改暗黑模式。比如可以動(dòng)態(tài)更改頁面的favicon或更改<meta name="theme-color">來決定Chrome中URL欄的顏色。代碼并不復(fù)雜:
const darkModeMediaQuery=window.matchMedia('(prefers-color-scheme: dark)');darkModeMediaQuery.addListener((e)=> { const darkModeOn=e.matches; console.log(`Dark mode is ${darkModeOn ? 'on' : 'off'}.`);});
上面我們看到的都是原生CSS處理暗黑模式的技術(shù)方案。事實(shí)上我們還可以通過CSS Hack來實(shí)現(xiàn),采用CSS的filter和CSS的混合模式mix-blend-mode。
下面這個(gè)示例就是CSSfilter實(shí)現(xiàn)的暗黑模式:
Switch from light to dark mode using the toggle [CSS filter]@airenCodePen【9】(https://codepen.io/airen/pen/qzBzXB)
關(guān)鍵代碼很少:
.theme-dark { filter: invert(100) hue-rotate(180deg); } .theme-dark img { filter: invert(100) hue-rotate(180deg); }
另外還可以使用CSS的mix-blend-mode來實(shí)現(xiàn):
.dark-mode-screen {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
background: white;
mix-blend-mode: difference;
}
如果你想讓頁面中部分元素忽略mix-blend-mode:difference帶來的影響,可以使用isolation: isolate:
.twitter-logo,
.emoji {
isolation: isolate;
}
效果會(huì)類似下圖這樣:
有關(guān)于這方面的詳細(xì)介紹可以閱讀@thoughtspile的《How to create a dark theme without breaking things: learning with the Yandex Mail team》【10】和的@wgao19《Night Mode with Mix Blend Mode: Difference》【11】。(https://habr.com/en/company/yandex/blog/450032/)(https://dev.wgao19.cc/sun-moon-blending-mode/)
根據(jù)前面的內(nèi)容去操作,不管是使用CSS的媒體查詢prefers-color-scheme、新特性color-scheme還是借助CSS的濾鏡filter或混合模式mix-blend-mode都可以輕易的給Web網(wǎng)站或應(yīng)用程序添加暗黑模式。
注意,很多同學(xué)有一個(gè)小誤區(qū),認(rèn)為filter和mix-blend-mode只能用于圖片,事實(shí)上并非如此,他可以運(yùn)用于Web的各種元素上。
那么掌握了實(shí)現(xiàn)暗黑模式的技術(shù)方案就能做出好的效果嗎?并非如此,其中還是有很多細(xì)節(jié)需要我們注意。比如顏色的配置、Web媒體(圖片、Icon等)和可訪問性等方面的處理都值得我們?nèi)ネ魄谩?/p>
顏色的配置
在給Web網(wǎng)站或Web應(yīng)用設(shè)計(jì)暗黑模式的時(shí)候,你千萬不要鉆到死胡同里。暗黒模式并不僅僅是黑(black)與白(white)之間的切換。你想像一下,在一個(gè)深夜密不透光的地方,用你的肉眼注視著一塊高亮的屏幕,時(shí)間久了,你會(huì)有什么樣的一個(gè)感覺:
正確的做法是應(yīng)該為你的品牌色系提供一個(gè)暗色系版本,如果不奏效的話,可以根據(jù)需要在黑色和灰色之間選擇一個(gè)平均顏色。比如說,Web的背景顏色是black(#000)(或者接近#000)的話,建議你前景色(比如文本顏色)取值為rgb(250,250,250)(或者靠近這個(gè)顏色值)。這樣才能讓你的整體效果不至于亮瞎用戶的眼睛。比如下面這樣的一個(gè)效果:
如果你實(shí)在拿不準(zhǔn)配色是否合理(Web安全顏色),你可以借助在線工具,比如 Contrast Checker 【12】:(https://contrastchecker.com/)
該工具是根據(jù) WCAG 2.0 guidelines for contrast accessibility【13】 標(biāo)準(zhǔn)來做的。比如下面圖所展示的效果就是一個(gè)較好的效果:(https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html)
除了借助工具來檢測(cè)之外,瀏覽器的插件也是把利器,可以借助瀏覽器有關(guān)于Accessibility相關(guān)的插件來做檢測(cè),就比如小站,檢測(cè)出來的結(jié)果令人汗顏:
事實(shí)上,很多網(wǎng)站都存在這樣的缺陷:
圖片處理
在暗黑模式下,圖片的處理也是非常重要的。它們可能會(huì)直接影響用戶的體驗(yàn),太亮的圖像可能會(huì)讓用戶感到困惑和不舒服。而且有人做過這方面相應(yīng)的調(diào)查,大多數(shù)被調(diào)查的人在暗黑模式下更喜歡亮度低的圖像。比如下面這張圖:
左側(cè)是暗黑模式下效果,右側(cè)是在高亮模式下效果。
為了能更用戶更好的體驗(yàn),這里提倡在不同模式下給用戶展示不同效果的圖像,但并非說在不同的模式下引入不同的圖片。就目前CSS的技術(shù),我們?cè)谕粓D像源下可以很好的對(duì)圖像做處理。比如,粗暴一點(diǎn)的使用CSS的opacity,溫柔一點(diǎn)的使用CSS的混合模式mix-blend-mode(如果是背景的話則用background-blend-mode)或 filter。
// 粗暴模式
@media (prefers-color-scheme: dark) {
img {
opacity: 0.65;
}
img:hover {
opacity: 1;
}
}
// 溫柔模式
@media (prefers-color-scheme: dark) {
img {
filter: brightness(.8) contrast(1.2);
}
}
不過這里有一個(gè)細(xì)節(jié)需要注意,我們引入的圖像源有可能是.svg的矢量圖,如果希望給矢量圖(更多是Icon)一個(gè)不同于位圖(更多是圖像,照片)的重新著色處理。那么我們可以通過屬性選擇器和偽類選擇器將.svg過濾掉:
@media (prefers-color-scheme: dark) {
img:not([src*=".svg"]) {
filter: brightness(.8) contrast(1.2);
}
}
如果你希望給用戶更多的選擇的話,我們可以將CSS自定義屬性和JavaScript結(jié)合起來,可以讓用戶根據(jù)自己的喜好去做調(diào)節(jié)。這里還是拿圖片的處理為例吧:
// dark.css
:root {
--brightness: brightness(.8);
--contrast: contrast(1.2);
--image-filter: var(--brightness) var(--contrast);
}
// JS
document.documentElement.style.setProperty('--image-filter', value);
也可以參考下面這個(gè)Demo【14】,使用filter修改圖片效果:
(https://codepen.io/nitnelav/full/jgwRNJ)
CSS Filters@nitnelavCodePen【15】(https://codepen.io/nitnelav)
當(dāng)然,如果你是位追求極致的同學(xué),希望在暗黑模下給用戶提供最好的圖像,而不是隨便修改圖片的亮度或飽和度;但又不希望因加載圖片資源過多而影響整體的性能(甚至不希望因?yàn)樽约旱脑蜻^渡浪費(fèi)流量)。如果真的是這樣的話,你可以由設(shè)計(jì)師為暗黑模式下提供特定的圖片,然后與<picture>元素一起使用。可以在<picture>的<source>根據(jù)媒體屬性的設(shè)置加載所需要的圖片資源:
// dark.css
:root {
--brightness: brightness(.8);
--contrast: contrast(1.2);
--image-filter: var(--brightness) var(--contrast);
}
// JS
document.documentElement.style.setProperty('--image-filter', value);
暗黑模式下會(huì)加載dark.webp圖片,在高亮模式或者不支持prefers-color-scheme的瀏覽器中會(huì)加載light.webp圖片,不支持<picture>的瀏覽器會(huì)加載light.png圖片。你將看到的效果可能如下:
圖標(biāo)的處理
剛才提到過,很有可能你Web網(wǎng)站或Web應(yīng)用程序中有很多Icon圖標(biāo)用的是SVG圖標(biāo)。在暗黑模式下,同樣要對(duì)Icon圖標(biāo)做相應(yīng)的處理。這里來看兩種情景。
先來看第一種,那就是.svg文件和其他格式的圖像一相通過<img>標(biāo)簽引入。由于該Icon很有可能是純色的,因此在暗黑模式下,我們可以通過filter來做dark/light之間的切換:
/* dark.css */
:root {
--icon-filter: invert(100%);
--icon-filter_hover: invert(40%);
}
img[src*=".svg"] {
filter: var(--icon-filter);
}
/* light.css */
:root {
--icon-filter_hover: invert(60%);
}
/* style.css */
img[src*=".svg"]:hover {
filter: var(--icon-filter_hover);
}
如果你還想調(diào)整成其他的顏色,還可以像下面這個(gè) Demo 【16】來操作,增加filter的屬性值選項(xiàng):(https://codepen.io/airen/full/eaRBXq)
Change Icon Color with CSS Filter (Forked @ Cassie Evans)@airenCodePen【17】(https://codepen.io/airen/pen/eaRBXq)
該方法和處理圖像的方法是類似的。接下來我們?cè)賮砜吹诙N方式。使用的Icon圖標(biāo)很有可能是內(nèi)聯(lián)的SVG,針對(duì)這樣的場(chǎng)景,我們可以使用CSS的currentColor屬性。currentColor最大的特性就是可以根據(jù)color的值來決定元素的顏色,而對(duì)于SVG繪制的Icon圖標(biāo),主要由path、circle、rect這樣的元素構(gòu)成,這些元素可以通過fill、stroke來決定填充色和描邊色。換句話說,我們?cè)谑褂脙?nèi)聯(lián)SVG時(shí),將SVG中用到fill和stroke的屬性值都強(qiáng)制設(shè)置成currentColor,就像下面這樣:
<svg xmlns="http://www.w3.org/2000/svg"
viewbox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round"
>
<circle cx="12" cy="12" r="10"/>
<circle cx="12" cy="12" r="4"/>
<line x1="21.17" y1="8" x2="12" y2="8"/>
<line x1="3.95" y1="6.06" x2="8.54" y2="14"/>
<line x1="10.88" y1="21.94" x2="15.46" y2="14"/>
</svg>
另外在媒體查詢中設(shè)置:
@media (prefers-color-scheme: dark) { :root { --background-color: #111416 --text-color: #ccc; --link-color: #f96; } svg { color: var(--text-color) }}
如果你分成多個(gè)文件的話,可能會(huì)像下面這樣的:
@media (prefers-color-scheme: dark) {
:root {
--background-color: #111416
--text-color: #ccc;
--link-color: #f96;
}
svg {
color: var(--text-color)
}
}
讓切換有一個(gè)過渡效果
熟悉CSS的同學(xué)都應(yīng)該記得,CSS的transition可以讓元素在兩個(gè)狀態(tài)的切換過程中有一個(gè)平滑過渡的效果,以至于不會(huì)那么生硬:
而我們聊的dark/light兩模式之間的切換剛好穩(wěn)合transition。加上dark/light兩模式之間的切換就是color和background-color屬性值的切換。為了讓整個(gè)切換過程有一個(gè)過渡效果,我們可以把transition加上來。比如:
/* dark.css */
:root {
--color: rgb(250, 250, 250);
--background-color: rgb(5, 5, 5);
--link-color: rgb(0, 188, 212);
--main-headline-color: rgb(233, 30, 99);
--accent-background-color: rgb(0, 188, 212);
--accent-color: rgb(5, 5, 5);
}
/* light.css */
:root {
--color: rgb(5, 5, 5);
--background-color: rgb(250, 250, 250);
--link-color: rgb(0, 0, 238);
--main-headline-color: rgb(0, 0, 192);
--accent-background-color: rgb(0, 0, 238);
--accent-color: rgb(250, 250, 250);
}
/* style.css */
:root {
color-scheme: light dark;
}
body {
color: var(--color);
background-color: var(--background-color);
}
svg {
color: var(--color);
}
如果你不依任CSS,或者說希望讓自己的Web網(wǎng)站或Web應(yīng)用程序都具備dark/light模式的切換,那么可以通過JavaScript來實(shí)現(xiàn)。因?yàn)閐ark/light模式的切換說到底就是兩套主題的切換。當(dāng)然,你可以讓該JS的能力更為強(qiáng)大一些,不僅僅是對(duì).css文件的切換,粗暴簡單的實(shí)現(xiàn)網(wǎng)站換膚這樣的一個(gè)功能。或許你可以這樣做:
為了節(jié)約篇幅,這里就不把JavaScript代碼貼出來了,感興趣的話可以看看@Koos Looijesteijn的《 A guide to implementing dark modes on websites》【18】一文。文章中詳細(xì)的根據(jù)上面幾個(gè)過程,向大家展示了對(duì)應(yīng)的JavaScript代碼。如果你不愿閱讀文章的話,可以點(diǎn)擊鏈接直接閱讀源代碼【19】。(https://www.kooslooijesteijn.net/blog/add-dark-mode-to-website)(https://gist.github.com/kslstn/20f654fd27eb29619040c74fa6526919)
不過就我個(gè)人而言,我不太推薦使用JavaScript方案,這可能和我自己的信條有關(guān)系:
在Web端能用CSS實(shí)現(xiàn)的絕不借助JavaScript。
下面有一個(gè)簡單的示例:
Switch light / dark mode with prefers-color-scheme: dark@airenCodePen【20】(https://codepen.io/)
在該示例的頁面上沒有提供任何切換按鈕給用戶做選擇。主要目的是希望頁面能根據(jù)系統(tǒng)級(jí)別的設(shè)置來決定采用什么主題。假設(shè)你的系統(tǒng)默認(rèn)就開啟了暗黑模式。但你在瀏覽器看到的效果也不一定是dark模式下的效果。這主要是因?yàn)闉g覽器對(duì)prefers-color-scheme支持有一定的差異。不過我們可以對(duì)瀏覽器做一些設(shè)置,讓頁面能正常的跟著系統(tǒng)設(shè)置做出正確的渲染。
如果你使用Firefox,可以在地址欄中輸入about:config,然后鼠標(biāo)右鍵點(diǎn)擊選擇“新建(New)” → “整數(shù)(Integer)”,新建整數(shù)ui.systemUsesDarkTheme,并且將其值設(shè)置為1:
如果你使用的是Safari瀏覽器,可以使用它自帶的工具來查看效果:
最終在支持的瀏覽器下看到的效果如下:
除此之外,還可以給瀏覽器安裝插件。比如@CHRIS HOFFMAN在他的《 How to Enable Dark Mode for Google Chrome》【21】文章中就詳細(xì)的介紹了怎么在Chrome瀏覽器安裝Dark Reader【22】插件,讓W(xué)eb頁面具有暗黑模式瀏覽效果:(https://www.howtogeek.com/360650/how-to-enable-dark-mode-for-google-chrome/)(https://chrome.google.com/webstore/detail/dark-reader/eimadpbcbfnmbkopoojfekhnkhdbieeh)
上面我們通過不同的方式向大家闡述和演示了如何實(shí)現(xiàn)黑暗模式和高亮模式切換的解決方案。有粗暴簡單的方式,原始的切換樣式表的方式,還有采用一些新的CSS特性,比如CSS自定義屬性,新的媒體查詢特性,還有神奇的濾鏡和混合模式。而且這些解決方案中既有CSS和JavaScript的混合解決方案,也有純CSS的解決方案,甚至還有原生系統(tǒng)和瀏覽器通信的解決方案。還是那句老話,不管哪種解決方案或者技術(shù)手段,都有自己的利弊,沒有最好,只有最適合的使用場(chǎng)景。在實(shí)際使用的時(shí)候,應(yīng)該具體問題具體分析。
擴(kuò)展閱讀
如果你想更深入了解這方面的知識(shí),建議你花一些時(shí)間閱讀下面相關(guān)的教程:
https://web.dev/prefers-color-scheme/#vector-graphics-and-icons
https://ishadeed.com/article/dark-mode
https://adactio.com/journal/15941
https://www.kooslooijesteijn.net/blog/add-dark-mode-to-website
https://www.howtogeek.com/360650/how-to-enable-dark-mode-for-google-chrome/
https://css-tricks.com/dark-modes-with-css/
https://dev.wgao19.cc/2019-05-04__sun-moon-blending-mode/
https://kleinfreund.de/css-only-dark-mode/
https://www.kirupa.com/tricks/dealing_with_dark_mode.htm
https://kevinchen.co/blog/support-macos-mojave-dark-mode-on-websites/
https://dev.to/ananyaneogi/create-a-dark-light-mode-switch-with-css-variables-34l8
https://hacks.mozilla.org/2019/05/firefox-67-dark-mode-css-webrender/
https://habr.com/en/company/yandex/blog/450032/
更多技術(shù)干貨,關(guān)注「淘系技術(shù)」微信公眾號(hào)
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。