iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 17
0

梯形和平行四邊形的差別是,它只有二個邊是平行的,另外二個邊可以是任何角度。以前這個形狀是非常難用CSS做出來,雖然它是一種常見的形狀,尤其用在模仿標籤的樣子。如果不是用background-image的話,一般會在長方形兩旁用border仿造出二個三角形。

雖然這個方法省去了用HTTP request去取得圖片,也可以適應不同的寬度,但是卻不是最好的。這樣做不僅浪費了二個偽元素,在樣式修改上也很不方便。舉個例子,要加邊界、背景圖案,或是圓角的話,這個方法就不容易做到。

就是因為傳統的做法是這麼困難,所以一般在網站看到的標籤都不是二側傾斜的,但是真實世界裡卻是如此。有沒有做梯形標籤的好的方法呢?


還記得前面我們用transform做出平行四邊形,是不是也可以用某一種transform就做出梯形?

壞消息是,不行。

不過可以這樣思考,想像在三度空間中旋轉一個長方形,以底為軸向外旋轉,這時從原本的視角看,它就像個梯形!因此我們可以用CSS 3D rotation來模擬這個效果。

.tab {
	display: inline-block;
	padding: .5em 1em;
	color: white;
	background: #840715;
	transform: perspective(.5em) rotateX(5deg);
}

https://ithelp.ithome.com.tw/upload/images/20181101/200916062BQm2wInI1.png
(上方是原本的長方形)

我們是得到了一個梯形,但是連裡面的文字也一起變形了。同樣的問題在製作平行四邊形時也遇到過,那時我們用了偽元素製造背景,將transform用在偽元素上,在這裡我們也如法炮製。

.tab {
	display: inline-block;
	padding: .5em 1em;
	color: white;
	position: relative;
}

.tab::before {
	content: '';
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	z-index: -1;
	background: #840715;
	transform: perspective(.5em) rotateX(5deg);
}

https://ithelp.ithome.com.tw/upload/images/20181101/20091606u18OhFORjt.png

很好,我們有了梯形背景,而且文字沒有變形。或許你回想起我們在做平行四邊形時用過巢狀元素,同樣方法也可以在這裡使用嗎?

答案是不行,因為CSS 3D transforms不能像2D transforms那樣用反方向的值取消。

現在有一個問題,我們能發現梯形的底比起原本的長方形的底來得寬,這是因為CSS 3D transforms的軸是位於圖形的中間,所以只要把這條軸線轉移到圖形底部就可以了。

.tab::before {	
	/* 將變形的軸設在圖形的底部 */
	transform-origin: bottom;
}

https://ithelp.ithome.com.tw/upload/images/20181101/20091606ffMVEjnbSO.png

這時又發生另一個問題,因為方形向外傾斜,所以從我們的角度看它的高度縮小了,文字的位置沒有變,造成文字和上邊界太靠近。這時你可能覺得加個padding-top不就解決了,不過這樣做的話,當瀏覽器不支援3D transforms的時候,我們的標籤在比例上就會很怪異。

https://ithelp.ithome.com.tw/upload/images/20181101/20091606AdnptN6hjx.png
(不支援3D transforms時我們的標籤多出了沒有用的padding)

比較好的方法是用scale(),讓標籤在有或沒有3D transforms時都好看。經過一番測試,發現將標籤高度放大130%是最合適的。

.tab::before {
	transform: scaleY(1.3) perspective(.5em) rotateX(5deg);
}

https://ithelp.ithome.com.tw/upload/images/20181101/20091606AerzQmID98.png

https://ithelp.ithome.com.tw/upload/images/20181101/20091606wlJJbKlSmm.png
(使用scale()即使在沒有3D transforms情況下,看起來不算太糟)

CodePen

這下子你可以看到我們的梯形幾乎和最早用border方式做的一樣,而且它的語法更加簡明扼要。更棒的是,當我們要為標籤加上樣式時,更能感受到這個方法的威力。

<nav>
  <a href="#">OS 1</a>
  <a href="#" class="selected">OS 2</a>
  <a href="#">OS 3</a>
</nav>
<div class="content">Content</div>
.content {
  padding: 1em;
  background: white;
  border: .1em solid rgba(0,0,0,.4);
  border-radius: 2px;
  margin-bottom: 2em;
}

nav {
  z-index: 1;
  padding-left: 1em;
}

nav > a {
  display: inline-block;
  padding: .3em 1em 0;
  text-decoration: none;
  color: black;
  position: relative;
  margin: 0 -.3em;
}

nav > a::before {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: -1;
  background: #ccc;
  background-image: linear-gradient(
    hsla(0,0%,100%,.6),
    hsla(0,0%,100%,0)
  );
  border: .1em solid rgba(0,0,0,.4);
  border-bottom: none;
  border-radius: .5em .5em 0 0;
  box-shadow: 0 .15em white inset;
  transform: scale(1.1, 1.3) perspective(.5em) rotateX(5deg);
  transform-origin: bottom;
}

.selected {
  z-index: 2;
}

.selected::before {
  background: white;
  margin-bottom: -.08em;
}

https://ithelp.ithome.com.tw/upload/images/20181101/20091606Frl164VIjM.png

在這裡我們給標籤加上了background, border, border-radiusbox-shadow,更棒的是,當我們想要改變標籤傾斜的方向,只要變更tranform-originbottom leftbottom right就可以了。

雖然好用,但這個方法有一個缺點,就是梯形斜邊的角度取決於元素的寬度,也就是說當寬度愈長,斜邊的角度會愈銳利。所以在處理寬度不一樣的標籤時,要讓梯形角度都一樣是很困難的。不過在應付寬度差異不大的標籤時-例如一個導覽例,角度的差距其實很難察覺得出來。

CodePen


上一篇
Secret 12: 截角效果 (clip-path)
下一篇
Secret 14: 簡單的圓餅圖 (CSS transforms)
系列文
CSS Secrets 導讀30

尚未有邦友留言

立即登入留言