CSS秘密花園:折角效果

jopen 9年前發布 | 20K 次閱讀 CSS 前端技術

CSS Secrets 》是 @Lea Verou 最新著作,這本書講解了有關于CSS中一些小秘密。是一本CSSer值得一讀的一本書,經過一段時間的閱讀,我、@南北和@彥子一起將在W3cplus發布一系列相關的讀后感,與大家一起分享。

問題

為元素的一個角落添加樣式,讓它看起來像是折起來的(通常是右上角或是左下角),有不同程度的逼真效果,是這幾年來一個非常受歡迎的效果。

近來,有幾個純CSS的解決方案,第一個是在2010年的時候由偽元素大師—— Nicolas Gallagher 發布的。它們通常是通過添加兩個三角形在左上角:一個用于翻頁,一個用于模糊主元素的角。這些三角形通常是通過舊的 border 技巧來創建的。

圖注:幾個早期的 csstricks.com 上的折角效果的設計,在每篇文章的右上角都可以看到

這些解決方案在當時是令人印象深刻的,現在它們卻在下面這幾種情況下有非常多的限制以及不足:

  • 當我們的元素的背景不是純色,而是一個圖案、紋理、照片、漸變,或其它任何這樣的背景圖像
  • 當我們想要一個不同于 45° 的角,或是一個旋轉折疊

有沒有一種方法可以用CSS直接創建一個更靈活的折疊角效果,而且在這些情況下也不會失效的?

針對45°角的解決方案

我們從一個右上角帶有斜角的元素開始,在 第三章第四小節中提到的基于漸變的解決方案 上創建。為了用這種技術創建一個 1em 的右上斜切角,代碼如下:

background: #58a; /* Fallback */
background:linear-gradient(-135deg, transparent 2em, #58a 0);

效果如下:

圖注:我們的起點:一個右上角帶有斜切角的元素,通過一個漸變完成

在這時候,我們已經完成了一半的工作:我們需要做的是添加一個暗色的三角形用于翻頁效果。我們需要通過添加另一個漸變來創建三角形,因此我們將根據需求重新調整 background-size 的值和右上角的 position 。

要創建三角形,我們需要做一個傾斜的線性漸變:

background:linear-gradient(to left bottom,transparent 50%, rgba(0,0,0,.4) 0) no-repeat 100% 0 / 2em 2em;

圖注:我們為了折疊三角形的完成的第二個漸變,此處的文本顯示的是淡淡的灰色而不是白色,這樣你就能大概看到文本所處的位置

你可以在上圖中看到只有這個漸變背景的效果。最后一步是把它們結合起來,這個我們會做,對吧?我們來試試,確保三角形翻頁是在我們的缺角漸變上的:

background: #58a; /* Fallback */
background:
    linear-gradient(to left bottom,transparent 50%, rgba(0,0,0,.4) 0) no-repeat 100% 0 / 2em 2em,
    linear-gradient(-135deg, transparent 2em, #58a 0);

正如你在下圖中看到的,結果和我們預期的并不一樣。為什么大小不匹配呢?都是 2em 呀!

圖注:把兩個漸變結合起來,并沒有產生我們所期待的結果

原因是(如我們在第三章第四小節中討論過的), 2em 的斜角尺寸在我們的第二個漸變中是顏色結點,并由此沿著漸變線測量,也就是斜角方向上的。另一方面, 2em 長度在 background-size 是背景平鋪的寬度和高度,是在水平方向和垂直方向測量的。

為了讓兩個對齊,我們可以采用下列操作中的一種,這取決于這兩個尺寸中我們想要保留哪個:

  • 要保留對角方向 2em 的尺寸,我們可以把 background-size 乘以根號二。
  • 要保留水平和垂直方向 2em 的尺寸,我們可以把切口斜角漸變顏色結點的位置除以根號二。

因為 background-size 重復兩次,大多數其他的CSS尺寸不是以對角線測量的,所以通常保留水平方向的 2em 是更好的選擇。這樣顏色結點的位置將變成 2 除以根號二等于根號二,約等于 1.414213562 ,也就是最后會變成 1.5em 。

background: #58a; /* Fallback */
background:
    linear-gradient(to left bottom,
    transparent 50%, rgba(0,0,0,.4) 0)
    no-repeat 100% 0 / 2em 2em,
    linear-gradient(-135deg,
    transparent 1.5em, #58a 0);

如下圖所示,最后我們會得到一個漂亮的、靈活的、簡單的圓角。

圖注:在改變藍色漸變顏色結點的位置之后,我們的折疊角終于完成了

請確保有足夠多的 padding ,至少能大于折疊角的尺寸,否則文本將覆蓋在角上(因為它只是一個背景),破壞了折疊角的視覺效果。

其它角度的解決方案

現實生活中的折疊角很少有正好是 45° 的。如果我們想要它更加逼真一點,我們可以使用一個稍微不同的角度,比如使用 -150deg 來完成一個 30° 的角。如果我們只改變斜角的角度,但是,代表翻頁效果的三角形不會自己調整,會導致兩角分裂,如圖所示。

圖注:改變切口的角度會導致分離

但是,我們不能直接調整它的尺寸。三角形的大小不是通過角度定義的,而是通過它的 width 和 height 值確定的。那我們要怎樣才能找到我們需要的 width 和 height 值呢?Well,三角函數的知識是時候派上用場了!

目前的代碼是長這樣的:

background: #58a; /* Fallback */
background:
    linear-gradient(to left bottom,
    transparent 50%, rgba(0,0,0,.4) 0)
    no-repeat 100% 0 / 2em 2em,
    linear-gradient(-150deg,
    transparent 1.5em, #58a 0);

你可以在下圖中看到,如果我們需要計算兩個 30-60-90 直角三角形斜邊的長度,我們需要知道它們其中一條邊的長度。

圖注:我們的切角,放大(灰色標記的角為 30° )

下圖所示的極坐標中的三角函數提醒我們,如果我們知道角度和三角形一條邊的長度,我們可以通過使用正弦、余弦、和勾股定理計算它的另外兩條邊的長度。

圖注:正弦和余弦根據一個角和斜邊,幫我們計算出了直角三角形的邊長

通過數學知識(或計算器),我們知道 cos 30° 等于根號三除以 2 和 sin 30° 等于二分之一。我們還知道,在我們的示例中,根據三角函數, sin 30° = 1.5 / x 以及 cos 30° = 1.5 / y ,因此:

在這里,根據勾股定理,我們可以計算出 z 的值:

現在我們可以調整三角形的大小了:

background: #58a; /* Fallback */
background:
    linear-gradient(to left bottom,
    transparent 50%, rgba(0,0,0,.4) 0)
    no-repeat 100% 0 / 3em 1.73em,
    linear-gradient(-150deg,
    transparent 1.5em, #58a 0);

現在,我們的折疊角如圖所示。

圖注:盡管我們確實達到了我們想要的結果,事實卻證明,它看起來非常不真實

你可以看到,現在的三角形雖然和我們的切口相匹配,但是效果看起來并不逼真!雖然我們可能還不能很快找出原因,我們前面已經看到過很多折疊角了,所以我們一眼就能看出它和我們的視覺習慣嚴重偏離了。你可以通過給一個實際的紙片折一個這樣角度的角,來幫助弄明白為什么這個效果看起來這么假。在紙上我們找不出能夠折疊出隱約如上圖這樣效果的角的方法。

你可以拿張紙折一個角看看,如圖所示

圖注:模擬折疊角效果(Leonie and Phoebe Verou的可愛紙片)

我們想要的三角形是稍微旋轉的,并且和我們“剪”掉的三角形的尺寸是一樣的。因為我們不能旋轉背景,所以是時候用偽元素來移動效果了:

.note {
    position: relative;
    background: #58a; /* Fallback */
    background:
    linear-gradient(-150deg,
    transparent 1.5em, #58a 0);
}
.note::before {
    content: '';
    position: absolute;
    top: 0; right: 0;
    background: linear-gradient(to left bottom,
    transparent 50%, rgba(0,0,0,.4) 0)
    100% 0 no-repeat;
    width: 3em;
    height: 1.73em;
}

這里,我們復制了剛剛下圖中的效果,放到偽元素中。

我們的下一步是通過交換其 width 和 height 來讓它變成我們切掉的角的對稱角,改變三角形的方向,而不是互補。然后我們將其按照 30° ( (90° – 30°) – 30° ) 的方向逆時針旋轉。使其斜邊平行于我們的切口角:

.note::before {
    content: '';
    position: absolute;
    top: 0; right: 0;
    background: linear-gradient(to left bottom,
    transparent 50%, rgba(0,0,0,.4) 0)
    100% 0 no-repeat;
    width: 1.73em;
    height: 3em;
    transform: rotate(-30deg);
}

你可以在下圖中看到我們改變后的效果。

圖注:我們差不多完成了,但是我們需要移動三角形

正如你看到的,我們現在只需要移動三角形,使我們的兩個三角形(黑色的和切開的那個)的斜邊重合。就目前看,我們需要在水平和垂直方向移動三角形,所以比較難確定要怎么移動。我們可以通過把 transform-origin 設置為 bottom right ,讓事情變得簡單一點,這樣三角形的右下角就會變成旋轉中心,然后,保持固定在同一個地方:

.note::before {
    /* [Rest of styling] */
    transform: rotate(-30deg);
    transform-origin: bottom right;
}

確保把 translateY() 變換放在旋轉之前,否則我們的三角形將沿它的 30° 角移動,因為每個變換都將讓元素的整個坐標系統一起變換,而不僅僅是元素本身!

如下圖所示:

圖注:添加 transform-origin: bottom right; 可以讓事情變得簡單很多:現在我們只需要在垂直方向移動我們的三角形就ok了。

現在我們只需要向上垂直移動我們的三角形。為了找到確切的值,我們可以再次使用幾何來解決問題。如下圖所示

圖注:知道我們的三角形要移動多少并不像看起來那么難

我們的三角形需要的垂直偏移量是 x-y=3-(根號三) 1.267949192 ,結果約為 1.3em :

.note::before {
    /* [Rest of styling] */
    transform: translateY(-1.3em) rotate(-30deg);
    transform-origin: bottom right;
}

下圖中的效果,終于給了我們想要的效果。

圖注:我們的三角形終于對上了~~好感動有木有

呼~真是不容易!此外,現在我們的三角形是通過偽元素生成的,我們可以讓它變得更加逼真,通過添加圓角,(實際的)漸變,還有 box-shadow s!最后的代碼如下所示:

.note {
    position: relative;
    background: #58a; /* Fallback */
    background:
    linear-gradient(-150deg,
    transparent 1.5em, #58a 0);
    border-radius: .5em;
}
.note::before {
    content: '';
    position: absolute;
    top: 0; right: 0;
    background: linear-gradient(to left bottom,
    transparent 50%, rgba(0,0,0,.2) 0, rgba(0,0,0,.4))
    100% 0 no-repeat;
    width: 1.73em;
    height: 3em;
    transform: translateY(-1.3em) rotate(-30deg);
    transform-origin: bottom right;
    border-bottom-left-radius: inherit;
    box-shadow: -.2em .2em .3em -.1em rgba(0,0,0,.15);
}

你可以在下圖中欣賞我們的勞動成果。

效果看起來不錯,但是代碼重用的情況如何呢?我們來想一些常見的修改和變化:

  • 只需要一次編輯就可以改變元素尺寸或其它參數(如 padding )。
  • 只需要編輯兩次就可以改變背景顏色(沒有降級的情況下)。
  • 需要四次編輯和幾個普通的計算,就可以改變折疊角的大小。
  • 只需要五次編輯和幾個甚至更少的瑣碎的計算來改變折疊角的角度。

最后兩個是比較難的。可能需要使用預處理器的 @mixin :

@mixin folded-corner($background, $size,$angle: 30deg)
{
    position: relative;
    background: $background; /* Fallback */
    background:
        linear-gradient($angle - 180deg,
        transparent $size, $background 0);
    border-radius: .5em;
    $x: $size / sin($angle);
    $y: $size / cos($angle);
    &::before {
        content: '';
        position: absolute;
        top: 0; right: 0;
        background: linear-gradient(to left bottom,
        transparent 50%, rgba(0,0,0,.2) 0,
        rgba(0,0,0,.4)) 100% 0 no-repeat;
        width: $y; height: $x;
        transform: translateY($y - $x)
        rotate(2*$angle - 90deg);
        transform-origin: bottom right;
        border-bottom-left-radius: inherit;
        box-shadow: -.2em .2em .3em -.1em rgba(0,0,0,.2);
    }
}
/* used as... */
.note {
    @include folded-corner(#58a, 2em, 40deg);
}

注意:在本書寫作期間,SCSS還沒有原生支持三角函數。要啟用支持,你可以使用 Compass框架 ,相對于其它庫。你甚至可以自己寫,使用泰勒展開函數!LESS,同樣可以調用它們。

來自: http://www.w3cplus.com/css3/css-secrets/folded-corner-effect.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!