SVG< use>元素在动态环境下在iOS或Safari上不起作用

hec6srdp  于 2023-05-23  发布在  iOS
关注(0)|答案(1)|浏览(210)

我无法让svg <use>元素在任何浏览器中在iOS上工作。它也不能在Mac上的Safari上工作。它在Android和我在PC或Mac上测试过的所有其他浏览器上都能工作。换句话说,这可能是一个webkit问题。当<path> className根据系统/用户输入和更改动态更新时,环境为React。这个<path>元素是使用viewBox隐藏的,并由一个大svg元素中的<use>元素引用。这是React jsx生成的HTML:

<form
   <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="0" height="0" viewBox="0 0 0 0">
   ...
      <path id="path9146scheduleamap" name="path9146scheduleamap" d="m 146.86174,202.08294 v -13.04313 h 19.67563 v 13.04313 z m 0,0" class="alert00During"></path>
   ...
   </svg>
   <div class="container-fluid">
      <div class="row">
         <div class="col">
            <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="mapSVG" version="1.1" viewBox="0 0 199.58284 125.05578" preserveAspectRatio="xMinYMin meet" xmlns:svgjs="http://svgjs.dev/svgjs" width="100%" class="mapSVGClass" style="cursor: move; user-select: none; touch-action: none; transform-origin: 50% 50%; transition: none 0s ease 0s; transform: scale(0.67032) translate(-107.635px, -394.821px);">
               ...
               <use id="use_path9146scheduleamap" href="#path9146scheduleamap" xlink:href="#path9146scheduleamap" d="m 146.86174,202.08294 v -13.04313 h 19.67563 v 13.04313 z m 0,0" transform="matrix(1,0,0,1,-8.574823231476401,-100.56069682128418)"></use>
               ...
            </svg>
         </div>
      </div>
   </div>
</form>

下面是更新svg path元素的React代码。

class Venues extends Component{
   ...
   map2Venues(venue){
      return (
         <path
           id={venue.mapIdentifier} 
           name={venue.mapIdentifier} 
           key={venue.mapIdentifier}
           d={venue.pathD}
           className={venue.alertClass}
         />
      );
   }
   ...
   render() {
      ...
      return (
         <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="0" height="0" viewBox="0 0 0 0" >
            {this.reducedVenues.map((venue) => this.map2Venues(venue))}
         </svg>
      );
      ...
   }
}

下面是svg元素和<use>元素的React:

class Map extends Component {
   ...
   render() {
      ...
      return (
        <Container fluid>
          <Row>
            <Col>
              <div
                id="svgMapDiv"
                ref={this.svgDivRef}
                dangerouslySetInnerHTML={{ __html: this.props.svgMap }}
              />
            </Col>
          </Row>
        </Container>
      );
      ...
   }
}

带有<use>元素的svg在组件创建时只加载一次,而带有<path>元素的svg中的class属性会频繁更新。我理解内联svg的危险。它是受控的。如果这是webkit的bug,那么React的解决方案将受到欢迎。

UPDATE我在MacOS的Safari中确认,当<path>元素和对应的属性在引用<use>元素创建之前静态应用并存在时,属性会正确传播到<use>元素。更多的证据是,当<path>元素动态更新时,<use>元素不会更新。

我发现这个bug description似乎没有解决。我尝试了Stuart P. Bentley于2013-03-09 14:34:36在那个bug描述中,但它仍然不起作用。也就是说,当属性被直接更改而不是通过类或样式属性(一个真实的的痛苦)时,它仍然不能在所有iOS浏览器和MacOS上的Safari上工作(但在所有其他浏览器和操作系统上工作)。例如,更改上面的Venues组件的React代码仍然不起作用:

class Venues extends Component{
   ...
   map2Venues(venue){
    let fill = "";
    let stroke = "";
    let stroke_width = "";
    let animation_name = "";
    let animation_duration = "";
    let animation_iteration_count = "";
    if(venue.alertClass.includes("alertSelected")){
      fill = "purple";
      stroke = "purple";
      stroke_width = "1";
    }else if(venue.alertClass.includes("alert00During")){
      fill = "green";
      stroke = "green";
      stroke_width = "1";
    }
    ...
    return (
      <path
        id={venue.mapIdentifier} 
        name={venue.mapIdentifier} 
        key={venue.mapIdentifier}
        d={venue.pathD}
        opacity="0.8"
        fill={fill}
        stroke={stroke}
        stroke-width={stroke_width}
      />
    );
  }
  ...
  render() {
     ...
     return (
        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="0" height="0" viewBox="0 0 0 0" >
           {this.reducedVenues.map((venue) => this.map2Venues(venue))}
        </svg>
      );
      ...
  }
}

因此,我仍然需要一个解决这个bug的方法,因为它似乎没有被解决。(这难道不是一个重要的功能吗,特别是因为所有其他浏览器和平台都支持它?)

1aaf6o9v

1aaf6o9v1#

解决方法:据我所知,iOS,Safari和webkit移动的浏览器仍然不支持使用典型的React组件更新来动态更新SVG图像中的<use>元素。它也不支持在SVG元素被添加到React DOM之后(即在使用dangerouslySetInnerHTML调用之后)直接修改SVG元素。但是,可以在调用dangerouslySetInnerHTML之前直接修改SVG**,方法是使用javascript(或jQuery)修改SVG文本。

一般步骤:
1.创建<defs>元素,
1.创建新样式的<path>并将其添加到此<defs>元素(<use>元素引用的<path>),
1.抓取SVG文本let newSvgMap = this.state.svgMap;
1.使用javascript或jQuery将<defs>元素添加到newSvgMap中。
1.通过使用this.setState({ svgMap: newSvgMap })调用更新状态,更新dangerouslySetInnerHTML,从而使用新的<path>样式更新组件,然后
1.通过创建新的<def>并替换this.state.svgMap文本中的前一个,对样式的任何更改重复此过程。
示例:

let defsElement = document.createElementNS('http://www.w3.org/2000/svg','defs'); 
venues.forEach(venue=>{
    let pathElement = document.createElementNS('http://www.w3.org/2000/svg','path');
    pathElement.setAttribute('id', venue.mapIdentifier);
    pathElement.setAttribute('name', venue.mapIdentifier);
    pathElement.setAttribute('key', venue.mapIdentifier);
    pathElement.setAttribute('d', venue.pathD);
    pathElement.setAttribute('opacity', venue.opacity);
    pathElement.setAttribute('fill', venue.fill);
    pathElement.setAttribute('stroke', venue.stroke);
    pathElement.setAttribute('stroke-width', venue.stroke-width);
    defsElement.appendChild(pathElement);
})
defsElement.setAttribute('id', 'scheduleamap-defs');
let defElementstring = defsElement.outerHTML;
let currentSvgDefString = this.state.svgMap.match(/<defs id="myUniqueId-defs">.*<\/defs>/);
if (currentSvgDefString !== null) {
    let newSvgMap = this.state.svgMap.replace(currentSvgDefString[0], defElementstring);
    this.setState({ svgMap: newSvgMap });//This updates the dangerouslySetInnerHTML in the component which updates all the styles
}

注意事项:
1.我会直接设置<path>的属性,而不是使用类。
1.我将使用已经添加的空<defs id="myUniqueId-defs"><\/defs>初始化SVG文件,以使react中的javascript更简单(否则您还必须添加初始<defs>,而不是简单的替换。
1.我已经测试过这适用于动画。我假设它适用于正常静态SVG元素中支持的任何功能。
这不是最有效的解决方案,但据我所知,这是唯一一个在iOS、Safari和webkit移动的浏览器中动态更新SVG元素样式的解决方案。

相关问题