最近,有群友問我,他們的一個作業,儘量使用少的標籤去實現這樣一個象棋佈局:
他用了 60 多個標籤,而他的同學,只用了 6 個,問我有沒有辦法儘可能的做到利用更少的標籤去完成這個佈局效果。
其實,對於一個頁面的佈局而言,標籤越少不一定是好事,我們在考慮 DOM 的消耗的同時,也需要關注程式碼的可讀性,以及後續基於這個佈局的製作的互動的難易性等等。
當然,僅僅從用更少的標籤完成這個佈局的角度而言,我們能夠把標籤數壓縮到多少呢個?(不考慮 <body>
和 <html>
)
答案是 1 個。
本文就嘗試使用一個標籤完成這個效果,當然,這僅僅是探索 CSS 的極限,不代表我推薦在實際業務中這樣去寫。
我們對整個佈局進行一下拆分,大致可以分為三部分:網格 + 虛線交叉十字 + 特殊符號:
並且,像虛線交叉十字和特殊的符號都不止一個,這裡必然會有一些技巧存在。
OK,首先,我們實現最簡單的網格佈局:
不考慮最外層的一圈邊框,我們可以首先利用多重線性漸變實現一個網格佈局:
<div class="g-grid"></div>
.g-grid {
width: 401px;
height: 451px;
background:
repeating-linear-gradient(#000, #000 1px, transparent 1px, transparent 50px),
repeating-linear-gradient(90deg, #000, #000 1px, transparent 1px, transparent 50px);
background-repeat: no-repeat;
background-size: 100% 100%, 100% 100%;
background-position: 0 0, 0 0;
}
效果如下:
在最外層加一層邊框有非常多辦法,這裡我們簡單使用 outline
配合 outline-offset
即可:
.g-grid {
width: 401px;
height: 451px;
background:
repeating-linear-gradient(#000, #000 1px, transparent 1px, transparent 50px),
repeating-linear-gradient(90deg, #000, #000 1px, transparent 1px, transparent 50px);
background-repeat: no-repeat;
background-size: 100% 100%, 100% 100%;
background-position: 0 0, 0 0;
outline: 1px solid #000;
outline-offset: 5px;
}
這樣,一個架子就差不多了:
當然,棋盤中間的一行,是沒有格子的。要將上述漸變程式碼處理一下,可以分成上下兩塊,利用 background-size
和 background-position
進行分隔。
當然,我們也可以在最上層直接再疊一層純白色漸變:
.grid {
// ...
background:
// 最上層疊加一層白色漸變
linear-gradient(#fff, #fff),
// 下面兩個重複線性漸變實現網格
repeating-linear-gradient(#000, #000 1px, transparent 1px, transparent 50px),
repeating-linear-gradient(90deg, #000, #000 1px, transparent 1px, transparent 50px);
background-repeat: no-repeat;
background-size: calc(100% - 2px) 49px, 100% 100%, 100% 100%;
background-position: 1px 201px, 0 0, 0 0;
}
到這裡,其實核心還都是漸變,目前共 3 層漸變,得到這樣一個效果:
OK,我們繼續,我們需要基於上述的基礎,得到兩個交叉虛線十字,像是這樣:
這裡其實真的有難度。想象一下,如果給你一個 DIV,去實現其中一個,可以怎麼做呢?
通過 border 中特有的虛線 dashed?這樣可能就需要兩個元素設定單邊的虛線邊框,然後旋轉相交得到。(可以利用元素的兩個偽元素,實現在一個 DOM 中)。
當然,這樣的話,我們的標籤就不夠用了。
所以,這裡我們另闢蹊徑,繼續使用漸變!
首先,打個樣,如果是一個 100px x 100px 的 DIV,可以怎麼利用漸變去畫交叉虛線十字呢?
<div></div>
div {
position: relative;
margin: auto;
width: 100px;
height: 100px;
border: 1px solid #000;
background: linear-gradient(
45deg,
transparent 0, transparent calc(50% - 0.5px),
#000 calc(50% - 0.5px), #000 calc(50% + 0.5px),
transparent calc(50% + 0.5px), transparent 0);
}
我們首先利用漸變,實現一條 1px 的斜線,注意這裡的漸變是從透明到黑色到透明,實現了一條 45° 的斜線。
我們再反 45° 過來,利用多重線性漸變,實現透明到白色的漸變效果:
div {
position: relative;
margin: auto;
width: 100px;
height: 100px;
border: 1px solid #000;
background:
// 漸變 1
repeating-linear-gradient(-45deg, transparent 0, transparent 5px, #fff 5px, #fff 10px),
// 漸變 2
linear-gradient(45deg,
transparent 0, transparent calc(50% - 0.5px),
#000 calc(50% - 0.5px), #000 calc(50% + 0.5px),
transparent calc(50% + 0.5px), transparent 0);
}
這樣,我們就得到了一條虛線:
好吧,這一步有一些同學可能會有一點疑惑,怎麼變過來的。
我把上面漸變 1的透明色改成黑色,就很好理解了:
想象一下,上圖的黑色部分,如果是透明的,就能透出原本的那條斜線沒有被白色遮擋住的地方。
這裡,需要提一下,在漸變中,越是先書寫的漸變,層級越高。
好,有了上面的鋪墊,我們基於上面的程式碼,再繼續利用漸變,把上下兩個交叉虛線十字補齊即可:
.g-grid {
width: 401px;
height: 451px;
outline: 1px solid #000;
outline-offset: 5px;
background:
// 最上層的白色塊,擋住中間的網格
linear-gradient(#fff, #fff),
// 實現網格佈局
repeating-linear-gradient(#000, #000 1px, transparent 1px, transparent 50px),
repeating-linear-gradient(90deg, #000, #000 1px, transparent 1px, transparent 50px),
// 棋盤上方的虛線1
repeating-linear-gradient(-45deg, transparent 0, transparent 5px, #fff 5px, #fff 10px),
linear-gradient(45deg, transparent,
transparent calc(50% - 0.5px),
#000 calc(50% - 0.5px),
#000 calc(50% + 0.5px),
transparent calc(50% + 0.5px),
transparent 0),
// 棋盤上方的虛線2
repeating-linear-gradient(45deg, transparent 0, transparent 5px, #fff 5px, #fff 10px),
linear-gradient(-45deg, transparent,
transparent calc(50% - 0.5px),
#000 calc(50% - 0.5px),
#000 calc(50% + 0.5px),
transparent calc(50% + 0.5px),
transparent 0),
// 棋盤下方的虛線1
repeating-linear-gradient(-45deg, transparent 0, transparent 5px, #fff 5px, #fff 10px),
linear-gradient(45deg, transparent,
transparent calc(50% - 0.5px),
#000 calc(50% - 0.5px),
#000 calc(50% + 0.5px),
transparent calc(50% + 0.5px),
transparent 0),
// 棋盤下方的虛線2
repeating-linear-gradient(45deg, transparent 0, transparent 5px, #fff 5px, #fff 10px),
linear-gradient(-45deg, transparent,
transparent calc(50% - 0.5px),
#000 calc(50% - 0.5px),
#000 calc(50% + 0.5px),
transparent calc(50% + 0.5px),
transparent 0);
background-repeat: no-repeat;
background-size:
calc(100% - 2px) 49px, 100% 100%, 100% 100%,
// 交叉虛線 1
100px 100px, 100px 100px, 100px 100px, 100px 100px,
// 交叉虛線 2
100px 100px, 100px 100px, 100px 100px, 100px 100px;
background-position:
1px 201px, 0 0, 0 0,
// 交叉虛線 1
151px 0, 151px 0, 151px 0, 151px 0,
// 交叉虛線 2
151px 350px, 151px 350px, 151px 350px, 151px 350px;
}
嚯,這漸變程式碼確實複雜了點,但是其實每一塊的作用都是很清晰的,這樣,我們的棋盤就變成了這樣:
到這裡,我們僅僅使用了元素本身,要知道,我們還有元素的兩個偽元素沒使用。要實現的只剩下多個的這個符合:
因為一共要實現 12 個這樣的符號,有的符合還是不完整的,所有這些要在剩餘的元素的兩個偽元素中完成。可選的方法思來想去,也只有 box-shadow 了。
利用 box-shadow
能夠非常好的複製自身。這個技巧其實也反覆講過非常多次了。
我們首先利用元素的一個偽元素,在這個位置,實現一個短橫線:
程式碼大致如下:
.g-grid {
// ...
&::before {
content: "";
position: absolute;
top: 95px;
left: 35px;
width: 10px;
height: 1px;
background: #000;
}
}
我們利用 box-shadow
複製自身,可以完成一半橫線效果。當然這裡由於是個鏡面佈局,可以利用映象 -webkit-box-reflect: below
減少一半的程式碼:
.g-grid {
// ...
&::before {
content: "";
position: absolute;
top: 95px;
left: 35px;
width: 10px;
height: 1px;
background: #000;
box-shadow:
20px 0, 0 10px, 20px 10px,
300px 0, 320px 0, 300px 10px, 320px 10px,
-30px 50px, -30px 60px,
50px 50px, 50px 60px, 70px 50px, 70px 60px,
150px 50px, 150px 60px, 170px 50px, 170px 60px,
250px 50px, 250px 60px, 270px 50px, 270px 60px,
350px 50px, 350px 60px;
-webkit-box-reflect: below 259px;
}
}
效果如下:
最後,利用另外一個偽元素,完成另外一半的豎向橫線即可:
.g-grid {
// ...
&::before {
// ...
}
&::after {
// ...
box-shadow:
10px 0, 0 20px, 10px 20px,
300px 0px, 300px 20px, 310px 0, 310px 20px,
-40px 50px, -40px 70px,
50px 50px, 50px 70px, 60px 50px, 60px 70px,
150px 50px, 150px 70px, 160px 50px, 160px 70px,
250px 50px, 250px 70px, 260px 50px, 260px 70px,
350px 50px, 350px 70px;
-webkit-box-reflect: below 260px;
}
}
這樣,我們就在一個標籤內,得到這樣一個效果:
當然,還剩下楚河、漢界 4 個字,這個也簡單直接加在 div 中即可,配合一些簡單的 CSS 調整,整個效果就在一個標籤內完成啦:
完整的程式碼你可以戳這裡:CodePen Demo -- CSS Chess board
好,實際中我確實不太推薦這麼去寫,純粹是為了實現而實現,少了很多程式碼可讀性的考量。因此,本文更多的是給大家帶來一些思路,當遇到類似的問題的使用能夠有更多的靈感。
好了,本文到此結束,希望本文對你有所幫助