[譯]在 Vue 中編寫 SVG 圖標組件

在 Vue 中編寫 SVG 圖標組件

一種相似圖標字體的酷方法來使用 SVG

[Harpal Singh](https://unsplash.com/@aquatium?utm_source=medium&utm_medium=referral) 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 拍攝的照片

在考慮了將矢量圖標從圖標字體遷移到內聯 SVG 的緣由以後,我在 Vue.js 中找到了一個用 SVG 替換圖標字體的解決方案,同時仍能保持使用圖標字體的靈活性和易用性——可以使用 CSS 輕鬆改變圖標的大小、顏色以及其它屬性。css

一種流行的方法是使用 v-html 指令和 npm 模塊 html-loader 來將 SVG 導入到咱們的 Vue 模板中,並在 Vue 的生命週期函數 mounted() 中修改渲染的 <svg> 元素。CSS 樣式能夠直接應用到 <svg> 元素或者是其父元素上,而且這些可以組成一個可複用的組件。html

建立 Svg-icon 組件

讓咱們建立 Svg-icon.vue 組件文件,並在裏面接收三個 prop 變量。前端

  1. icon 是一個字符串類型的 prop 變量用來傳遞 .svg 文件名的導入
  2. hasFill 是一個布爾類型的 prop 變量來告訴組件 fill 屬性是否用於更改 <svg> 元素的顏色,默認值爲 false 即不使用 fill 屬性
  3. growByHeight 是一個布爾類型的 prop 變量來決定 heightwidth 是否相對於 font-size 進行縮放,默認值爲 true 即便用 height
<script>
function recursivelyRemoveFill(el) {
    if (!el) {
        return;
    }
    el.removeAttribute('fill');
    [].forEach.call(el.children, child => {
        recursivelyRemoveFill(child);
    });
}
export default {
    name: 'svg-icon',
    props: {
        icon: {
            type: String,
            default: null
        },
        hasFill: {
            type: Boolean,
            default: false
        },
        growByHeight: {
            type: Boolean,
            default: true
        },
    },
    mounted() {
        if (this.$el.firstElementChild.nodeName === 'svg') {
            const svgElement = this.$el.firstElementChild;
            const widthToHeight = (svgElement.clientWidth / svgElement.clientHeight).toFixed(2);
            if (this.hasFill) {
                // recursively remove all fill attribute of element and its nested children
                recursivelyRemoveFill(svgElement);
            }
            // set width and height relative to font size
            // if growByHeight is true, height set to 1em else width set to 1em and remaining is calculated based on widthToHeight ratio
            if (this.growByHeight) {
                svgElement.setAttribute('height', '1em');
                svgElement.setAttribute('width', `${widthToHeight}em`);
            } else {
                svgElement.setAttribute('width', '1em');
                svgElement.setAttribute('height', `${1 / widthToHeight}em`);
            }
            svgElement.classList.add('svg-class');
        }
    }
}
</script>

<template>
    <div v-html="require(`html-loader!../assets/svg/${icon}.svg`)" class="svg-container"></div>
</template>

<style lang="scss" scoped>
.svg-container {
    display: inline-flex;
}
.svg-class {
    vertical-align: middle;
}
</style>
複製代碼

咱們將 .svg 圖標文件經過 require() 傳遞給 html-loader 方法,該方法會將文件字符串化,而且經過 v-html 指令將其渲染爲 <svg> 元素。vue

全部對 <svg> 元素修改的地方都在 mounted() 生命週期方法裏面。node

  • 將由 growByHeight 定義的 <svg> 元素的 heightwidth 屬性設置爲 1emfont-size 的一倍)並對另外一個元素使用 widthToHeight。因爲並不是全部的 SVG 都是正方形的,所以咱們從渲染的元素計算 withToHeight 比率,以便 SVG 在父元素的 font-size 屬性大小改變的時候按比例縮放到其原始尺寸。
  • 爲了設置 <svg> 元素的 fill 屬性,咱們須要覆蓋掉 SVG 文件內部附帶的 fill 屬性。當 hasFill 值爲 true 的時候,咱們從 <svg> 元素及其子元素中遞歸地刪除 fill 屬性。而後使用 CSS 選擇器將 fill 值添加到其父元素或 <svg> 元素就能夠了。
  • 還能夠向元素中添加例如 class 等其它 DOM 屬性,這些屬性可用於設置組件中的範圍樣式

建立微笑圖標

讓咱們使用 Font Awesome 和咱們的 Svg-icon 組件中的圖標字體建立一個微笑圖標。android

smile-solid, credit: [https://fontawesome.com/license](https://fontawesome.com/license)

使用圖標字體

<template>
  <i class="fas fa-smile smile-icon"></i>
</template>

<style lang="scss" scoped>
.smile-icon {
  font-size: 24px;
  color: #aaa;

  &:hover {
    color: #666;
  }
}
</style>
複製代碼

.smile-icon 類的 CSS 選擇器以及僞類選擇器 :hover 來設置圖標的 font-sizecolor 屬性。ios

使用 Svg-icon 組件

<script>
import SvgIcon from './components/Svg-icon';

export default {
  name: 'my-component',
  components: {
    'svg-icon': SvgIcon,
  },
}
</script>

<template>
  <div class="smile-icon">
    <svg-icon icon="smile-solid" :hasFill="true"></svg-icon>
  </div>
</template>

<style lang="scss" scoped>
.smile-icon {
  font-size: 24px;
  fill: #aaa;

  &:hover {
    fill: #666;
  }
}
</style>
複製代碼

上面的實現和圖標字體方法相同,除了 .smile-icon 類在父元素中,在 Svg-icon 組件中,color 屬性被替換爲 fill。咱們還必須確保 smile-solid.svg 文件位於 Svg-icon 組件的 require() 方法指定的路徑(./assets/svg/)中。git

渲染成 HTML

這是由 v-html 的輸出渲染的 HTML。注意:會刪除掉全部的 fill 屬性,並將 heightwidth 屬性添加到 <svg> 中。github

<div class="smile-icon">
  <svg height="1em" width="1em" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="smile" class="svg-inline--fa fa-smile fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512">
    <path d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm80 168c17.7 0 32 14.3 32 32s-14.3 32-32 32-32-14.3-32-32 14.3-32 32-32zm-160 0c17.7 0 32 14.3 32 32s-14.3 32-32 32-32-14.3-32-32 14.3-32 32-32zm194.8 170.2C334.3 380.4 292.5 400 248 400s-86.3-19.6-114.8-53.8c-13.6-16.3 11-36.7 24.6-20.5 22.4 26.9 55.2 42.2 90.2 42.2s67.8-15.4 90.2-42.2c13.4-16.2 38.1 4.2 24.6 20.5z">
    </path>
  </svg>
</div>
複製代碼

過渡到 SVG

Credit: [https://tympanus.net](https://tympanus.net)

因爲 SVG 被認爲是將來的發展方向,所以最好是在保留圖標字體的易用性的基礎上,逐步放棄使用圖標字體。Svg-icon 組件是一個例子,告訴了咱們如何使用可用的庫來抽離出 <svg> 元素中的混亂部分,同時模仿使用圖標字體的好處!npm

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索