software-mansion / react-native-svg

SVG library for React Native, React Native Web, and plain React web projects.
MIT License
7.42k stars 1.12k forks source link

LinearGradient rendering as Black/Transparent #1326

Open RBrNx opened 4 years ago

RBrNx commented 4 years ago

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 commented 4 years ago

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?

msand commented 4 years ago

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?

stale[bot] commented 4 years ago

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.

zhiqingchen commented 1 year ago

the problem still exists at 13.6.0

zhiqingchen commented 1 year ago

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 commented 1 year ago

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

ljianc commented 1 year ago

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库集成,您是怎么做的呢 十分感谢

zhiqingchen commented 1 year ago

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

zhiqingchen commented 1 year ago

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

ljianc commented 1 year ago

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

OK,TKS, Thank you for still remembering this question

pixelsomatic commented 5 months ago

I still have the same problem with version 14.1.0

bernardobelchior commented 1 month ago

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!