svg之間還能互相影響?

背景

最近搞了一個項目,基於React,用了本身的腳手架fdt。css

svg的使用

項目就不上圖了,總之其中存在各式各樣的圖標。最終選擇了svg來控制圖標的顯示,緣由主要有以下幾方面:html

  • 相比傳統的圖片,尺寸更小,可壓縮性更強
  • 可伸縮,更清晰
  • 方便讀取和修改
  • 設計軟件直接導出

svg的使用方式多種多樣,適合本身的纔是最好的。下面簡單介紹下咱們的項目如何在fdt腳手架中使用了svg。由於fdt基於webpack打包,因此webpack中必不可少須要增長針對svg的配置。代碼以下:react

// webpack.config.js

{
    test: /\.svg$/,
    use: [
    {
        loader: path.resolve(__dirname, "./fdtsvgloader")
    },
        "svg-loader"
    ],
    include: [path.resolve(opts.baseDir, "src")],
    exclude: [path.resolve(opts.baseDir, "src/svginline")]
}
複製代碼

咱們依然使用svg-loader進行svg的處理,可是svg-loader返回的是一個包含attributescontent的對象,咱們沒法直接使用。處理後的結果以下代碼所示:webpack

module.exports = {
  attributes: {
    xmlns: 'http://www.w3.org/2000/svg',
    viewBox: '0 0 1024 1024'
  },
  content: '<path d="M441.9 167.3l-19.8-19.8c-4.7-4.7-12.3-4.7-17 0L224 328.2 42.9 147.5c-4.7-4.7-12.3-4.7-17 0L6.1 167.3c-4.7 4.7-4.7 12.3 0 17l209.4 209.4c4.7 4.7 12.3 4.7 17 0l209.4-209.4c4.7-4.7 4.7-12.3 0-17z"/>'
}
複製代碼

所以,咱們在fdt中單獨寫了一個loader來獲得咱們想要的svg格式。代碼以下:web

// fdtsvgloader.js

module.exports = function(source) {
  return `
    ${source}
    var fdtsvg = require('fdt-svg-loader')
    module.exports = fdtsvg(module.exports)
    `;
};

// fdt-svg-loader

var React = require("react");
module.exports = function(svg) {
  const content = svg.content;
  return function(props) {
    const newprops = { viewBox: "0 0 1024 1024", height: "20px", fill: "#000" };
    newprops.dangerouslySetInnerHTML = { __html: content };
    newprops;
    return React.createElement("svg", { ...newprops, ...props });
  };
};

複製代碼

其中,fdtsvgloader.js中接受的參數即爲svg-loader處理後的結果。最後,通過fdt-svg-loader處理後,獲得了React建立的svg元素,幷包含默認屬性viewBox,height以及fill值。所以咱們在組件中能夠以下方式引用svg:瀏覽器

// demo.tsx

import PptIcon from "@/image/newppt.svg";

export default class Demo extends Component {
    render() {
        return <PptIcon width="18" height="18" viewBox="0 0 27 34" />;
    }
}   

複製代碼

demo中傳遞的屬性即可覆蓋默認屬性,靈活控制svg的大小。至此,咱們在項目中愉快的使用svg來控制各式各樣圖標的顯示。安全

遇到的問題

效果圖
可是忽然有一天,在一個慵懶的午後,咱們的測試同窗忽然告訴我,頁面中的圖標從左邊變成了右邊的樣子。短暫的驚慌以後,我迅速抄起鍵盤尋找bug的所在之處。 實不相瞞,在作此項目以前,我較少涉獵svg的知識,對於svg我自己並非很熟悉。所以,尋找bug的過程當中遇到了一些困難和挫折。在較短的時間內並無迅速找到問題所在,所以我首先想到的是從新更換一下圖標(設計軟件中導出的圖標咱們作過手動處理)。我驚奇的發現,這個方法竟然好用,果斷完成上線。 過後慢慢琢磨這個事情,一個svg圖片並無受到外部CSS的影響,爲何會忽然致使問題呢?爲了更快的發現問題,我仔細研究了一下咱們的svg圖標的結構,學到了一些關於svg的內容:

  • <g>該標籤表明組合
  • <defs>定義重用圖形
  • <polygon>定義多邊形
  • <mask>定義蒙層
  • <use>實現SVG現有圖形的重用

既然沒法直接找到答案,那隻好上排除法來尋找問題所在了。最後發現,問題出現的緣由是咱們新引入的圖標影響了原有圖標。svg互相影響也真的讓我很是震驚。 那究竟是怎麼互相影響的呢?緣由就是新的圖標中定義了一個mask蒙層,屬性id爲mask-2。受影響的圖標中,path標籤的mask屬性引用了該mask-2的蒙層,致使新圖標的出現影響了部分舊圖標。 那麼對於直接在html中引入svg,瀏覽器對於重用圖標的尋找機制是怎麼樣的呢?咱們作了以下測試:bash

<svg width="400" height="300">
        <defs>
            <linearGradient id='white2black'>
                <stop offset="0" stop-color="white"></stop>
                <stop offset="100%" stop-color="black"></stop>
            </linearGradient>
            <mask id="opacity">
                <rect x="0" y="0" width="400" height="300" fill="url(#white2black)"></rect>
            </mask>
        </defs>
        <rect id="back" x="0" y="0" width="400" height="300" fill="#d4fcff"></rect>
        <rect id="front" x="0" y="0" width="400" height="300" fill="#fcd3db" mask="url(#opacity)"></rect>
    </svg>
    <svg width="600" height="300">
        <defs>
            <linearGradient id='white2black'>
                <stop offset="0" stop-color="blue"></stop>
                <stop offset="50%" stop-color="black"></stop>
                <stop offset="100%" stop-color="green"></stop>
            </linearGradient>
            <mask id="opacity">
                <rect x="50" y="0" width="600" height="400" fill="green"></rect>
            </mask>
        </defs>
        <rect id="back" x="0" y="0" width="400" height="300" fill="#d4fcff"></rect>
        <rect id="front" x="0" y="0" width="400" height="300" fill="#fcd3db" mask="url(#opacity)"></rect>
    </svg>
複製代碼

該代碼的展現效果爲: svg

aaaaaaaaaaaa
當我把第一個svg的mask標籤刪除以後

<svg width="400" height="300">
        <defs>
            <linearGradient id='white2black'>
                <stop offset="0" stop-color="white"></stop>
                <stop offset="100%" stop-color="black"></stop>
            </linearGradient>
        </defs>
        <rect id="back" x="0" y="0" width="400" height="300" fill="#d4fcff"></rect>
        <rect id="front" x="0" y="0" width="400" height="300" fill="#fcd3db" mask="url(#opacity)"></rect>
    </svg>
    <svg width="600" height="300">
        <defs>
            <linearGradient id='white2black'>
                <stop offset="0" stop-color="blue"></stop>
                <stop offset="50%" stop-color="black"></stop>
                <stop offset="100%" stop-color="green"></stop>
            </linearGradient>
            <mask id="opacity">
                <rect x="50" y="0" width="600" height="400" fill="green"></rect>
            </mask>
        </defs>
        <rect id="back" x="0" y="0" width="400" height="300" fill="#d4fcff"></rect>
        <rect id="front" x="0" y="0" width="400" height="300" fill="#fcd3db" mask="url(#opacity)"></rect>
    </svg>
複製代碼

效果變爲: 測試

aaaaaaaaaaaa
至此,真相已經付出了水面。svg在尋找重用元素時的機制爲:在當前HTML環境中尋找第一個匹配的元素。並非咱們想象中的在svg本身內部尋找或者逐層往外尋找。因此,在html中直接引入svg必然會存在互相影響的問題,也必然會帶來一些未知的風險。那麼到底有沒有較爲安全的方案呢?我以爲方法總比困難多,解決方案確定是存在的。爲了防止互相影響,咱們能夠採用使用css中的background-image屬性將svg引入,該方案中的svg在尋找重用元素時,僅僅會尋找自身標籤內是否存在,而不會向外尋找,所以必定程度上保證了svg圖標的安全性。那麼咱們之後要一直使用該種方案嗎?我以爲仍是分場景使用最爲合適。例如:一成不變的svg圖標能夠採用css的方式引入,帶有交互行爲的圖標能夠採用html的方式引入,方便修改樣式。固然,我以爲最重要的就是,對於咱們直接使用的圖標,svg內最好乾淨的僅剩下path標籤,這樣不會帶來任何問題。

一樣,svg sprites使用use引入一樣存在問題。。

相關文章
相關標籤/搜索