mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-05 22:56:11 +00:00
# Summary Introducing the long-awaited **Filters** in `react-native-svg` 🎉 ### Motivation This PR is the beginning of bringing support of SVG Filters into `react-native-svg`. * **related issues**: This PR series will address the following issues: #150, #176, #635, #883, #994, #996, #1216 * **feature overview**: This PR is a boilerplate for Filters * introducing `Filter` component and `FeColorMatrix` as a start. * It also introduces a new subdirectory called `react-native-svg/filter-image` with a `FilterImage` component. # Usage ## Filter and Fe... Filters are compatible with the web familiar standard, so most things should be compatible out-of-the-box and changes will be limited to using a capital letter as it's component. ### Example ```tsx import React from 'react'; import { FeColorMatrix, Filter, Rect, Svg } from 'react-native-svg'; export default () => { return ( <Svg height="300" width="300"> <Filter id="myFilter"> <FeColorMatrix type="saturate" values="0.2" /> </Filter> <Rect x="0" y="0" width="300" height="300" fill="red" filter="url(#myFilter)" /> </Svg> ); }; ```  ## Filter Image `FilterImage` is a new component that is not strictly related to SVG. Its behavior should be the same as a regular `Image` component from React Native with one exception - the additional prop `filters`, which accepts an array of filters to apply to the image. ### Example ```tsx import React from 'react'; import { StyleSheet } from 'react-native'; import { FilterImage } from 'react-native-svg/filter-image'; const myImage = require('./myImage.jpg'); export default () => { return ( <FilterImage style={styles.image} source={myImage} filters={[ { name: 'colorMatrix', type: 'saturate', values: 0.2 }, { name: 'colorMatrix', type: 'matrix', values: [ 0.2, 0.2, 0.2, 0, 0, 0.2, 0.2, 0.2, 0, 0, 0.2, 0.2, 0.2, 0, 0, 0, 0, 0, 1, 0, ], }, ]} /> ); }; const styles = StyleSheet.create({ image: { width: 200, height: 200, }, }); ```  ## Test Plan **Example App**: Updated the example app with various filter effects, showcasing real-world usage. ## Compatibility | OS | Implemented | | ------- | :---------: | | iOS | ✅ | | Android | ✅ | ## Checklist - [x] I have tested this on a device and a simulator - [x] I added documentation in `README.md` and `USAGE.md` - [x] I updated the typed files (typescript)
198 lines
4.4 KiB
TypeScript
198 lines
4.4 KiB
TypeScript
/**
|
|
* Sample React Native App for react-native-svg library
|
|
* https://github.com/magicismight/react-native-svg/tree/master/Example
|
|
*/
|
|
'use strict';
|
|
|
|
import React, {Component} from 'react';
|
|
import {
|
|
Dimensions,
|
|
StyleSheet,
|
|
Text,
|
|
View,
|
|
ScrollView,
|
|
TouchableHighlight,
|
|
TouchableOpacity,
|
|
SafeAreaView,
|
|
} from 'react-native';
|
|
import {Modal, Platform} from 'react-native';
|
|
import {Svg, Circle, Line} from 'react-native-svg';
|
|
|
|
import * as examples from './src/examples';
|
|
import {commonStyles} from './src/commonStyles';
|
|
|
|
const names: (keyof typeof examples)[] = [
|
|
'Svg',
|
|
'Stroking',
|
|
'Path',
|
|
'Line',
|
|
'Rect',
|
|
'Polygon',
|
|
'Polyline',
|
|
'Circle',
|
|
'Ellipse',
|
|
'G',
|
|
'Text',
|
|
'Gradients',
|
|
'Clipping',
|
|
'Image',
|
|
'TouchEvents',
|
|
'PanResponder',
|
|
'Reusable',
|
|
'Reanimated',
|
|
'Transforms',
|
|
'Markers',
|
|
'Mask',
|
|
'Filters',
|
|
'FilterImage',
|
|
];
|
|
|
|
const initialState = {
|
|
modal: false,
|
|
content: null,
|
|
};
|
|
|
|
export default class SvgExample extends Component {
|
|
state: {
|
|
content: React.ReactNode;
|
|
modal: boolean;
|
|
scroll?: boolean;
|
|
} = initialState;
|
|
|
|
show = (name: keyof typeof examples) => {
|
|
if (this.state.modal) {
|
|
return;
|
|
}
|
|
let example = examples[name];
|
|
if (example) {
|
|
let samples = example.samples;
|
|
this.setState({
|
|
modal: true,
|
|
content: (
|
|
<View>
|
|
{samples.map((Sample, i) => (
|
|
<View style={commonStyles.example} key={`sample-${i}`}>
|
|
<Text style={commonStyles.sampleTitle}>{Sample.title}</Text>
|
|
<Sample />
|
|
</View>
|
|
))}
|
|
</View>
|
|
),
|
|
scroll: (example as {scroll?: boolean}).scroll !== false,
|
|
});
|
|
}
|
|
};
|
|
|
|
hide = () => {
|
|
this.setState(initialState);
|
|
};
|
|
|
|
getExamples = () => {
|
|
return names.map(name => {
|
|
var icon;
|
|
let example = examples[name];
|
|
if (example) {
|
|
icon = example.icon;
|
|
}
|
|
return (
|
|
<TouchableHighlight
|
|
style={styles.link}
|
|
underlayColor="#ccc"
|
|
key={`example-${name}`}
|
|
onPress={() => this.show(name)}>
|
|
<View style={commonStyles.cell}>
|
|
{icon}
|
|
<Text style={commonStyles.title}>{name}</Text>
|
|
</View>
|
|
</TouchableHighlight>
|
|
);
|
|
});
|
|
};
|
|
|
|
modalContent = () => (
|
|
<>
|
|
<SafeAreaView style={{flex: 1}}>
|
|
<ScrollView
|
|
style={styles.scroll}
|
|
contentContainerStyle={styles.scrollContent}
|
|
scrollEnabled={this.state.scroll}>
|
|
{this.state.content}
|
|
</ScrollView>
|
|
</SafeAreaView>
|
|
<SafeAreaView style={styles.close}>
|
|
<TouchableOpacity activeOpacity={0.7} onPress={this.hide}>
|
|
<Svg height="20" width="20">
|
|
<Circle cx="10" cy="10" r="10" fill="red" />
|
|
<Line x1="4" y1="4" x2="16" y2="16" stroke="#fff" strokeWidth="2" />
|
|
<Line x1="4" y1="16" x2="16" y2="4" stroke="#fff" strokeWidth="2" />
|
|
</Svg>
|
|
</TouchableOpacity>
|
|
</SafeAreaView>
|
|
</>
|
|
);
|
|
|
|
render() {
|
|
return (
|
|
<SafeAreaView style={styles.container}>
|
|
<Text style={commonStyles.welcome}>SVG library for React Apps</Text>
|
|
<View style={styles.contentContainer}>{this.getExamples()}</View>
|
|
{(Platform.OS === 'windows' || Platform.OS === 'macos') &&
|
|
this.state.modal ? (
|
|
<View style={styles.scroll}>{this.modalContent()}</View>
|
|
) : (
|
|
<Modal
|
|
transparent={false}
|
|
animationType="fade"
|
|
visible={this.state.modal}
|
|
onRequestClose={this.hide}>
|
|
{this.modalContent()}
|
|
</Modal>
|
|
)}
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
}
|
|
|
|
const hairline = StyleSheet.hairlineWidth;
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
paddingTop: 20,
|
|
alignItems: 'center',
|
|
overflow: 'hidden',
|
|
},
|
|
contentContainer: {
|
|
alignSelf: 'stretch',
|
|
borderTopWidth: hairline,
|
|
borderTopColor: '#ccc',
|
|
borderBottomWidth: hairline,
|
|
borderBottomColor: '#ccc',
|
|
flexWrap: 'wrap',
|
|
flexDirection: 'row',
|
|
marginHorizontal: 10,
|
|
},
|
|
link: {
|
|
height: 40,
|
|
alignSelf: 'stretch',
|
|
width: Dimensions.get('window').width / 2 - 10,
|
|
},
|
|
close: {
|
|
position: 'absolute',
|
|
right: 20,
|
|
top: 20,
|
|
},
|
|
scroll: {
|
|
position: 'absolute',
|
|
top: 30,
|
|
right: 10,
|
|
bottom: 20,
|
|
left: 10,
|
|
backgroundColor: '#fff',
|
|
},
|
|
scrollContent: {
|
|
borderTopWidth: hairline,
|
|
borderTopColor: '#ccc',
|
|
},
|
|
});
|