Using Group opacity with Mask child also applies the opacity on other outside nodes
Description
When using a <Mask> inside a <Group> and then also applying opacity to the group it somehow applies that same opacity even to nodes not part of the group that comes after the group. This happens only if I place a Mask inside the group.
My use case is a bit more advanced than the repro. I have some charts that I apply some gradient opacity effects through a mask and then control the chart visibility through the group opacity. This has previously worked, but seems to have stopped working in recent versions. Not sure which one.
I can think of some workarounds, but I think the use case is strong enough to have it behave like expected, not applying the opacity to other nodes than the group.
https://github.com/user-attachments/assets/85e048b7-9211-4760-9143-7ad0d7d6da58
React Native Skia Version
2.2.11
React Native Version
0.79.6
Using New Architecture
- [x] Enabled
Steps to Reproduce
The following is a simple repro. I've also attached a git repo that can be cloned to test this.
import { Canvas, Group, Mask, Rect } from "@shopify/react-native-skia"
import { Button, StyleSheet, View } from "react-native"
import { useSharedValue } from "react-native-reanimated"
export default function App() {
const opacity1 = useSharedValue(1)
const opacity2 = useSharedValue(1)
return (
<View style={styles.container}>
<Canvas style={{ height: 250, width: 250, borderWidth: 1, borderColor: "red", marginBottom: 40 }}>
<Group opacity={opacity1}>
<Mask mask={<Rect x={0} y={0} width={100} height={100} color="white" />} mode="luminance">
<Rect x={0} y={0} width={100} height={100} color="red" />
</Mask>
</Group>
<Rect x={100} y={0} width={100} height={100} color="orange" />
<Group opacity={opacity2}>
<Rect x={0} y={100} width={100} height={100} color="blue" />
</Group>
</Canvas>
<View style={{ flexDirection: "row", gap: 10 }}>
<Button title="Square 1 Opacity = 1" onPress={() => opacity1.set(1)} />
<Button title="Square 1 Opacity = 0" onPress={() => opacity1.set(0)} />
</View>
<View style={{ flexDirection: "row", gap: 10 }}>
<Button title="Square 2 Opacity = 1" onPress={() => opacity2.set(1)} />
<Button title="Square 2 Opacity = 0" onPress={() => opacity2.set(0)} />
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
})
Snack, Code Example, Screenshot, or Link to Repository
https://github.com/albinhubsch/skia-group-opacity
@albinhubsch good to see you here 👋 While I haven't tried it yet, I could see how this bug could happen, the issue is that the opacity of the group is used in whatever is inmask=? It sounds fair to add tests for it and fix it.
I was just looking at another <Mask /> bug report (3254), maybe a good way to work on these bugs is to build the expected result on figma (we match the semantic there, or at least try to) and we use it in our test suite.
👋 @wcandillon ,,great to hear! I do like the sound of trying to get it into figma. Our designers figma files are usually a great source of inspiration for me on how to layer and implement stuff in rnskia, so I guess matching the semantics and catch it early in tests sounds great!
Let me know if I can assist in any way.
haha sry about that close, reopen 👆 cmd+enter 😅
I'm facing the same bug. It happens even when no mask is used, for example, when wrapping <Text /> inside <Group opacity={0.85}>.
I am also seeing this same problem where opacity is being applied. The opacity is applying to all siblings and even subsequent draws from higher in the react tree. This is just trying to wrap a <RoundedRect><Paint /></RoundedRect> in a single Groupcomponent. If I don't render thisRoundedRect` component, then the fades of all other draws is not impacted.