mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-06 07:06:11 +00:00
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:
@@ -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() {
|
||||
|
||||
95
apps/test-examples/src/Test2403.tsx
Normal file
95
apps/test-examples/src/Test2403.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user