《CSS揭秘》閱讀筆記

我還記得國外某位大牛在一篇文章中寫道,CSS is fine, it's just really hard。讀完他的那篇文章,我是不信的,CSS會有多難,不過就是大量屬性堆疊在一起罷了。然而等我切切實實地把寫一個又一個頁面的時候,我充分理解到了作者通過'really hard' 想要表達的心酸。相比于一門真正的編程語言(比如JAVA、JavaScript等),CSS以其復雜龐大的細節深深征服了我。我想我應該更加深入具體的去了解它,以至于更加得心應手的使用它。

《CSS揭秘》是圖靈讀者群里某位小伙伴推薦給我的,我對這本書充滿了期待。

第一章 CSS編碼技巧

盡量減少代碼重復
1.在實踐中,代碼可維護性的最大要素是盡量減少改動時要編輯的地方。
2.當某些值相互依賴時,應該把它們的相互關系用代碼表達出來。
3.代碼易維護和代碼量少不可兼得。
4.我們還得到了一個特殊的顏色關鍵字 currentColor ,它是從SVG 那里借鑒來的。這個關鍵字并沒有綁定到一個固定的顏色值,而是一直被解析為 color 。實際上,這個特性讓它成為了 CSS 中有史以來的第一個變量 。雖然功能很有限,但它真的是個變量。
5.inherit 可以用在任何 CSS 屬性中,而且它總是綁定到父元素的計算值(對偽元素來說,則會取生成該偽元素的宿主元素)。同樣在有時候對于一些用于布局的元素可以使用 padding來將元素大小撐開,而不用顯式地設置 width和 height。

traingle { position: relative; }
triangle:before{
    content: "";
    position: absolute;
    top: -.4em; left: 1em;
    padding: .35em;/*這里使用了padding使得元素具有了大小*/
    background: inherit;
    border: inherit;
    border-right: 0;
    border-bottom: 0;
    transform: rotate(45deg);
}

相信你的眼睛而不是數字

人的眼睛并不是一臺完美的輸入設備。有時候精準的尺度看起來并不精準,而我們的設計需要順應這種偏差。舉一個在視覺設計領域廣為人知的例子吧,我們的眼睛在看到一個完美垂直居中的物體時,會感覺它并不居中。實際上,我們應該把這個物體從幾何學的中心點再稍微向上挪一點,才能取得理想的視覺效果。來親身體驗一下這件怪事吧(參見圖 1-9)。

與此類似,在字體設計領域廣為人知的是,圓形的字形(比如 0)矩形字形相比,需要稍微放大一些,因為我們傾向于把圓形感知得比其實際尺寸更小一些。

這些視覺上的錯覺在任何形式的視覺設計中都普遍存在,需要我們有針對性地進行調整。一個非常常見的例子是給一個文本容器設置內邊距。不論內容文本有多長,是一個單詞還是幾個段落,這個問題都會出現。假如我們給容器的四邊指定相同的內邊距,則實際效果看起來并不相等,就像圖 1-11顯示的那樣。原因在于,字母的形狀在兩端都比較整齊,而頂部和底部則往往參差不齊,從而導致你的眼睛把這些參差不齊的空缺部分感知為多出來的內邊距。因此,如果我們希望四邊的內邊距看起來是基本一致的,就需要減少頂部和底部的內邊距。你可以在圖 1-12 中看出這種差異。

響應式設計

媒體查詢不能以一種連續的方式來修復問題。它們的工作原理基于某幾個特定的階梯(亦稱“斷點”),如果大部分樣式代碼并不是以彈性的方式來編寫的,那么媒體查詢能做的只是修補某個特定分辨率下的特定問題——這本質上只是把灰塵掃到地毯下面而已。媒體查詢的斷點不應該由具體的設備來決定,而應該根據設計自身來決定。

提示
不妨考慮在你的媒體查詢中使用 em 單位取代像素單位。這能讓文本縮放在必要時觸發布局的變化。

一下是對響應式設計的一些建議:
1.使用百分比長度來取代固定長度。如果實在做不到這一點,也應該
嘗試使用與視口相關的單位( vw 、 vh 、 vmin 和 vmax ),它們的值解
析為視口寬度或高度的百分比。
2.當你需要在較大分辨率下得到固定寬度時,使用 max-width (會覆蓋width,同時本身會被min-width覆蓋)而不是width ,因為它可以適應較小的分辨率(在較小的分辨率下,使用max-width可以避免出現滾動條),而無需使用媒體查詢。
3.不要忘記為替換元素(比如 img 、 object 、 video 、 iframe 等)設置一個 max-width ,值為 100% 。
4.假如背景圖片需要完整地鋪滿一個容器,不管容器的尺寸如何變化,background-size: cover 這個屬性都可以做到。但是,我們也要時刻牢記——帶寬并不是無限的,因此在移動網頁中通過 CSS 把一張大圖縮小顯示往往是不太明智的。
5.當圖片(或其他元素)以行列式進行布局時,讓視口的寬度來決定列的數量。彈性盒布局(即 Flexbox)或者 display: inline-block
加上常規的文本折行行為,都可以實現這一點。
6.在使用多列文本時,指定 column-width (列寬)而不是指定
column-count (列數),這樣它就可以在較小的屏幕上自動顯示為單列布局。

總的來說,我們的思路是盡最大努力實現彈性可伸縮的布局,并在媒體查詢的各個斷點區間內指定相應的尺寸。

合理使用代碼簡寫

合理使用簡寫是一種良好的防衛性編碼方式,可以抵御未來的風險。當然,如果我們要明確地去覆蓋某個具體的展開式屬性并保留其他相關樣式,那就需要用展開式屬性。

但我們應該注意到,在同時使用簡寫屬性和展開式屬性時,我們應該將簡寫屬性寫在展開式屬性的前面,特別是當你不確定簡寫屬性是否包含這個展開式屬性的時候,以防止展開式屬性被簡寫屬性覆蓋。

    background: url(tr.png) no-repeat top right / 2em 2em,
    url(br.png) no-repeat bottom right / 2em 2em,
    url(bl.png) no-repeat bottom left / 2em 2em;

其實我們可以從 CSS 的“列表擴散規則”那里得到好處。它的意思是說,如果只為某個屬性提供一個值,那它就會擴散并應用到列表中的每一項。

    background: url(tr.png) top right,
    url(br.png) bottom right,
    url(bl.png) bottom left;
    background-size: 2em 2em;
    background-repeat: no-repeat;

怪異的簡寫語法

你可能已經注意到前面那個背景屬性簡寫的例子了:在 background 簡寫屬性中指定 background-size時,需要同時提供一個 background-position 值(哪怕它的值就是其初始值也需要寫出來),而且還要使用一個斜杠( / )作為分隔。為什么有些簡寫的語法如此怪異?

這通常都是為了消除歧義。在這個例子中, top right 顯然是background-position ,而 2em 2em 是background-size ,不管它們的順序如何。但是,請設想一下 50% 50% 這樣的值,它到底是 background-size 還是 background-position 呢?當你在使用展開式屬性時,CSS 解析器明白你的意圖;而當你使用簡寫屬性時,解析器需要在沒有屬性名提示的情況下弄清楚 50% 50% 到底指什么。這就是需要引入斜杠的原因。**

我應該使用預處理器嗎?

在每個項目開始時使用純 CSS,只有當代碼開始變得無法保持 DRY 時,才切換到預處理器的方案。為了避免可能發生的“依賴”或“濫用”,在引入預處理器的問題上需要冷靜決策,不應該在每個項目一開始時就不動腦筋順著慣性來。

第二章 邊框與背景

半透明邊框

使用 background-clip屬性來調整背景的大小,使其不再延伸到邊框的下面。

多重邊框

1.使用多個 box-shadow來創建多重邊框,當偏移量和模糊量的值均為0的時候,設置第四個參數(擴張半徑),就可以呈現出如同實線邊框一樣的“投影”。
2.使用 border和 outline來創建雙層邊框。但是當設置 border-radius后,outline不能完全貼合 border,這是一個bug。

這兩個解決方案都要注意要預先預留出 box-shadow和 outline的位置(增加額外的 margin)。如果是 inset的 box-shadow則需要額外的padding。

靈活的背景定位

很多時候我們對容器中的背景圖片進行定位時,我們可以使用background-position屬性。但是,當容器尺寸不固定時,這個方案就有可能存在很大局限,借助于現代的CSS特性,我們可以擁有更好的方法。

1.background-position 擴展語法,我們可以在 position偏移量前面指定關鍵字。

    background: right 20px bottom 10px;

2.首先我們要明白,盒模型存在著三個矩形框(border-box、padding-box、content-box),那么 background-position是相對于哪個矩形框來說的呢?默認情況下是以 padding-box為準的,這樣邊框才不會遮擋住背景。如果我們把 background-origin的值改為 content-box,我們在 background-position屬性中使用的關鍵字就會以內容框的邊緣為基準了。

    padding: 10px;
    background-origin: content-box;

3.如果我們想要把圖片定位到距離底邊10px,距離右邊20px 的位置。我們以左上角便宜的思路來考慮,其實就是希望有一個100%-20px的水平偏移量,以及100%-10px的垂直偏移量。

    background-position: calc(100% - 20px) calc(100% - 10px);
    /*注意減號左右兩側需要留一個空格*/

邊框內圓角

有時候我們可以利用兩個嵌套的容器來實現這個效果,給外部容器設置一個外邊距,內部容器設置一個圓角。

但是加入我們只利用一個容器呢?那我們可以利用容器的 outline和 box-shadow屬性,來為容器設置一個 “邊框”。首先我們應該知道描邊(outline)并不會跟著元素的圓角走,但 box-shadow 卻是會的。

    background: tan;
    border-radius: .8em;
    padding: 1em;
    box-shadow: 0 0 0 .6em #655;
    outline: .6em solid #655;

box-shadow在這里實際上就是為了填滿 outline和圓角之間存在的空隙,所以 box-shadow的擴張值不必和 outline的寬度一樣大,實際擴展值為

    x=border-radius
    sqrt(x^2+x^2)-x

條紋背景

1.對于水平條紋和垂直條紋我們使用 background: linear-gradient()屬性值來創建顏色漸變,以此來生成條紋。

    background: linear-gradient(red 50%,yellow 0);/*默認是垂直條紋*/
    background: linear-gradient(to right,red 50%,yellow 0);

    background: linear-gradient(red 33.3%,yellow 33.3%,yellow 66.6%,blue 100%);

    background-size: 100% 30px;

同時我們也可以生成有一定角度的條紋效果。

    background: linear-gradient(45deg,red 50%,yellow 0);
    background-size: 30px 30px;

然而你會發現這存在問題,這里所設置的45°偏轉并不是相對于整個容器來說的,而是相對于這里設置的30px邊長的小正方形。

因此假如我們需要給容器設置一個“偏移”,那么比較好的做法就是,將兩列拆成四列。

    background: linear-gradient(45deg,red 25%,yellow 0,yellow 50%,red 0,red 75%,yellow 0);
    background-size: 30px 30px;

這里我們會發現這樣確實達到了容器“偏移”的效果,但是問題是 background-size仍然設為 30px大小,我們所得到的條紋到底是多寬呢?這個問題應該不難,大家可以自行思考。

2.你可能會發現上面所提到的方法只適用于一種情況,那就是45deg,一旦我們對角度的需求更改的話,那么上述辦法就失效了。幸運的是,我們還有更好的辦法來解決這個問題。

一個鮮為人知的真相是:linear-gradient()和 radial-gradient()還各有一個循環加強版:repeating-linear-gradient()和 repeating-radial-gradient()。它們的工作方式與前兩類相同,只不過色標是無限循環重復的,直到顏色鋪滿整個容器。

    background: repeating-linear-gradient(45deg,red,yellow 15px);

因此,當我們將中間的漸變區域“去掉”(將它設置為特別小)時,我們發現

    background: repeating-linear-gradient(45deg,red,red 15px,yellow 0,yellow 30px);

3.更加靈活的條紋

很多時候我們所需要的條紋并不是差異巨大的兩個不同顏色,而是兩個色系相同或者相近的顏色。我們可以采取將深色作為背景,而把半透明白色條紋覆蓋在上面來顯示出同色系的條紋效果。

復雜的背景圖案

1.線性

如果我們把垂直的漸變和水平的漸變以某種規律重合在一起的話,那么可以設計出令人驚訝的效果。

甚至我們還可以將兩幅圖案重合起來。

2.波點

除了線性漸變,徑向漸變也非常有用,可以利用徑向漸變來創建圓和橢圓。

    background: red radial-gradient(dan 30%,transparent 0);
    background-size: 30px 30px;

但是,這個圖案看起來并不十分實用。那么,下面這個可能讓你大吃一驚。

    background: red;
    background-image: radial-gradient(tan 30%,transparent 0),radial-gradient(tan 30%,transparent 0); 
    background-position: 0 0,15px 15px;
    background-size: 30px 30px;

3.棋盤

使用直角三角形拼成正方形,一共需要四個直角三角形。

    background: red;
    background-image: linear-gradient(45deg,yellow 25%,transparent 0),linear-gradient(45deg,transparent 75%,yellow 0),linear-gradient(45deg,yellow 25%,transparent 0),linear-gradient(45deg,transparent 75%,yellow 0);
    background-position: 0 0,15px 15px,15px 15px,30px 0;
    background-size: 30px 30px;

當然還有更加簡潔的寫法,那就是把前兩次漸變以及后兩次漸變分別合成一次漸變。

    background: red;
    background-image: linear-gradient(45deg,rgba(0,0,0,0.25) 25%,transparent 0,transparent 75%,rgba(0,0,0,0.25) 0),linear-gradient(45deg,rgba(0,0,0,0.25) 25%,transparent 0,transparent 75%,rgba(0,0,0,0.25) 0);
    background-position: 0 0,15px 15px;
    background-size: 30px 30px;

最佳的解決方案還是SVG圖案,對于棋盤來說,在以后有機會再學習。

偽隨機模型

在上面的背景中你會輕易的發現,我們一直是在重復平鋪幾何圖案。其實自然界中的事物都不是以無限平鋪的方式存在的。即使重復,也往往伴隨著多樣性和隨機性。

我們可以利用“蟬原則”來創建具有一定隨機性的背景圖案。

假設我們需要模擬四種顏色條紋的隨機性,那么我們可能想到,把這組條紋拆分成多個圖層:一種顏色做底色,另三種顏色作為條紋,然后再讓條紋以不同的顏色進行重復平鋪(對不同顏色條紋設置不同的background-size)。雖然這可以讓條紋出現局部的隨機性,但是一旦平鋪區間大小超過三種 background-size的最小公倍數時,我們發現條紋開始重復了。

那么這里為了使得 background-size的最小公倍數比較大,在設置background-size時,選擇質數會比較好,因為質數跟其他任意數字都是相對質數。

連續的圖像邊框

如果我們想要把一張圖片應用為邊框,并且還希望這個元素的尺寸在擴大或縮小時,這幅圖片都可以自動延伸并覆蓋完整的邊框區域,那么首先可能想到的是使用 border-image屬性。但是,如果我們詳細了解過 border-image屬性的工作原理后,那么你可能發現 border-image可能難以完成這個任務。

border-image是將一張圖片切割成九宮格的形式,然后將這九個部分分別對應為盒模型的 border的九個部分,這是非常死板的做法,一旦內容大小的改變,會對局部產生難以預期的影響。

一個非常容易想到的解決方案就是,在內容外部設置一個較大的容器,將圖片設為外部大容器的背景圖片,同時將內容背景設置為純白色以覆蓋內容部分展示出的背景圖片。但這個做法顯然是不太優雅的,它不僅引入了多余的容器,而且并沒有將結構和表現分離。

1.比較好的做法是,就是利用 background-clip和 background-origin(這里要將 background-origin設為 border-box)以及白色的線性(這里利用了漸變是作為 background-image二不是作為 color屬性)漸變來創建這樣一個容器。但這里要注意到的是,在使用 background簡寫屬性的時候,background-origin是在 background-clip屬性前面的。

2.同樣除了應用圖片,單純使用漸變也可以設計出善心悅目的邊框(千萬不要忽略漸變其實也是圖片!)

    padding: 1em;
    border: 1em solid transparent;
    background: linear-gradient(white, white) padding-box,repeating-linear-gradient(-45deg,red 0, red 12.5%,transparent 0, transparent 25%,#58a 0, #58a 37.5%,transparent 0, transparent 50%) 0 / 5em 5em;;

使用border-image同樣可以實現

    padding: 1em;
    border: 16px solid transparent;
    border-image: 16 repeating-linear-gradient(-45deg,red 0, red 1em,transparent 0, transparent 2em,#58a 0, #58a 3em,transparent 0, transparent 4em);

3.我們可以利用動畫和漸變來制作一個螞蟻行軍圖。

    @keyframes ants{to {background-position: 100% }}
    padding: 1em;
    border: 1px solid transparent;
    background: linear-gradient(white,white) padding-box,repeating-linear-gradient(-45deg,black 0,black 25%,white 0,white 50%) 0/ 0.6em 0.6em;
    animation: ants 12s linear infinite;

(這里的動畫是對于每一個 background-image來說的)
background-position如果只有一個值被指定,則這個值就會默認設置背景圖片位置中的水平方向,與此同時垂直方向的默認值被設置成50%。

我們也可以利用 border-image來創建一些特殊的邊框,比如頂邊邊框被裁剪的效果

    border-top: 0.2em solid transparent;
    border-image: 100% 0 0 linear-gradient(to right,currentcolor 4em,transparent 0);
    padding-top: 1em;

第三章 形狀

自適應的橢圓

我們日常會使用 border-radius來生成圓,比如

    background: red;
    width: 200px;
    height: 200px;
    border-radius: 100px;

這里有一個問題,就是當你指定大于100px的半徑的值的時候,仍然可以生成一個圓。規范特別指出了原因

    當任意兩個相鄰圓角的半徑之和超過 border box 的尺寸時,用戶代理必須按比例減小各個
邊框半徑所使用的值,直到它們不會相互重疊為止。

而要得到一個自適應的橢圓也很簡單,只需要將 border-radius的值設置為50%,這樣不管容器的大小、長寬都可以生成一個圓或者橢圓。

半橢圓
四分之一橢圓

這里我們需要明白 border-raius是一個簡寫值,那么實際展開值就是 border-top-left-radius、border-top-right-radius、border-bottom-left-radius、border-bottom-right-radius四個值。而如何簡寫這四個值呢?如果我們可以向它一次性提供用空格分開的多個值。如果我們傳給它四個值,這四個值就會從左上角開始以順時針順序應用到四個角,如果我們傳給它三個值,那么第二和第四個值相同,如果傳給它兩個值,那么第一和第三個值相同。同時,我們甚至可以提供完全不同的垂直和水平半徑。舉例來說,當 border-radius的值為 10px/ 5px 20px時,那么就相當于10px 10px 10px 10px/ 5px 20px 5px 20px。

平行四邊形

假設我們希望創建一個平行四邊形的按鈕,那么首先我們可能想到使用2D變形 skew(),但是這會造成整個按鈕包括內容傾斜,使的內容難以辨認。但,如果在內容上再嵌套一個容器,在按鈕 skew()的情況下,對內容容器進行相反方向的 skew()就能解決這個問題。但是這同樣犯了結構沒有與表現分離的錯誤。

這里可以利用偽元素來實現這樣一個效果。我們不對元素本身進行變形,而是對偽元素進行變形。

    .button {
    position: relative;
    /* 其他的文字顏色、內邊距等樣式…… */
}
    .button::before {
    content: ''; /* 用偽元素來生成一個矩形 */
    position: absolute;
    top: 0; right: 0; bottom: 0; left: 0;
    z-index: -1;
    background: #58a;
    transform: skew(45deg);
}

這里給我的感覺這并不是一個好方法,相對于上面添加額外的容器來說,操作限制太多,一旦文字內容太多(或者字號太大)超出了偽元素區域,看起來就很不協調。

菱形圖形

日常開發中將圖片裁切為菱形是常見的設計手法。

1.基于變換的手法,就像前一篇提到的,給內容增加一個額外的容器,內外進行相反的rotate()變換。

<div class="picture">
    ![](adam-catlace.jpg)
</div>

.picture {
    width: 400px;
    transform: rotate(45deg);
    overflow: hidden;
}
.picture > img {
    max-width: 100%;
    transform: rotate(-45deg);
}

但是這并不是我們想要的效果,圖形被裁剪成了一個八邊形。那么問題出在什么地方呢?仔細觀察我們想要得到的效果,你就會發現,我們需要的是圖片的寬度等于容器的對角線的長度才行。所以我們需要將圖片放大。將 max-width設置為1.414倍(應該明白1.414怎么來的?),但是還有更好的解決方法,就是 scale()進行縮放。

.picture {
    width: 400px;
    transform: rotate(45deg);
    overflow: hidden;
}
.picture > img {
    max-width: 100%;
    transform: rotate(-45deg) scale(1.42);
}

2.真正的裁剪路勁方案,你會發現上述的方法存在一個問題,就是當圖片不是正方形的時候,就無能為力了。

這里我們要介紹一個新屬性 clip-path,clip-path借鑒于 SVG語法具有很強的功能性。

    clip-path: polygon(50% 0,100% 50%,50% 100%,0 50%);

同時我們還可以利用這個屬性產生動畫

img {
    clip-path: polygon(50% 0, 100% 50%,50% 100%, 0 50%);
    transition: 1s clip-path;
}
img:hover {
    clip-path: polygon(0 0, 100% 0,100% 100%, 0 100%);
}

但是這個功能仍然還在實驗中。

切角效果

1.解決方案一是利用漸變,設置一個45度的漸變就可以了。而如果我們需要切除兩個角,使用一個漸變會使得相互覆蓋。所以這里就需要將限制漸變的大小,使得我們裁切的角不會被覆蓋。同時注意到一旦設置了 background-size,就要注意背景是否會重復。

    background: linear-gradient(45deg,white 15px,red 0) left/50% 100% no-repeat,linear-gradient(-45deg,white 15px,red 0) right/50% 100% no-repeat;

上述代碼切割了兩個底角。

如果是需要切割四個角那么你可以用四層漸變,但還有一個更好的辦法那就是之前介紹的 clip-path。

    background: yellowgreen;
    clip-path: polygon(20% 0,80% 0,100% 20%,100% 80%,80% 100%,20% 100%,0 80%,0 20%);

弧形切角

1.這里仍然使用漸變,不過是徑向漸變,就可以設計出內凹圓角的效果了

    background: radial-gradient(circle at top left,transparent 15px,red 0) top left,radial-gradient(circle at top right,transparent 15px,red 0) top right,radial-gradient(circle at bottom left,transparent 15px,red 0) bottom left,radial-gradient(circle at bottom right,transparent 15px,red 0) bottom right;
    background-size: 50% 50%;
    background-repeat: no-repeat;

2.另一個比較好的方案就是內聯SVG和 border-image方案,由于我還不會SVG矢量圖,所以這里略過。

3.最后一個仍然是 clip-path方案,但需要注意的是這是個仍在試驗階段的屬性。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 選擇qi:是表達式 標簽選擇器 類選擇器 屬性選擇器 繼承屬性: color,font,text-align,li...
    wzhiq896閱讀 1,805評論 0 2
  • 選擇qi:是表達式 標簽選擇器 類選擇器 屬性選擇器 繼承屬性: color,font,text-align,li...
    love2013閱讀 2,337評論 0 11
  • 1、屬性選擇器:id選擇器 # 通過id 來選擇類名選擇器 . 通過類名來選擇屬性選擇器 ...
    Yuann閱讀 1,657評論 0 7
  • CSS背景 background-color:red background-image:url('paper.gi...
    專注寒冰三千歲閱讀 341評論 0 2
  • 小師弟這幾天一直沉浸在太陽花變形的喜悅中,每天和太陽花在房間里聊天。 “小花花,我來找你修煉啦!”小兔子在門口叫喚...
    DH桃夭Bazinga閱讀 300評論 0 0