web-dev-qa-db-fra.com

React Composant SVG comme image de fond à div

J'essaie d'obtenir des composants de style pour prendre un SVG qui est un composant React et le définir comme image d'arrière-plan, mais j'obtiens l'erreur:

TypeError: impossible de convertir une valeur de symbole en chaîne

Code composant SVG:

import React from "react";

const testSVG = props => {
  return (
    <svg version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 193.3 129.7">
      <g>
        <g>
          <g>
            <path
              fill="#434343"
              class="st0"
              d="M162.4,116c-4.6,7.5-15.6,13.6-24.4,13.6H16c-8.8,0-12.3-6.2-7.7-13.7c0,0,4.6-7.7,11.1-18.4
                c4.4-7.4,8.9-14.7,13.3-22.1c1.1-1.8,6.1-7.7,6.1-10.1c0-7.3-15-24.9-18.5-30.7C13.5,23.6,8.3,14.9,8.3,14.9
                C3.7,7.4,7.2,1.2,16,1.2c0,0,65.9,0,106.8,0c10,0,25.9-4.5,33.5,3.8c8.7,9.3,15.5,25.4,22.5,36.8c2.6,4.2,14.5,18.3,14.5,23.8
                c-0.1,7.6-16,26.1-19.6,32C167.1,108.3,162.4,116,162.4,116z"
            />
          </g>
        </g>
      </g>
    </svg>
  );
};

export default testSVG;

Code composant du conteneur:

import React, { Component } from "react";
import StepSVG from "../../components/UI/SVGs/Test";

class StepBar extends Component {
  render() {
    const { steps } = this.props;

    return (
      <div>
        {steps
          ? steps.map(step => {
              return (
                <Wrap key={step._id} bg={StepSVG}>
                  <P>
                    {" "}
                    <Link to={"/step/" + step._id}>{step.title} </Link>
                  </P>
                </Wrap>
              );
            })
          : null}
      </div>
    );
  }
}

export default StepBar;

const Wrap = styled.div`
  padding: 30px 30px 0 0;
  display: inline-block;
  background-image: url(${props => props.bg});
`;

J'utilise create-react-app hors de la boîte.

J'ai également essayé sans utiliser de composants stylisés et utilisé des styles en ligne sans succès.

Est-il possible d'utiliser un SVG comme image de fond dans ce contexte avec le SVG comme composant?

7
user8467470

Malheureusement, ce que vous essayez d'atteindre n'est pas possible. Vous devrez conserver votre SVG en tant que fichier .svg réel (et lier à celui-ci), ou faire quelques astuces CSS pour placer le composant SVG derrière votre composant de premier plan.

En d'autres termes:

<!-- step.svg -->
<svg version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 193.3 129.7">
  <!-- copy paste your svg here -->
</svg>

Et dans votre composant:

import stepSvgUrl from "../../components/UI/SVGs/step.
// ...
<Wrap key={step._id} bg={stepSvgUrl}>

Lorsque vous importez un fichier SVG comme celui-ci, create-react-app applique un chargeur Webpack qui inclut l'URL générée au SVG que vous importez - ce qui est bien pour passer dans le background-image propriété css.

J'espère que cela t'aides!

3
Kris Selbekk

En fait, il existe un moyen de réaliser ce que vous voulez, sans que le SVG soit un composant:

Voici le composant StepBar mis à jour:

import React, { Component } from "react";
import StepSVG from "../../components/UI/SVGs/test.svg";
import encodeSVG from '../../lib/encodeSVG';

class StepBar extends Component {
  render() {
    const { steps } = this.props;

    return (
      <div>
        {steps
          ? steps.map(step => {
              return (
                <Wrap key={step._id} bg={StepSVG}>
                  <P>
                    {" "}
                    <Link to={"/step/" + step._id}>{step.title} </Link>
                  </P>
                </Wrap>
              );
            })
          : null}
      </div>
    );
  }
}

export default StepBar;

const Wrap = styled.div`
  padding: 30px 30px 0 0;
  display: inline-block;
  background-image: ${encodeSVG(bg, '#FF9900')};
`;

Voici le fichier encodeSVG lib:

var symbols = /[\r\n"%#()<>?\[\\\]^`{|}]/g;

function addNameSpace(data) {
  if (data.indexOf('http://www.w3.org/2000/svg') < 0) {
    data = data.replace(/<svg/g, "<svg xmlns='http://www.w3.org/2000/svg'");
  }

  return data;
}

function encodeSVG(data) {
  // Use single quotes instead of double to avoid encoding.
  if (data.indexOf('"') >= 0) {
    data = data.replace(/"/g, "'");
  }

  data = data.replace(/>\s{1,}</g, '><');
  data = data.replace(/\s{2,}/g, ' ');

  return data.replace(symbols, encodeURIComponent);
}

var encode = function(svg, fill) {
  if (fill) {
    svg = svg.replace(/<svg/g, `<svg fill="${fill}"`);
  }
  var namespaced = addNameSpace(svg);
  var dimensionsRemoved = namespaced
    .replace(/height="\w*" /g, '')
    .replace(/width="\w*" /g, '')
    .replace(/height='\w*' /g, '')
    .replace(/width='\w*' /g, '');
  var encoded = encodeSVG(dimensionsRemoved);

  var header = 'data:image/svg+xml,';
  var dataUrl = header + encoded;  

  return `url("${dataUrl}")`;
};

Vous importez le svg et utilisez un chargeur de chaîne pour les svgs avec votre système de construction préféré (par exemple WebPack), passez ce svg à encodeSVG, et le tour est joué! :)

2
phun-ky
url(`data:image/svg+xml;utf8,${svgTxt}`)

;ou

url(`data:image/svg+xml;base64,${btoa(svgTxt)}`)

c'est tout.

1
user11011301

J'ai fini par utiliser la solution de user11011301 en combinaison avec raw-loader pour charger le contenu SVG. Ce n'est pas la solution la plus propre, j'en suis sûr, mais je n'ai pas pu la faire fonctionner avec d'autres chargeurs de webpack.

import searchIcon from '../../images/search.svg';

<input style={{backgroundImage: `url(data:image/svg+xml;base64,${btoa(searchIcon)})`}}/>
1
paul.ago

Vous devriez probablement le convertir en chaîne (comme l'indique l'erreur). De toute évidence, vous ne pouvez pas utiliser un composant React comme image d'arrière-plan, car le composant react est en fait un objet javascript (JSX se convertit en JS, puis en HTML). Donc, ce que vous pouvez faire/essayer est d'utiliser, ReactDOMServer ( https://reactjs.org/docs/react-dom-server.html ).

Vous pouvez essayer quelque chose avec:

ReactDOMServer.renderToString(StepSVG)

ou

ReactDOMServer.renderToStaticMarkup(StepSVG)

Je n'ai pas testé si cela fonctionne mais ça vaut le coup pour vous je suppose :)

0
Arber Sylejmani