世界盃火熱進行中, 用一個div畫個足球場助助興

南城FE發表於2022-12-01

四年一度的世界盃正在火熱進行中,有沒有熬夜看你喜歡的隊伍比賽呢。在這歡慶的氛圍中,我決定用程式碼參與一把世界盃,擦邊參與,那就是用CSS畫一個足球場,正常的用CSS佈局肯定是非常easy的,所以決定只用一個div完成,接下來開始正文。

足球場的尺寸

畫之前首先要獲取幾個關鍵位置的尺寸,查詢度孃的結果如下:

  • 場地:長105米,寬68米;
  • 球門:長7.32米,高2.44米;
  • 大禁區(罰球區):長40.32米,寬16.5米;
  • 小禁區(球門區):長18.32米,寬5.5米
  • 中圈區:半徑9.15米;
  • 角球去:半徑1米;
  • 罰球弧:半徑9.15的半圓

基於這份資料定義一些基本的變數,本次實現基於上面的值乘以2作為畫素值,部分小區域數值有所調整。整體變數定義如下:

:root {
  --lineColor: #fff;
  --fieldWidth: 210px;
  --fieldHeight: 136px;
  --centerCircle: 18px;
  --cornerCircle: 4px;
  --grandForbiddenAreaWidth: 32px;
  --grandForbiddenAreaHeight: 80px;
  --smallRestrictedAreaWidth: 11px;
  --smallRestrictedAreaHeight: 36px;
  --goalWidth: 4px;
  --goalHeight: 14px;
}

CSS倒影

這裡用到了CSS倒影 box-reflect,因為只能用一個div,所以要儘可能利用現有的CSS能力,減少額外的程式碼量。足球場本質是一個對稱圖形,在這裡使用CSS倒影就很合適,如果不考慮只用一個div,還可以多次使用倒影。本次CSS邏輯只實現內容左側部分,右側內容由 box-reflect 倒影實現。

-webkit-box-reflect: right;

實現過程

首先增加邊框部分,本文所有的線條都是按2px實現。

div {
  width: calc(var(--fieldWidth) / 2);
  height: var(--fieldHeight);
  border: 2px solid var(--lineColor);
}


接下來開始畫中心部分的圓圈,因為只使用一個div,所以將大量使用CSS漸變實現各種線條部分內容,這裡需要注意的地方是有倒影的使用程式碼上只需要畫出半圓,所以要增加 no-repeat 避免在左側繪製出另一半圓。

radial-gradient(
  circle,
  #0000 var(--centerCircle) 0,
  var(--lineColor) calc(var(--centerCircle)),
  var(--lineColor) calc(var(--centerCircle) + 2px),
  #0000 calc(var(--centerCircle) + 2px)
) no-repeat calc(var(--fieldWidth) / 4) 50% / 100% 100%

然後繪製四個角落的角球區域圓圈,上下兩個角球部分需要分開繪製,核心程式碼都是一樣,只是 background-position 的位置不一樣。

// 上面角球 1/4 圓
radial-gradient(
  circle, 
  #0000 var(--cornerCircle),
  var(--lineColor) calc(var(--cornerCircle)),
  var(--lineColor) calc(var(--cornerCircle) + 2px),
  #0000 calc(var(--cornerCircle) + 2px)
) no-repeat calc(var(--fieldWidth) / 4 * -1) calc(var(--fieldHeight) / 2 * -1) / 100%

然後繪製大禁區部分,這部分本質是一個矩形,但是左邊線條和底部的線條是重合的,所以還需要繪製剩餘的三根線條,這裡為了減少一部分程式碼,其中兩條線使用 conic-gradient 繪製,剩餘的一條線使用 linear-gradient 繪製。

conic-gradient(
  from -90deg at right 2px bottom 2px,
   rgba(31, 157, 161, 0) 0 90deg,#fff 0) 0 calc((var(--fieldHeight) - var(--grandForbiddenAreaHeight)) / 2)/var(--grandForbiddenAreaWidth) var(--grandForbiddenAreaHeight) no-repeat,
linear-gradient(
  0deg,
  var(--lineColor) 2px,
  #0000 2px
) 0px calc((var(--fieldHeight) - var(--grandForbiddenAreaHeight)) / 2)/var(--grandForbiddenAreaWidth) 2px no-repeat

小禁區和大禁區實現方式是一樣的,只是在於區域的大小尺寸不一樣,增加兩個禁區後的效果如下:

接下來繪製罰球弧,這個是一條圓弧,貌似漸變不能單純的只繪製一條弧線,如果有知道的歡迎交流,這裡使用偽元素實現,基於偽元素的邊框加圓角並隱藏其中的三邊邊框即可達到期望的效果。

&::after {
  ...
  border: 2px solid #fff;
  border-radius: 50%;
  background: #0000;
  border-top-color: #0000;
  border-left-color: #0000;
  border-bottom-color: #0000;
}

此時的基本效果圖已經差不多了,再使用另外一個偽元素繪製一下球門的位置。

最後再增加一下球場內的草坪效果。這裡使用了重複線性漸變,程式碼如下:

repeating-linear-gradient(
  90deg,
  #56a224 0px,
  #56a224 10px, 
  #a9da27 10px, 
  #a9da27 20px
)

最後完整的效果圖如下:

線上程式碼:https://code.juejin.cn/pen/71...

最後

完整的實現過程就結束了,這只是一種實現的思路,在實際的專案中不建議使用,除此之外也還有很多其他的實現方式,歡迎討論你的實現方式。看完覺得有用記得點個贊再走,收藏起來說不定哪天就用上啦~

專注前端開發,分享前端相關技術乾貨,公眾號:南城大前端(ID: nanchengfe)

相關文章