0

I'm goal is to create an ellipse with centered text using react-native-svg. I came across the ability to mask in their docs. The one thing I'm trying to figure out is creating a, cutout effect, similar to how it's done in CSS:

enter image description here

The docs show how to do things like strokes and gradients but I just want to mask the portion of the ellipse based on the size and shape of the text. Does anyone know if there's a way to accomplish this?

<Svg width="100%" height="100%" viewBox="0 0 800 300">
  <Defs>
    <LinearGradient
      id="Gradient"
      gradientUnits="userSpaceOnUse"
      x1="0"
      y1="0"
      x2="800"
      y2="0"
    >
      <Stop offset="0" stopColor="white" stopOpacity="0" />
      <Stop offset="1" stopColor="white" stopOpacity="1" />
    </LinearGradient>
    <Mask
      id="Mask"
      maskUnits="userSpaceOnUse"
      x="0"
      y="0"
      width="800"
      height="300"
    >
      <Rect x="0" y="0" width="800" height="300" fill="url(#Gradient)" />
    </Mask>
    <Text
      id="Text"
      x="400"
      y="200"
      fontFamily="Verdana"
      fontSize="100"
      textAnchor="middle"
    >
      Masked text
    </Text>
  </Defs>
  <Rect x="0" y="0" width="800" height="300" fill="#FF8080" />
  <Use href="#Text" fill="blue" mask="url(#Mask)" />
  <Use href="#Text" fill="none" stroke="black" stroke-width="2" />
</Svg>
Carl Edwards
  • 13,826
  • 11
  • 57
  • 119

1 Answers1

0

Here are several issues with the code and how to solve them:

  • The text element needs to be inside the mask.
  • If you also want to use the gradient for the mask, you need to correct stopColor and stopOpacity with stop-color and stop-opacity.
  • If you want the text to form a cutout in the rect, you need to apply the mask to the rect.

Here is a working example including the gradient:

<svg width="100%" height="100%" viewBox="0 0 800 300">
  <defs>
    <linearGradient
      id="Gradient"
      gradientUnits="userSpaceOnUse"
      x1="0"
      y1="0"
      x2="800"
      y2="0"
    >
      <stop offset="0" stop-color="white" stop-opacity="0" />
      <stop offset="1" stop-color="white" stop-opacity="1" />
    </linearGradient>
    <mask id="Mask">
      <rect x="0" y="0" width="700" height="300" fill="url(#Gradient)" />
      <text
      id="Text"
      x="400"
      y="200"
      fontFamily="Verdana"
      fontSize="100"
      textAnchor="middle"
    >
      Masked text
    </text>
      
    </mask>
  </defs>
  
  <rect x="0" y="0" width="900" height="300" fill="#FF8080" mask="url(#Mask)"/>
  
</svg>

EDIT after comment:

For a version without gradient, just fill the mask rect with white:

<svg width="100%" height="100%" viewBox="0 0 800 300">
  <defs>
    <mask id="Mask">
      <rect x="0" y="0" width="700" height="300" fill="white" />
      <text
      id="Text"
      x="400"
      y="200"
      fontFamily="Verdana"
      fontSize="100"
      textAnchor="middle"
    >
      Masked text
    </text>
      
    </mask>
  </defs>
  
  <rect x="0" y="0" width="900" height="300" fill="#FF8080" mask="url(#Mask)"/>
  
</svg>
vqf
  • 2,600
  • 10
  • 16
  • Thanks. Just out of curiosity, how would this cutout effect be accomplished if you were to have just a solid rect instead of a gradient? Replacing the gradient with just a fill seems to hide the text for me. – Carl Edwards Jan 15 '21 at 15:34
  • Just fill it with `white`. I edited the answer with an example. – vqf Jan 15 '21 at 17:13
  • Perfect. Thank you! One thing to note for future viewers is that this is written in html as opposed to react components. I like your answers though given that you're able to present code snippets. Easily translatable. – Carl Edwards Jan 15 '21 at 17:52