feat: rewrite Svg transform (#2403)

# Summary

Currently, when applying transforms depending on the type (RN style vs
SVG style) transforms behave differently giving wrong results.

Example component
```tsx
<Svg
  height="200"
  viewBox="0 0 200 200"
  width="200"
  // transform={[{scale: 2}]}
  // transform="scale(2)"
  // transform={[{rotate: '45deg'}]}
  // transform="rotate(45)"
  // transform={[{translateX: 100}, {translateY: 100}]}
  // transform="translate(100 100)"
>
  <Mask id="myMask">
    <Rect fill="white" height="100" width="100" x="0" y="0" />
    <Path d="M10,35 A20,20,0,0,1,50,35 A20,20,0,0,1,90,35 Q90,65,50,95 Q10,65,10,35 Z" />
  </Mask>
  <Rect fill="pink" height="200" width="300" x="0" y="0" />
  <Circle cx="50" cy="50" fill="purple" mask="url(#myMask)" r="50" />
  <Rect fill="green" x="50" y="100" width="100" height="100" />
</Svg>
```
| Attribute | Before | After |
| --- | --- | --- |
| `transform={[{scale: 2}]}` | <img width="250" alt="image"
src="https://github.com/user-attachments/assets/c04d7e11-e039-4d1a-b804-e993f3877b6a">
| <img width="250" alt="image"
src="https://github.com/user-attachments/assets/bb717ae4-7c8f-410a-942d-1bd6feab273c">
|
| `transform="scale(2)"` | <img width="250" alt="image"
src="https://github.com/user-attachments/assets/85717613-ede0-44a8-8524-c9af4b37c09d">
| <img width="250" alt="image"
src="https://github.com/user-attachments/assets/f4e23bc6-8cfb-4509-a2f5-45c4f642c197">
|
| `transform={[{rotate: '45deg'}]}` | <img width="250" alt="image"
src="https://github.com/user-attachments/assets/90131401-2c52-4e8a-81ab-6cd449625953">
| <img width="250" alt="image"
src="https://github.com/user-attachments/assets/bab46300-4794-4322-bd95-d6e7e7abd30e">
|
| `transform="rotate(45)"` | <img width="250" alt="image"
src="https://github.com/user-attachments/assets/6d308022-4844-451a-b767-1c3e94e7a295">
| <img width="250" alt="image"
src="https://github.com/user-attachments/assets/553bbad5-9e37-4a52-b4e0-fa0c7b6b558e">
|
| `transform={[{translateX: 100}, {translateY: 100}]}` | <img
width="250" alt="image"
src="https://github.com/user-attachments/assets/91508d75-2b0a-4be6-9280-2ace017d9271">
| <img width="250" alt="image"
src="https://github.com/user-attachments/assets/36fb5cad-1ccf-4c99-8ffd-70ea56ba589f">
|
| `transform="translate(100 100)"` | <img width="250" alt="image"
src="https://github.com/user-attachments/assets/28fa66f2-b2f2-4b86-bb41-47bd507d6018">
| <img width="250" alt="image"
src="https://github.com/user-attachments/assets/36fb5cad-1ccf-4c99-8ffd-70ea56ba589f">
|

## Test Plan

Test example app -> `Test2403`
This commit is contained in:
Jakub Grzywacz
2024-08-19 09:16:18 +02:00
committed by GitHub
parent ca1c35caa9
commit d11d892496
10 changed files with 1571 additions and 21 deletions

View File

@@ -26,6 +26,7 @@ import Test2327 from './src/Test2327';
import Test2233 from './src/Test2233';
import Test2366 from './src/Test2366';
import Test2397 from './src/Test2397';
import Test2403 from './src/Test2403';
import Test2407 from './src/Test2407';
export default function App() {

View File

@@ -0,0 +1,95 @@
import {useCallback} from 'react';
import {
Animated,
Button,
Easing,
Text,
View,
useAnimatedValue,
} from 'react-native';
import {Circle, Mask, Path, Rect, Svg} from 'react-native-svg';
export const EASING_IN: (t: number) => number = Easing.bezier(0.7, 0, 0.3, 1);
export const EASING_OUT: (t: number) => number = Easing.bezier(0.5, 0, 0.5, 1);
const AnimatedSvg = Animated.createAnimatedComponent(Svg);
export default function Playground() {
const animatedValue = useAnimatedValue(0);
const handleMouseEnter = useCallback(() => {
Animated.timing(animatedValue, {
duration: 350,
easing: EASING_IN,
toValue: 1,
useNativeDriver: false,
}).start();
}, [animatedValue]);
const handleMouseLeave = useCallback(() => {
Animated.timing(animatedValue, {
duration: 350,
easing: EASING_OUT,
toValue: 0,
useNativeDriver: false,
}).start();
}, [animatedValue]);
return (
<View
style={{
paddingTop: 100,
marginHorizontal: 16,
flex: 1,
}}>
<Text style={{zIndex: 100}}>Text1</Text>
<AnimatedSvg
height="200"
viewBox="0 0 200 200"
width="200"
// use transform or style.transform
// transform={[
// {
// scale: animatedValue.interpolate({
// inputRange: [0, 1],
// outputRange: [1, 1.2],
// }),
// },
// ]}
style={{
transform: [
{
scale: animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.2],
}),
},
],
}}
//
>
<Mask id="myMask">
<Rect fill="white" height="100" width="100" x="0" y="0" />
<Path
d="M10,35 A20,20,0,0,1,50,35 A20,20,0,0,1,90,35 Q90,65,50,95 Q10,65,10,35 Z"
fill="black"
/>
</Mask>
<Rect fill="pink" height="200" width="300" x="0" y="0" />
<Circle
cx="50"
cy="50"
fill="purple"
mask="url(#myMask)"
r="50"
onPress={() => console.log('sadas')}
/>
<Rect fill="green" x="50" y="100" width="100" height="100" />
</AnimatedSvg>
<Text>Text2</Text>
<Button onPress={handleMouseEnter} title="Mouse Enter" />
<Button onPress={handleMouseLeave} title="Mouse Leave" />
</View>
);
}