CSS和SVG實現文字漸變、描邊、投影

XboxYan發表於2021-09-27

在一些 web 活動頁中經常能看到特殊處理的標題文字,比如這樣的

image-20210918103841918

暫時忽略掉特殊字型,通過設計稿的圖層樣式可以發現,共有 3 個文字特效,分別是漸變、描邊、投影

image-20210918105003942

作為一個有追求的前端,當然不會直接用圖片啦~ 這裡分別用 CSS 和 SVG 兩種方式來實現,一起看看吧

溫馨提示:文章細節較多,不感興趣的也可以直接跳到底部檢視線上 demo

一、CSS 文字漸變

首先來看 CSS 中的實現。

CSS 中並沒有直接的屬性來設定文字漸變,通常文字只能是純色。不過可以通過背景裁剪 background-clip讓背景色在文字區域顯示出來,看著就像是文字有了漸變

<p class="text">為你定製 發現精彩</p>
.text{
  background-image: linear-gradient(#FFCF02, #FF7352);
  background-clip: text;
    -webkit-background-clip: text;
}

但是這樣沒什麼效果,文字仍然是預設顏色

image-20210918113056809

原因其實很簡單,由於是裁剪的背景,最後展示的其實是背景顏色,有顏色的文字覆蓋在背景之上,所以這裡需要將文字顏色設定為透明就行了,用 color-webkit-text-fill-color都可以實現。

.text{
  background-image: linear-gradient(#FFCF02, #FF7352);
  background-clip: text;
    -webkit-background-clip: text;
  -webkit-text-fill-color: transparent; /*需要文字透明*/
}

這樣就可以看到文字漸變效果了

image-20210918113558250

二、SVG 文字漸變

再來看看 SVG 中的文字漸變。

SVG 中天然支援文字漸變,完全可以把文字當成普通的向量路徑,結構如下

<svg>
    <text>為你定製 發現精彩</text>
</svg>

直接通過 fill填充就行了,不過需要注意的是這裡填充稍微麻煩一點,漸變不能像 CSS 那樣,必須使用專門的漸變標籤 <linearGradient>,有興趣的可以檢視 linearGradient - SVG | MDN (mozilla.org),需定義在 <defs></defs>

<svg>
  <defs>
    <linearGradient id="gradient">
      <stop offset="0%"  stop-color="#FFCF02"/>
      <stop offset="100%"  stop-color="#FF7352"/>
    </linearGradient>
  </defs>
    <text class="text">為你定製 發現精彩</text>
</svg>

<linearGradient>中的 <stop> 標籤用來定義漸變的顏色坡度,offsetstop-color分別定義漸變的節點和顏色,然後通過 fill屬性填充漸變(指明 id )

.text{
  fill: url(#gradient);
}

效果如下(並不是圖片載入有問題)

image-20210918150224988

這樣下來有兩個問題

  1. 文字水平方向和垂直方向都不居中
  2. 漸變方向是水平向右的

首先看第一個問題。SVG 中對文字的自適應處理還是非常弱的,比如 CSS 中常見的自動換行 SVG 中只能手動在指定位置換行。這裡居中需要用到兩個屬性 text-anchordominant-baseline,分別標文字錨點對齊和文字基線對齊,簡單來說就是水平和垂直方向的對齊方式

.text{
  text-anchor: middle;
  dominant-baseline: middle;
  fill: url(#gradient);
}

同時 <text>還需要設定 xy位置,這裡的百分比可以和 CSS 中的背景位置百分比做類比

<text class="text" x="50%" y="50%">為你定製 發現精彩</text>

這樣就居中顯示了

image-20210918151535204

關於漸變方向的問題,SVG 中是用x1y1x2y2兩組座標來確定的。給定一個矩形,左上角是 [0,0],右下角是 [1, 1],這樣任意角度都可以表示出來了

image-20210918151901299

比如現在需要垂直向下方向的,那麼可以在<linearGradient>設定 x1="0" y1="0" x2="0" y2="1",如下

<svg>
  <defs>
    <linearGradient id="gradient" x1="0" y1="0" x2="0" y2="1">
      <stop offset="0%"  stop-color="#FFCF02"/>
      <stop offset="100%"  stop-color="#FF7352"/>
    </linearGradient>
  </defs>
    <text class="text">為你定製 發現精彩</text>
</svg>

效果如下

image-20210918152238267

三、CSS 文字描邊

CSS 中有個專門用於文字描邊的屬性 -webkit-text-stroke,可以控制描邊的寬度和顏色,比如

.text{
  -webkit-text-stroke: 2px #333;
}

效果如下

image-20210918154946939

確實有描邊了,但是文字好像瘦了一圈,如果覺得不太明顯,可以再設定大一點

image-20210918155647207

從這裡可以看出,-webkit-text-stroke其實是 居中描邊,並且是覆蓋在文字上的,也無法更改描邊方式。而事實上,很多設計工具都是可以選擇描邊方式的,比如 figma

image-20210918160301899

那麼,如何實現外描邊效果呢?

也是可以的!用兩層文字,一層文字描邊,一層文字漸變就可以了,為了節省標籤,可以用偽元素來生成

<p class="text" data-title="為你定製 發現精彩">為你定製 發現精彩</p>

::before設定漸變,位於上方,原文字設定描邊,位於下方,注意把 ::before-webkit-text-stroke去除

.text::before{
    content: attr(data-title);
    position: absolute;
    background-image: linear-gradient(#FFCF02, #FF7352);
    background-clip: text;
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
      -webkit-text-stroke: 0;
}
.text{
    -webkit-text-stroke: 6px #333;
}

疊加示意如下

Kapture 2021-09-19 at 12.32.04

改變不同的描邊也不會出現文字“變瘦”的情況

image-20210919123457209

四、SVG 文字描邊

SVG 也可以實現描邊效果,和 CSS 比較類似,應該說 CSS 是借鑑 SVG 的,通過 strokestroke-width來控制描邊顏色和大小,比如

.text{
  /*其他*/
  stroke-width: 4px;
  stroke: #333;
}

可以得到這樣的效果

image-20210919124453144

和 CSS 表現一樣,都是居中描邊,也無法改變。

不一樣的是,SVG 控制更為靈活,預設是先填充、然後再描邊,所以看著是描邊在填充之上,但是,我們可以改變這種規則,設定先描邊,再填充,那麼填充的顏色就會覆蓋在描邊之上了。SVG 中改變這種規則的可以通過 paint-order 來設定,關於這個屬性,有興趣的可以訪問張鑫旭老師的這篇文章:CSS paint-order祝大家元旦快樂

.text{
  /*其他*/
  stroke-width: 4px;
  stroke: #333;
  paint-order: stroke; /*先描邊*/
}

這樣就實現了外描邊效果,是不是比 CSS 方便許多?

image-20210919125130810

除此之外,SVG 還可以設定描邊路徑的轉角處的形狀,比如 figma 中關於轉角的設定如下

image-20210919125653465

SVG 中與之相對應的屬性叫做 stroke-linejoin,這裡是圓角,可以做如下設定

.text{
  /*其他*/
  stroke-width: 4px;
  stroke: #333;
  paint-order: stroke; /*先描邊*/
  stroke-linejoin: round; /*路徑轉角為圓角*/
}

各種屬性效果如下

image-20210919130558691

五、CSS 文字投影

繼續新增效果。CSS 可以通過 text-shadow來新增文字投影

.text{
    -webkit-text-stroke: 6px #333;
      text-shadow: 0 4px 0 #333;
}

結果變成了這樣

image-20210919131243404

原因其實還和文字漸變有關,漸變其實是背景色,文字是透明的,所以給文字新增陰影,結果陰影就覆蓋在了背景之上。除了使用text-shadow,還可以通過 drop-shadow濾鏡實現

.text{
    -webkit-text-stroke: 6px #333;
      filter: drop-shadow(0 4px 0 #333);
}

這樣就完美實現了

image-20210919131815511

六、SVG 文字投影

SVG 就比較靈活了,比如上面使用的 drop-shadow濾鏡,其實就是借鑑了 SVG 中的 <feDropShadow>濾鏡,所以 SVG 也可以這樣實現

<svg>
    <defs>
        <linearGradient id="gradient" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%"  stop-color="#FFCF02"/>
          <stop offset="100%"  stop-color="#FF7352"/>
        </linearGradient>
          <filter id="shadow">
            <feDropShadow dx="0" dy="4" stdDeviation="0" flood-color="#333"/>
        </filter>
    </defs>
    <text x="50%" y="50%" class="text">為你定製 發現精彩</text>
</svg>

這裡dxdystdDeviationflood-colordrop-shadow(dx,dy,stdDeviation,flood-color)中的引數是一一對應的,就不多說明了,然後在文字中應用濾鏡

.text{
  /*其他*/
  filter:url(#shadow);
}

這樣也能實現文字投影

image-20210919132653973

其實 SVG 中大可不必這麼麻煩,剛才上面 text-shadow之所以不能使用,就是因為 CSS 實現的文字漸變是背景,是假的文字漸變,但是 SVG 中是真真正正的漸變填充,所以沒錯,這裡可以直接用 CSS 中的 text-shadow 來實現,SVG 和 CSS 現在很多屬性和樣式都互通了,如下

.text{
    /*其他*/
      fill: url(#gradient);
      text-shadow: 0 4px 0 #333;
}

實現更加簡潔

image-20210919132959524

七、特殊字型處理

通常活動標題會採用一些特殊的字型,英文字型還好,整個引入都可以,但是中文就不行了,大多數中文字型都非常大,可能達到幾十MB或者幾百MB。其實我們只需要用到出現的字型,如果把出現的文字這一部分的特殊字型單獨提取出來,那麼整個字型檔案將大大減小,這個過程就叫做字型子集化。

那麼該如何處理呢?

這裡推薦一個工具 Fontmin - 字型子集化方案,關於字型子集化的原理,可以參考這篇文章:效能優化魔法師:中文字型實踐篇 - 掘金

image-20210922192856814

下載客戶端後,匯入字型檔案.ttf,然後輸入需要用到的文字,如下

image-20210922193402350

點選生成,可以得到如下檔案

image-20210922193601713

其中第一個以-embed為字尾的 CSS,裡面是轉換 base64 後的檔案,可以直接引入

@font-face {
    font-family: "HYLiLiangHeiJ Regular";
    src: url("HYLiLiangHeiJ Regular.eot"); /* IE9 */
    src: url("HYLiLiangHeiJ Regular.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
    url(data:application/x-font-ttf;charset=utf-8;base64,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) format("truetype"), /* chrome、firefox、opera、Safari, Android, iOS 4.2+ */
    url("HYLiLiangHeiJ Regular.svg#HYLiLiangHeiJ Regular") format("svg"); /* iOS 4.1- */
    font-style: normal;
    font-weight: normal;
}
.text{
  /*其他樣式*/
  font-family: "HYLiLiangHeiJ Regular";
}

這樣幾乎實現了和設計稿完全一致的效果

image-20210922194407525

其實如果從頭看下來,應該也能自己實現一個,既能掌握原理,也能加深印象,完全變成自己的了。不過可能不是每個同學都有時間,或者能夠靜下心來研究每一個案例,所以這裡還是整理了一下線上 demo,想要快速看結果的直接訪問就行了,如下

CSS 實現可以訪問 text-css (codepen.io)

SVG 實現可以訪問 text-svg (codepen.io)

八、總結和說明

以上介紹了 CSS 和 SVG 兩種不同的方式來實現文字的特殊效果,從效果來看,顯然 SVG 要更勝一籌,比如描邊更加平滑、也無需多層巢狀,但 CSS 也有優勢,比如漸變色和投影更加簡單,總結一下

  1. CSS 文字漸變本質是背景裁剪,需要將文字顏色設為透明
  2. SVG 文字天然支援漸變填充,需要藉助 linearGradient 標籤
  3. SVG 文字居中稍微麻煩點,需要藉助 text-anchor 和 dominant-baseline
  4. CSS 和 SVG 描邊都是居中描邊,並且無法改變
  5. CSS 外描邊可以通過多層結構疊加實現
  6. SVG 可以通過 paint-order 讓填充繪製在描邊之上
  7. CSS 文字陰影在文字透明時會穿透過來,可以用 drop-shadow 模擬投影
  8. SVG 中的 feDropShadow 和 CSS 中的 drop-shadow 類似
  9. SVG 可以直接用 CSS 中的 text-shadow 實現文字投影
  10. 字型子集化 fontmin

CSS 和 SVG 各有優勢,也相互影響,SVG 中也可以使用很多 CSS 樣式,CSS中也開始引入很多 SVG 屬性,在平時的開發中,可以充分結合兩者的優勢。最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤

相關文章