SVG样式相互干扰的问题

8
我有一堆用Illustrator生成的标志,我想直接将它们嵌入到我的网站中。这些svg都有一个<style>元素,在其中定义了在SVG元素内部使用的样式,类似于这样:
<svg>
  <style>
    .st1 { fill:#ff00ff; }
    .st2 { fill:#ff3421; }
    /* ... and so on */
  </style>
    <!-- svg paths and shapes -->
</svg>

问题在于这些样式彼此干扰。因此,如果最后一个图像定义了. st21 {fill:#555555} ,则该样式将应用于所有具有 class =“st21”的路径,包括所有先前加载的SVG图像的路径。
在另一个线程中,有人建议我使用 标记包装我的svg-xml,但似乎不起作用。
如何确保内联SVG样式不相互干扰,而不触及实际的SVG代码?
这是一个pen来说明问题:https://codepen.io/pwkip/pen/RLPgpW

如果可能,请使用<img>将它们嵌入。同时,在Illustrater导出SVG时,请告诉您的图形设计师将CSS属性设置为演示属性。 - lumio
@lumio 谢谢。演示属性起作用了,但是它使文件大小从10kB增加到了180kB,所以我正在寻找一种方法来防止样式相互干扰。使用img而不是object进行包装似乎行不通。 - Jules Colle
“wrap”这个词不在我的答案中,是你自己想象/添加的。object/iframe标签用于外部内容,它们并不“包裹”任何东西。 - Robert Longson
你可以解析样式容器并手动应用样式。 - lumio
我有一个类似的情况,但由于相同的ID,样式被搞乱了。不得不在其中一个SVG中更改ID :) - Oleksandr Oliynyk
2个回答

6
我建议首先使用适当的CSS属性导出SVG。在从Illustrator导出时选择:style attributes,在SVG中会是这样的:

<path style="fill: red"></path>

这可能会增加文件大小,但它肯定可以完成任务。我在这里找到了一个很好的解释:这里

我想我会确保设计师将来会做到这一点。这可能是我得到的最有用的答案,但它涉及更改SVG代码,所以无法接受。不过还是加1分吧 ;) - Jules Colle

0

我想出了一个JavaScript解决方案。虽然如果你使用大量SVG,这可能有点过度和缓慢。但到目前为止,这个方法运行得很好。

我的做法是,我遍历所有的SVG并收集/解析它们的CSS样式。我收集所有的类名和属性,并手动应用于SVG元素上。

const svgCollection = document.querySelectorAll( 'svg' );

function parseStyles( styleTag ) {
  if ( !styleTag ) {
    return {};
  }
  
  const classCollection = {};
  
  const plain = styleTag.innerHTML;
  const regex = /\.([^\s{]+)[\s]*\{([\s\S]+?)\}/;
  const propertyRegex = /([\w\-]+)[\s]*:[\s]*([^;]+)/;
  const result = plain.match( new RegExp( regex, 'g' ) );
  if ( result ) {
    result.forEach( c => {
      const classResult = c.match( regex );
      const propertiesResult = classResult[ 2 ].match( new RegExp( propertyRegex, 'g' ) );
      const properties = propertiesResult.reduce( ( collection, item ) => {
        const p = item.match( propertyRegex );
        collection[ p[ 1 ] ] = p[ 2 ];
        return collection;
      }, {} );
      
      classCollection[ classResult[ 1 ] ] = properties;
    } );
    
    return classCollection;
  }
  
  return {};
}

function applyProperties( element, properties ) {
  if ( !properties ) {
    return;
  }
  
  Object.keys( properties ).forEach( key => {
    element.style[ key ] = properties[ key ];
  } );
}

function applyStyles( element, styles ) {
  const classNames = ( element.getAttribute( 'class' ) || '' ).split( ' ' );
  classNames.forEach( c => {
    applyProperties( element, styles[ c ] );
  } );
  element.setAttribute( 'class', '' );
}

for ( let i = 0; i < svgCollection.length; i += 1 ) {
  const svg = svgCollection[ i ];
  const styles = parseStyles( svg.querySelector( 'style' ) );
  const elements = svg.querySelectorAll( '[class]' );
  for ( let j = 0; j < elements.length; j += 1 ) {
    applyStyles( elements[ j ], styles );
  }
}
<p>this shape should be blue:</p>
<svg height="210" width="210">
  <style>
    .st1 {
      fill:blue;
    }
  </style>
  <polygon points="100,10 40,198 190,78 10,78 160,198" class="st1"/>
</svg>
<p>this shape should be red:</p>
<svg height="210" width="210">
  <style>
    .st1 {
      fill:red;
    }
  </style>
  <ellipse cx="105" cy="80" rx="100" ry="50" class="st1" />
</svg>

尽管这样做效果很好,但我不建议这样做(如你所询问的评论中所提到的)。最好在Illustrator中将CSS属性设置为演示属性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接