大家好,我是零一,今天給大家表演 僅用一個HTML標籤實現帶動畫的抖音LOGO,涉及了很多知識點,歡迎交流討論
先上結果,最終實現效果如下:

還原度應該還可以吧?
抖音Logo結構想要用CSS來畫抖音的Logo,前提要先了解它的構造,一定是一些幾何圖形的拼接組合,因為之前很多業界大佬已經扒過抖音的Logo的結構了,我就拿來借用一下:

好的,有點複雜,簡化一下,其實就是 4 個部分

每個顏色劃出來的區域代表一個部分,所以最後是:1/4圓環 + 半圓 + 長條矩形 + 半徑略大一些的1/4圓環
製作思路回到本文標題,有人要說我標題黨了,這 logo 都劃分成四個部分了,你怎麼用一個標籤搞定呢?就算你用 ::before 和 ::after,也加上標籤本體一共也就三個部分
而且!抖音的 logo 是有兩層的:

可以看到,是一個青色的音符和一個紅色的音符疊加的
所以! 這麼多元素,你怎麼用一個標籤完成呢?而且還說帶動畫,是不是逗我們玩呢?
好了,別急,咱們先捋一下思路哈:
如何在一個偽元素中一筆畫出整個音符圖案呢?
💡 想到辦法了,超級簡單,給大家演示一下
<style>/*為了保證文章整潔,省略一些代碼...*/.douyin::before{background:url('青色的音符.png')}.douyin::affter{background:url('紅色的音符.png')}</style><divclass="douyin"/>好了,輕鬆搞定,本文結束!鼓掌👏🏻 大家 點讚、收藏、轉發 走起~

別罵了,別罵了,剛剛跟大家開了個小玩笑,正文走起!
我們肯定是要用到 background 屬性的,不然哪來的色塊啊,去扒一下 MDN 文檔:

都不用想了,只有一個 background-image 有用,那再具體看看:

劃重點了!同學們,background-image 支持為一個元素設置一個或多個圖像,來看一下其支持哪些圖像類型:

看了語法,發現基本上 <image> 類型支持的都是直接設置圖片的,唯獨有一個支持漸變函數的,例如:linear-gradient、repeating-linear-gradient、radial-gradient、conic-gradient ...
如果你還不會看CSS的語法,可以看我之前寫的 熱議:CSS為什麼這麼難學?一定是你的方法不對,超詳細地講解了如何解讀CSS的語法(帶實戰的)
什麼是漸變函數呢?根據它們的單詞名字可以知道,支持了 線性、徑向(其實就是圓)、錐形 的顏色漸變。我們用前兩個就可以滿足抖音 logo 的構造了
因為根據 MDN 上的解釋,我們是可以使用多個漸變函數來控制元素的背景圖像的,多個值用 , 隔開,例如官方的例子:
background-image:linear-gradient(rgba(0,0,255,0.5),rgba(255,255,0,0.5)),url("../../media/examples/lizard.png");用個比較形象的比喻,background-image 就像我們寫字一樣,寫字需要一筆一划寫,而 background-image 中 , 隔開的每一個值就像每一個筆畫,這些值共同組成了一個 "圖像"
那我們就可以藉助這些函數來畫出抖音的logo了
開搞開搞先來測量一下抖音 logo 中 音符 的長寬比,為了等會給音符留出一定的空間

特地用截圖工具圈出了紅色音符的部分,得到的寬高是 248 * 285,計算一下寬高比就約等於 248/285 = 0.87,那我們就要在中間留出一個寬高比為 0.87 的矩形位置給音符
打地基那就先打地基!
<style>.douyin{width:100px;aspect-ratio:0.87;/*寬高比0.87*/border-radius:25%;padding:20pxcalc(20px+100px/0.87*0.13/2);/*四周留白,中間騰出位置給音符*/background-color:#000;}</style><divclass="douyin"/>這裡需要解釋一下 padding 的值的設定,20px 是我隨便設置的一個邊距大小,既然頂部和底部都是 20px,而且本身整體元素的寬高比又不是 1:1(整體不是正方形),那為了視覺上的居中和整體寬高的 1:1,我們需要將左右邊距增大至整體寬度與高度相等
因此 100px / 0.87 拿到的就是整體的高度,再乘以 0.13 拿到的就是寬高的差值,因為要平均分到兩邊,所以還要除以 2
現在妥妥是個正方形了,當前的效果:

這裡為了讓等會的音符只在圖中的那塊兒區域繪製,我們給外部容器設置一下 display: grid,等會還需要藉助 grid 布局的能力
.douyin::before,.douyin::after{content:'';grid-area:1/1;/*居中展示*/}畫1/4圓環如何畫圓環?用一個簡單的例子來演示一下:
<style>.demo{/*demo是一個正方形*/background:radial-gradient(100%100%at100%100%,transparent050%,red50%100%,transparent,);}</style><divclass="demo"/>我們就得到了一個這樣的圖形:

怎麼得到這樣的 1/4圓環 的呢?我們把樣式拆解一下:
100% 100% at 100% 100%:at 的左側 表示圓(或橢圓)在橫向、豎向的半徑長度;at 的右側 表示圓形在坐標軸上的位置
那對應到這個圖上就是:

radial-gradient() 函數除了第一個參數,其餘的參數都表示 顏色及漸變程度
因此 transparent 0 50% 表示從 圓心 開始到 半徑為一半長度的位置 顏色為 透明
這裡怕大家看不出來,我把 transparent 改成 blue,放效果圖給大家看:

原理同上一個,從半徑為 50% 的位置一直到半徑為 100% 的部分,顯示紅色
效果圖為:

其實只有 黃色箭頭 所指出的這個區域是我們代碼造成的,那為什麼一直到正方形的左上角都是紅色呢?因為radial-gradient() 函數需要最後設置一個 color-stop,請看下面
transparent這也是函數的最後一個參數,表示漸變以透明色 為結束,即從上一個位置(red 50% 100%)的結束位置開始一直到容器的邊緣,都顯示為透明
現在再來看看效果:

這樣一個 1/4圓環 就畫好了
那麼回到我們的正文來
.douyin::before,.douyin::after{content:'';grid-area:1/1;/*居中展示*/+background:+radial-gradient(+100%100%at100%100%,+transparent050%,+#08fff950%100%,+transparent+);}現在咱們存放音符的容器是一個寬高比為 0.87 的長方形,如果按照我們剛剛畫矩形的代碼來,最後出來的應該是這樣的:

很明顯,圓環的兩端寬度不一致,此時我們可以利用 background-size 對其進行壓縮,以此得到一個寬度一致的圓環,我擺爛了,懶得計算了,直接控制台微調吧

這下差不多等寬了,且大概是一個標準的 1/4圓環,然後咱們要把它放到左側中間靠下一點的位置,代碼如下:
.douyin::before,.douyin::after{content:'';grid-area:1/1;/*居中展示*/background:radial-gradient(100%100%at100%100%,transparent050%,#08fff950%100%,transparent)+left52%/41%36%no-repeat;}有內味兒了,有沒有?

原理都相似,就放一個半圓的生成以及位移過程圖吧:

代碼如下,也不過多解釋各種數值的意義了,因為我全是微調的:
.douyin::before,.douyin::after{content:'';grid-area:1/1;/*居中展示*/background:radial-gradient(100%100%at100%100%,transparent050%,#08fff950%100%,transparent)left52%/41%36%no-repeat,+radial-gradient(+50%100%attop,+transparent44%,+#08fff945%98%,+transparent+)0100%/73%31%no-repeat;}畫長條長條可能跟圓環和半圓不太一樣,它用到的是 linear-gradient() 線性函數,我們也不搞花里胡哨的操作,就直接把整個區域都鋪滿顏色,然後通過橫縱縮放得到一個長方形吧
.douyin::before,.douyin::after{content:'';grid-area:1/1;/*居中展示*/background:radial-gradient(100%100%at100%100%,transparent050%,#08fff950%100%,transparent)left52%/41%36%no-repeat,radial-gradient(50%100%attop,transparent44%,#08fff945%98%,transparent)0100%/73%31%no-repeat,+linear-gradient(#08fff9,#08fff9)66%0/20%70%no-repeat;}效果過程動畫如下:

再次略過講解,直接看代碼:
.douyin::before,.douyin::after{content:'';grid-area:1/1;/*居中展示*/background:radial-gradient(100%100%at100%100%,transparent050%,#08fff950%100%,transparent)left52%/41%36%no-repeat,radial-gradient(50%100%attop,transparent44%,#08fff945%98%,transparent)0100%/73%31%no-repeat,linear-gradient(#08fff9,#08fff9)66%0/20%70%no-repeat,+radial-gradient(+100%100%at100%0,+transparent058%,+#08fff958.5%99%,+transparent+)100%0/47%41.8%no-repeat;}效果圖如下:

到此為止,一個音符就畫好了,離成功只剩一步之遙
拆分剛剛咱們的代碼時把 ::before 和 ::after 放在一起寫的,其實現在是兩個一模一樣的音符完全重疊,而且現在兩個音符的顏色也是一樣的,我們來改造一下
顏色通過變量獲取為了代碼不冗餘,咱們把剛才代碼中所有的 #08fff9 用變量來獲取,即 #08fff9 => var(--color)
.douyin::before,.douyin::after{content:'';grid-area:1/1;/*居中展示*/background:radial-gradient(100%100%at100%100%,transparent050%,var(--color)50%100%,transparent)left52%/41%36%no-repeat,radial-gradient(50%100%attop,transparent44%,var(--color)45%98%,transparent)0100%/73%31%no-repeat,linear-gradient(var(--color),var(--color))66%0/20%70%no-repeat,radial-gradient(100%100%at100%0,transparent058%,var(--color)58.5%99%,transparent)100%0/47%41.8%no-repeat;}並單獨給 ::before 和 ::after 設置顏色變量
+.douyin::before{+--color:#08fff9;+}+.douyin::after{+--color:#f00044;+}除此之外,我們要移動其中一個音符,讓兩個音符不再重疊
.douyin::before{--color:#08fff9;}.douyin::after{--color:#f00044;+transform:translate(3%,3%);}看看效果

好了,但兩個音符錯位了,但是顏色的混合效果好像還沒有,這時候要用到 mix-blend-mode 屬性了,MDN的定義就是使當前元素與其父元素的內容和背景以某種方式混合,支持的屬性有些多,本文就不跳出去講太多別的東西了,我直接在控制台一個個試過去,發現 lighten、plus-lighter、screen 都是能達到我們的效果的,不過具體作用我還不是很了解,日後可以學習一下
請看嘗試過程👇

最後我們就設置下 mix-blend-mode: lighten吧
wow! 我們的 Logo 製作好啦!

抖音怎麼不能不抖?
我們現在設置的是紅色的音符向右向上偏移 3%,那我們現在就要這兩個音符都抖起來,其實就是修改它們各自的偏移量。又要改造一下代碼了!
.douyin::before{--color:#08fff9;transform:translate(calc(var(--x,0%)-3%),calc(var(--x,0%)-3%));}.douyin::after{--color:#f00044;transform:translate(calc(3%-var(--x,0%)),calc(3%-var(--x,0%)));}/*hover時,設置偏移變量--x*/.douyin:hover::before,.douyin:hover::after{--x:0.1%;transition:transformcubic-bezier(.5,300,.5,-150).3s;}請看效果:

本來還想把我寫過的一個 文字故障風 的效果加到這個 Logo 里的,一定很酷,但是有些無能為力,因為要給音符設置故障風效果,是要用到偽元素的,而現在音符本身已經是偽元素了,我不能脫離了我本文的標題 "僅用一個html標籤,實現帶動畫的抖音Logo" ,如果你感興趣,可以下去自己加上,到時候記得艾特我,我也想看看效果
想不到什麼花里胡哨的動畫了,最後再給大家表演一下 抖音Logo 的 "異變" 吧
準備好了嗎?
3~
2~
1~

哇!不得不說,太好看了!哈哈哈哈,其實實現原理也不難,我只是給元素加了個 filter: invert(1); 的屬性
最後怎麼樣,我是不是沒有標題黨?確實是 僅用一個HTML標籤,實現了一個帶動畫的抖音Logo 吧?
最後希望本文對大家有所幫助,零一能力有限,如果本文有任何錯誤,歡迎評論區指出;如果你有更多的奇思妙想,也歡迎評論區跟我一起探討~
也貼心得給大家準備了完完整整的示例代碼,需要的小夥伴可以自行查看
完整代碼:https://github.com/zero2one3/code-example/tree/master/css/write_tiktok_logo_with_one_html_tag.html
我是零一,分享技術,不止前端!下期見~
往期推薦
小程序的鼻祖在國內就這麼消亡了!
不用跑項目,組件效果所見即所得,絕了!
86張腦圖,一口氣看完 React
我是傻x,被迫看了 1 天源碼,千萬別學我!
12個可能你沒見過,但非常實用的 HTML 標籤
CSS狀態管理,玩出花了!
創作不易,加個點讚、在看支持一下哦!