評價打分元件,SVG 半顆星的解決方案!

前端小智發表於2021-09-25
微信搜尋 【大遷世界】, 我會第一時間和你分享前端行業趨勢,學習途徑等等。
本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

對於一個內容服務的網站來說評價打分也是很重要的一部分,它有利於分析使用者對我們的內容的喜好程式。最近,我們團需要為一個專案實現一個星級評價的元件,需求如下:

  • 效能(不能用圖片)
  • 可調整的大小
  • 可訪問性
  • 小數位打分(如:3.53.2
  • 使用 css 就可以直接控制樣式

要達到上面的要求,經常調研,最終選擇了 SVG 方案。

任務

下圖是我們最終想要的效果:

image.png

我們主要的工作就是讓星星可以改變其顏色,描邊,大小,還可以顯示半顆星星。

實現

在實現之前,我們需要有一個基礎 SVG 結構,如下所示:

<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
    <path d="..."/>
</svg>

新增 aria-label

新增 aria-label 可以使用讀屏器使用者能夠訪問這一資訊。

<p aria-label="Rating is 4.5 out of 5">
   <svg width="32" height="32" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
      <path d="..."/>
   </svg>
</p>
aria-label屬性用來給當前元素加上的標籤描述,接受字串作為引數。是用不可視的方式給元素加label(如果被描述元素存在真實的描述元素,可使用 aria-labelledby 屬性作為來繫結描述元素和被描述元素來代替)。

如何重用SVG

我們可以把上面的SVG 標籤複製五次,或者提取path資料並儲存在某個地方,然後在不重複程式碼的情況下重新使用它。我們選擇後者。

首先,我們需要建立一個寬度和高度為零的SVG,這樣它就不會保留空間。

<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!-- Content -->
</svg>

在該SVG中,我們需要在<symbol>元素中包含path資料。根據MDN:

symbol 元素用於定義圖形模板物件,可以透過<use>元素來例項化。

<symbol>裡面的內容與圖示的內容相同。另外,新增一個 id 也很重要,這樣我們以後就可以引用這個 symbol

<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg">
    <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" id="star">
        <path d="..."/>
    </symbol>
</svg>

有了這個設定,我們現在可以用<use>元素來重用這個symbol。做法就是使用id作為href屬性的值。

<p class="c-rate">
    <svg class="c-icon" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon" width="32" height="32">
      <use href="#star"></use>
    </svg>
</p>

星星的樣式

有了上面的星星結構,我們現在來新增樣式:

<p class="c-rate">
    <svg class="c-icon active" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon active" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon active" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon active" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon" width="32" height="32">
      <use href="#star"></use>
    </svg>
</p>
.c-icon {
    --star-active: #fece3c;
    --star-inactive: #6c6962;
    fill: var(--star-inactive);
}

.c-icon.active {
    fill: var(--star-active);
}

執行後的結果:

image.png

半顆星

使用 SVG後做半顆星就很容易,有兩個很好的解決方案。第一個是使用<masks>,第二個是使用SVG 漸變。

使用<masks>

使用 masks 的目的是模擬擦除星星的一部分並將另一部分塗成半透明顏色的效果。

image.png

在上圖中,有一個正方形和一個星星。它們的交集就是我們想要的結果。

做法如下:

  • 建立一個可重用的SVG模板
  • 新增一個 <mask> 元素,位置為x=50%
  • 將 mask 應用到星星上
<!-- The reusable SVG template -->
<svg viewBox="0 0 32 32" id="star">
  <defs>
    <mask id="half">
      <rect x="50%" y="0" width="32" height="32" fill="white" />
    </mask>
    <symbol viewBox="0 0 32 32" id="star">
      <path d="..." />
    </symbol>
  </defs>
</svg>

<svg class="c-icon" width="32" height="32" viewBox="0 0 32 32">
    <use href="#star" mask="url(#half)" fill="green"></use>
</svg>

問題是,當一顆半透明的星星被遮住時,我們如何才能顯示它呢? 好吧,多虧了 SVG,我們可以在 <mask> 中包含多個元素。

<mask id="half">
  <rect x="0" y="0" width="32" height="32" fill="white" />
  <rect x="50%" y="0" width="32" height="32" fill="black" />
</mask>

mask 中,白色元素表示我們想要顯示的內容,黑色元素表示我們想要隱藏的內容。結合在一起時,我們可以建立一個cut-out effect效果。

image.png

注意,白色矩形被定位在0,0點,而黑色矩形被定位在50%,0。下面是它的效果:

image.png

塗寫的部分代表最終結果,半顆星。 現在,你的可能在想,如何新增另一個半透明的星星以使其更清晰?

透過使用比純黑更淺的顏色,我們將得到一個半透明的效果。這意味著目前被隱藏的區域將有一個淺色的星形顏色。

<mask id="half">
  <rect x="0" y="0" width="32" height="32" fill="white" />
  <rect x="50%" y="0" width="32" height="32" fill="grey" />
</mask>

image.png

到這,我們回顧一下完整的 SVG 標籤。

<svg style="width: 0; height: 0;" viewBox="0 0 32 32">
   <defs>
      <mask id="half">
         <rect x="0" y="0" width="32" height="32" fill="white" />
         <rect x="50%" y="0" width="32" height="32" fill="grey" />
      </mask>

      <symbol viewBox="0 0 32 32" id="star">
         <path d="..." />
      </symbol>
   </defs>
</svg>

<p class="c-rate">
   <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32">
      <use href="#star" mask="url(#half)" fill="green"></use>
   </svg>
   <!-- 4 more stars -->
</p>

這樣,我們就有了一個部分填充的恆星。這個解決方案的絕妙之處在於,我們不需要提供兩種色調, mask 會起作用的:

image.png

事例地址:https://codepen.io/shadeed/pe...

第一種方法到這就介紹完了,我們來看第二種方法。

帶有SVG漸變的半星

mask類似,我們需要在<defs>元素中定義一個漸變。

<svg style="width: 0; height: 0;" viewBox="0 0 32 32">
    <defs>
        <linearGradient id="half" x1="0" x2="100%" y1="0" y2="0">
            <stop offset="50%" stop-color="#f7efc5"></stop>
            <stop offset="50%" stop-color="#fed94b"></stop>
        </linearGradient>
    </defs>
</svg>

注意,我們有兩個色塊,第一個代表前半部分,第二個代表淺色陰影。在這個解決方案中,我們需要手動提供兩種顏色。

image.png

<p class="c-rate">
    <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32">
        <use href="#star" fill="url(#half)"></use>
    </svg>
</p>

事例地址:https://codepen.io/shadeed/pe...

輪廓樣式

接下來我們給星星做個輪廓,這樣看起來會更立體點。

image.png

SVG Mask 解決輪廓樣式的問題

要新增描邊,我們只需要在SVG元素中新增stroke。這將很好地工作全星。然而,對於部分的,它將被切斷,因為掩碼。這對完整的星星來說是很好的。然而,對於半顆,由於mask的原因,它將被遮住。

image.png

為了解決這個問題,我們需要另一個星形輪廓。可以透過複製<use>元素並刪除它的 mask 來實現這一點。

<p class="c-rate">
   <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32">
      <use href="#star" mask="url(#half)" fill="green"></use>
      <use href="#star" fill="none" stroke="grey"></use>
   </svg>
</p>

注意,我們有兩個<use>元素。一個帶mask的,一個只有 stroke 的。這就是使用SVG masks 實現輪廓樣式的方法。

事例地址:https://codepen.io/shadeed/pe...

SVG 漸變實現輪廓樣式

對於漸變解決方案,我們不需要複製圖示,因為沒有mask 。我們需要做的是新增一個stroke,它就完成了。

<svg style="width: 0; height: 0;" viewBox="0 0 32 32">
   <defs>
      <linearGradient id="half" x1="0" x2="100%" y1="0" y2="0">
        <stop offset="50%" stop-color="#f7efc5"></stop>
        <stop offset="50%" stop-color="#fed94b"></stop>
      </linearGradient>
   </defs>
</svg>

<p class="c-rate">
   <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32">
     <use href="#star" fill="url(#half)" stroke="grey"></use>
   </svg>
</p>

事業地址:https://codepen.io/shadeed/pe...

大小

透過使用CSS變數並確保SVG具有正確的viewBox屬性,我們可以輕鬆地調整它們的大小。

.c-icon {
    width: var(--size, 24px)
    height: var(--size, 24px);
}

.c-icon--md {
    --size: 40px;
}

.c-icon--lg {
    --size: 64px;
}

~完,我是刷碗智,我要去 SPA 了,我們下期見~


原文:https://ishadeed.com/article/...

編輯中可能存在的bug沒法實時知道,事後為了解決這些bug,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug

交流

有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。

本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

相關文章