1. 程式人生 > >你該知道的《css揭祕》--形狀篇

你該知道的《css揭祕》--形狀篇

關於以下內容,我們假設結構層的變更是不允許的。

我們也儘量不去新增額外的HTML,以做到樣式層與結構層的分離,如若實在沒有其他的可能性,才退而求其次來增加額外的HTML。

自適應橢圓

製作一個自適應的橢圓是形狀篇中最簡單的圖形了,簡直不能再簡單了。

眾所周知,製作與圓相關的圖形,用到的屬性就要 border-radius 了。我們知道,border-radius可以單獨指定水平和垂直半徑,使用斜槓 (/) 來分開,兩個值相等可以製作一個圓弧,如果不相等,便是一個橢圓弧了。

.demo{
    border-radius: 100px / 75px;
}
複製程式碼

其實border-radius是一個簡寫屬性,我們有兩種方式可以為元素的每個角指定不同的值。

第一種簡寫方式

.demo{
   border-radius:10px 20px 30px 40px / 50px 60px 70px 80px;
   /* 斜槓(/)前代表水平半徑,後代表垂直半徑,順序分別為左上角開始,順時針走向,所以這段程式碼表示左上角(10px/50px) 右上角(20px/60px)右下角(30px/70px)左下角(40px/80px) */
}
複製程式碼

第二種是分開寫的方式

.demo{
    border-top-left-radius:10px/50px;
    border-top-right-radius:20px/60px;
    border-bottom-right-radius:30px
/70px; border-bottom-left-radius:40px/80px; } 複製程式碼

如果生成一個自適應的橢圓就很簡單了,只要每個角的水平半徑為寬的50%,垂直半徑為高的50%,就ok了。 程式碼簡寫為:

.ellipse{
    border-radius:50%;
}
複製程式碼

平行四邊形

平行四邊形也是頁面中常出現的一種圖形,我們可能很容易就想到,使用skew()將矩形傾斜一定角度即可。

.parallelograms{
    transform: skew(-45deg);
    /*...... */
}
複製程式碼

可惜效果不如人意,文字也跟著傾斜了,這時候很容易想到,藉助一層dom結構,再把內部文字傾斜回來。

<div class="parallelograms">
    <div>二十首情詩與絕望的歌</div>
</div>
複製程式碼
 .parallelograms{
    margin: 50px auto;
    max-width: 200px;
    padding: 10px;
    line-height: 30px;
    text-align: center;
    color:#fff;
    background-color: #58a;
    transform:skew(-45deg);
}
.parallelograms div{
    transform: skew(45deg);
}
複製程式碼

很好,效果不錯,但是卻新增額外的 HTML 元素。我們不做過多討論。

接下來我們討論第二種方式,使用 偽元素來實現,這時候就體現了偽元素的好處。 關於偽元素的內容可參考我的另一邊文章你所不知道的css

思路:我們可以把偽元素作為第一種方法中的輔助結構層,把所有樣式(背景、邊框等)應用到偽元素上,然後再對偽元素進行變形,得到我們的平行四邊形形狀,而正式內容不受影響,然後把偽元素定位z-index設為-1,便可漏出正文的內容。


 .parallelograms{
    margin: 50px auto;
    max-width: 200px;
    padding: 10px;
    line-height: 30px;
    text-align: center;
    color:#fff;
    position: relative;
}
.parallelograms:before{
    content:'';
    position: absolute;
    left:0;
    top:0;
    right:0;
    bottom:0;
    background-color: #58a;
    transform:skew(-45deg);
    z-index: -1;
}
複製程式碼

提醒: 這個技巧不僅對 skew() 變形來說很有用,還適用於其他任何變形樣式, 當我們想變形一個元素而不想變形它的內容時就可以用到它。

菱形圖片

看到這個圖形狀,是不是馬上想起上一小節平行四邊形的製作,一樣的道理,需要把圖片用一個

包裹起來,然後對其應用相反的 rotate() 變形樣式:

<div class="diamond">
    <img src="https://avatars1.githubusercontent.com/u/8121621?v=4" alt="..." />
</div>
複製程式碼
.diamond {
    margin:30px auto;
    width: 100px;
    height: 100px;
    transform: rotate(45deg);
    overflow: hidden;
    border: 1px solid red; /*為了更好的展示問題*/
}

.diamond img {
    max-width: 100%;
    transform: rotate(-45deg);
}

複製程式碼

奈何,天不遂人願!問題在於 max-width:100% 中的100%是指width的100%,也就是400px,而正方形旋轉後最長邊為對角線,是 根號2倍的width,自然圖片的寬度不夠了,我們可以使用 scale() 變形樣式來把這個圖片放大。找到問題後,我們修復它,

程式碼如下:

.diamond {
    margin:30px auto;
    width: 100px;
    height: 100px;
    transform: rotate(45deg);
    overflow: hidden;
    border: 1px solid red; /*為了更好的展示問題*/
}

.diamond img {
    max-width: 100%;
    transform: rotate(-45deg) scale(1.42);
}
複製程式碼

這個方法需要一層額外的 HTML 標籤,這是我們不做優先考慮的。同時有一個最大的問題就是,只能處理正方形圖片,否則就會失效。

在上節中我們使用過偽元素的技巧,同樣可以用在這裡,程式碼如下:

 .diamond{
    margin:30px auto;
    width: 100px;
    height: 100px;
    overflow: hidden;
    position: relative;
    transform: rotate(45deg);
}
.diamond:before{
    content:'';
    position: absolute;
    left: 0;
    right:0;
    top:0;
    bottom:0;
    transform: rotate(-45deg) scale(1.42);
    background: url(https://avatars1.githubusercontent.com/u/8121621?v=4);
    background-size: cover;
}
複製程式碼

原理與上面藉助結構層是一樣的,所以面臨同樣的問題,只能處理正方形圖片。

接下來我們使用一種更為好用的方法來解決不是正方形的圖片。(裁切路徑方案)

丟擲程式碼:

.diamond{
    /*......*/
    clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
}
複製程式碼

clip-path 屬性是從svg中借鑑過來的,裁切路徑允許我們把元素裁剪為我們想要的任何形狀。polygon()函式允許我們用一系列(以逗號分隔的)座標點來指定任意的多邊形。

該方法同樣可以實現上個章節中的平行四邊形,以下章節中的切角、梯形等等任意形狀,只需要按順序排列座標點即可,以下章節不再做過多展示,自己可以多多嘗試。

切角效果

切角效果,很容易想到的就是我的上篇文章 你該知道的《css揭祕》--背景與邊框篇 中的條紋背景製作中用到的線性漸變 linear-gradient()

我們很輕易的可以實現一個角被切掉的效果,程式碼如下:

.bevel-corners{
    background: #58a; /*linear-gradient不支援的情況下,作為程式碼回退機制*/ 
    background:linear-gradient(-45deg, transparent 15px, #58a 0);
}
複製程式碼

接下來使用兩層漸變背景實現兩個角被切掉。

首先分析一下,預設情況下, 這兩層漸變都會填滿整個元素,因此它們會相互覆蓋。需要讓它們都縮小一些,於是我們使用 background-size 讓每層漸變分別只佔據整個元素一半的面積,並且 background-repeat設為 no-repeat

程式碼如下:

.bevel-corners{
    background: #58a;
    background:
    linear-gradient(-45deg, transparent 15px, #58a 0) right,
    linear-gradient(45deg, transparent 15px, #58a 0) left;
    background-size:50% 100%;
    background-repeat:no-repeat;
}
複製程式碼

同樣的原理,我們把每層漸變改為整個元素的四分之一,則四層漸變色,可以實現四個角被切掉。

程式碼如下:

.bevel-corners{
    background:#58a;
    background:
    linear-gradient(-45deg,transparent 15px, #58a 0) bottom right,
    linear-gradient(45deg,transparent 15px, #58a 0) bottom left,
    linear-gradient(135deg,transparent 15px, #58a 0) top left,
    linear-gradient(-135deg,transparent 15px, #58a 0) top right;
    background-size:50% 50%;
    background-repeat:no-repeat;
}
複製程式碼

繼續增加難度,實現 弧形切角, 原理都一樣,換湯不換藥,只需將線性漸變改為徑向漸變即可。

實現程式碼如下:

.scoop-corners{
    background: #58a; 
    background:
    radial-gradient(circle at top left, transparent 15px, #58a 0) top left,
    radial-gradient(circle at top right, transparent 15px, #58a 0) top right,
    radial-gradient(circle at bottom right, transparent 15px, #58a 0) bottom right,
    radial-gradient(circle at bottom left, transparent 15px, #58a 0) bottom left;
    background-size: 50% 50%; 
    background-repeat: no-repeat;
}
複製程式碼

簡單的餅圖

實現最後一個圖形--餅圖(綠色為餅圖,棕色來顯示比率)

基於 transform 的解決方案

思路:把圓形的左右兩部分指定為上述兩種顏色,然後用偽元素覆蓋上去,通過旋轉來 決定露出多大的扇區。

20%的餅圖程式碼如下:

.pie {
    width: 100px; 
    height: 100px;
    border-radius: 50%; 
    background: yellowgreen;
    background-image:linear-gradient(90deg, transparent 50%, #655 0);
}
.pie:before { 
    content: '';
    display: block; 
    margin-left: 50%;
    height: 100%;
    border-radius: 0 100% 100% 0 / 50%; 
    background-color: inherit; 
    transform-origin: left;
    transform: rotate(.2turn);
}
複製程式碼

當旋轉超過50%之後,餅圖就變成了下圖這樣,

然後我們可以讓旋轉的偽元素的顏色反一下,變成棕色既可以實現50%-100%比率的餅圖,

60%的餅圖程式碼如下:

.pie:before { 
    content: '';
    display: block; 
    margin-left: 50%;
    height: 100%;
    border-radius: 0 100% 100% 0 / 50%; 
    background-color: #655; 
    transform-origin: left;
    transform: rotate(.1turn);
}

複製程式碼