react-native-svg icon indicating copy to clipboard operation
react-native-svg copied to clipboard

LinearGradient rendering as Black/Transparent

Open RBrNx opened this issue 4 years ago • 12 comments

Bug

When rendering some SVGs that use Linear Gradient, the Gradient renders as black on Android, or sometimes transparent on iOS.

Screenshot_20200329-191846_Expo

Environment info

React native info output:

System:
    OS: Linux 4.4 Ubuntu 18.04.1 LTS (Bionic Beaver)
    CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
    Memory: 3.53 GB / 15.86 GB
    Shell: 5.4.2 - /usr/bin/zsh
  Binaries:
    Node: 12.9.1 - ~/.nvm/versions/node/v12.9.1/bin/node
    Yarn: 1.22.4 - ~/.yarn/bin/yarn
    npm: 6.14.4 - ~/.nvm/versions/node/v12.9.1/bin/npm
  npmPackages:
    react: ~16.9.0 => 16.9.0
    react-native: https://github.com/expo/react-native/archive/sdk-36.0.0.tar.gz => 0.61.4

Library version: 9.13.3 that is bundled with Expo SDK 36

Describe what you expected to happen:

  1. The SVG should render with Linear Gradients on Android and iOS.

Short, Self Contained, Correct (Compilable), Example

I've put together this Expo Snack that reproduces the issue. The original SVG file is also contained in the assets folder.

https://snack.expo.io/BJer8PCUI

I have also read through issues #1153 and #540. In the first example I have actually been able to use the SVG provided successfully, it does not display the same behavior as my SVG. I have also tried changing the stopColor prop to a style prop, but this has no effect.

RBrNx avatar Mar 29 '20 18:03 RBrNx

So I managed to do some digging and I've discovered the problem. LinearGradient (and perhaps other elements) don't support the xLinkHref prop, or if they do support it, it does not work correctly.

The vast majority of my example SVG uses the following syntax (generated by SVGR)

<LinearGradient
    id='prefix__b'
    x1={353.71}
    y1={360.14}
    x2={360.02}
    y2={360.14}
    gradientTransform='matrix(-1 0 0 1 528.02 0)'
    gradientUnits='userSpaceOnUs'
>
    <Stop offset={0} stopColor='#e4c5d5' />
    <Stop offset={0.42} stopColor='#e9d3cf' />
    <Stop offset={1} stopColor='#efe8ca' />
</LinearGradient>
<LinearGradient id='prefix__g' x1={287.43} y1={352.21} x2={294.44} y2={352.21} xlinkHref='#prefix__b' />

where LinearGradient prefix__g wants to inherit the properties from LinearGradient prefix__b and then overriding the X and Y coordinates.

I discovered that replacing this inheritance with the values from prefix__b leads to the correct rendering of the SVG like so:

<LinearGradient
    id='prefix__b'
    x1={353.71}
    y1={360.14}
    x2={360.02}
    y2={360.14}
    gradientTransform='matrix(-1 0 0 1 528.02 0)'
    gradientUnits='userSpaceOnUse'
>
    <Stop offset={0} stopColor='#e4c5d5' />
    <Stop offset={0.42} stopColor='#e9d3cf' />
    <Stop offset={1} stopColor='#efe8ca' />
</LinearGradient>
<LinearGradient
    id='prefix__g'
    x1={287.43}
    y1={352.21}
    x2={294.44}
    y2={352.21}
    gradientUnits='userSpaceOnUse'
    gradientTransform='matrix(-1 0 0 1 528.02 0)'
>
    <Stop offset={0} stopColor='#e4c5d5' />
    <Stop offset={0.42} stopColor='#e9d3cf' />
    <Stop offset={1} stopColor='#efe8ca' />
</LinearGradient>

Here is a snack that shows the old broken SVG, and the new working SVG. https://snack.expo.io/yTM9VSiR8

Is this something that react-native-svg intends to support?

RBrNx avatar Apr 05 '20 14:04 RBrNx

Ah, as long as it's defined by the spec, and some ever-green browser supports it, then ideally react-native-svg would support it as well. There's a workaround here: https://github.com/react-native-community/react-native-svg/pull/920

But should probably be implemented on the native side, and closer to the wording of the spec. Would you mind looking into it?

msand avatar Apr 05 '20 14:04 msand

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. You may also mark this issue as a "discussion" and I will leave this open.

stale[bot] avatar Jun 04 '20 23:06 stale[bot]

the problem still exists at 13.6.0

zhiqingchen avatar Nov 23 '22 09:11 zhiqingchen

i I use react-native-svg to render the chart of echarts, the gradient is rendered black or transparent, especially when there is animation.

https://echarts.apache.org/examples/zh/editor.html?c=dataset-encode0

Simplified source code

<svg xmlns="http://www.w3.org/2000/svg" baseProfile="full" width="522" height="782" style="position:absolute;left:0;top:0;user-select:none">
  <g clip-path="url(#a)">
    <path d="M0 0h20v140H0Z" transform="rotate(-90 483.418 293.582)" fill="url(#b)"/>
    <path d="M0 0h20v140H0Z" transform="rotate(-90 483.418 293.582)" fill="url(#c)"/>
  </g>
  <defs>
    <linearGradient gradientUnits="objectBoundingBox" x1="0" y1="0" x2="0" y2="1" id="b">
      <stop offset="0%" stop-color="#AAA"/>
      <stop offset="50%" stop-color="#AAA"/>
      <stop offset="100%" stop-color="#AAA"/>
    </linearGradient>
    <linearGradient gradientUnits="objectBoundingBox" x1="0" y1="0" x2="0" y2="1" id="c">
      <stop offset="0%" stop-color="#65B581"/>
      <stop offset="50%" stop-color="#FFCE34"/>
      <stop offset="100%" stop-color="#FD665F"/>
    </linearGradient>
    <clipPath id="a">
      <path d="M189.836 774v-14a3 3 0 0 1 3-3h134a3 3 0 0 1 3 3v14a3 3 0 0 1-3 3h-134a3 3 0 0 1-3-3"/>
    </clipPath>
  </defs>
</svg>

render by webview & react-native-svg & react-native-skia (With transition animation)

  1. webview ✅
  2. react-native-svg Transparent/black
  3. skia ✅
expected Transparent black
a image image

zhiqingchen avatar Nov 23 '22 10:11 zhiqingchen

Does this problem only occur in certain scenarios? Is there a way to avoid it?

zhiqingchen avatar Nov 23 '22 10:11 zhiqingchen

Does this problem only occur in certain scenarios? Is there a way to avoid it? @zhiqingchen

您好,我也在考虑RN集成echarts,目前遇到两个问题请教一下: 1 用react-native-skia集成的话,原理和集成微信小程序差不多,CanvasRenderingContext2D您自己实现的吗,可否共享一下?不胜感激 1 如何用这个svg库集成,您是怎么做的呢 十分感谢

ljianc avatar Nov 25 '22 08:11 ljianc

@ljianc Our project is still under development, not officially open source.

zhiqingchen avatar Nov 25 '22 09:11 zhiqingchen

@ljianc try https://github.com/wuba/wrn-echarts

zhiqingchen avatar Jan 09 '23 08:01 zhiqingchen

@ljianc try https://github.com/wuba/wrn-echarts

OK,TKS, Thank you for still remembering this question

ljianc avatar Jan 09 '23 09:01 ljianc

I still have the same problem with version 14.1.0

pixelsomatic avatar Apr 13 '24 05:04 pixelsomatic

So I managed to do some digging and I've discovered the problem. LinearGradient (and perhaps other elements) don't support the xLinkHref prop, or if they do support it, it does not work correctly.

The vast majority of my example SVG uses the following syntax (generated by SVGR)

<LinearGradient
    id='prefix__b'
    x1={353.71}
    y1={360.14}
    x2={360.02}
    y2={360.14}
    gradientTransform='matrix(-1 0 0 1 528.02 0)'
    gradientUnits='userSpaceOnUs'
>
    <Stop offset={0} stopColor='#e4c5d5' />
    <Stop offset={0.42} stopColor='#e9d3cf' />
    <Stop offset={1} stopColor='#efe8ca' />
</LinearGradient>
<LinearGradient id='prefix__g' x1={287.43} y1={352.21} x2={294.44} y2={352.21} xlinkHref='#prefix__b' />

where LinearGradient prefix__g wants to inherit the properties from LinearGradient prefix__b and then overriding the X and Y coordinates.

I discovered that replacing this inheritance with the values from prefix__b leads to the correct rendering of the SVG like so:

<LinearGradient
    id='prefix__b'
    x1={353.71}
    y1={360.14}
    x2={360.02}
    y2={360.14}
    gradientTransform='matrix(-1 0 0 1 528.02 0)'
    gradientUnits='userSpaceOnUse'
>
    <Stop offset={0} stopColor='#e4c5d5' />
    <Stop offset={0.42} stopColor='#e9d3cf' />
    <Stop offset={1} stopColor='#efe8ca' />
</LinearGradient>
<LinearGradient
    id='prefix__g'
    x1={287.43}
    y1={352.21}
    x2={294.44}
    y2={352.21}
    gradientUnits='userSpaceOnUse'
    gradientTransform='matrix(-1 0 0 1 528.02 0)'
>
    <Stop offset={0} stopColor='#e4c5d5' />
    <Stop offset={0.42} stopColor='#e9d3cf' />
    <Stop offset={1} stopColor='#efe8ca' />
</LinearGradient>

Here is a snack that shows the old broken SVG, and the new working SVG. snack.expo.io/yTM9VSiR8

Is this something that react-native-svg intends to support?

I faced the same problem and this did fix it! Thank you for the suggestion!

bernardobelchior avatar Jul 28 '24 10:07 bernardobelchior