@wwimmo/react-native-sketch-canvas
Version:
react-native-sketch-canvas allows you to draw / sketch on both iOS and Android devices and sync the drawing data between users. Of course you can save as image.
832 lines (817 loc) • 29 kB
JavaScript
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow strict-local
*/
import React, {Component} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Alert,
TouchableOpacity,
ScrollView,
} from 'react-native';
import {RNCamera} from 'react-native-camera';
import RNSketchCanvas from '@wwimmo/react-native-sketch-canvas';
import {SketchCanvas} from '@wwimmo/react-native-sketch-canvas';
export default class example extends Component {
constructor(props) {
super(props);
this.state = {
example: 0,
color: '#FF0000',
thickness: 5,
message: '',
photoPath: null,
scrollEnabled: true,
};
}
takePicture = async function () {
if (this.camera) {
const options = {quality: 0.5, base64: true};
const data = await this.camera.takePictureAsync(options);
this.setState({
photoPath: data.uri.replace('file://', ''),
});
}
};
render() {
return (
<View style={styles.container}>
{this.state.example === 0 && (
<View
style={{
justifyContent: 'center',
alignItems: 'center',
width: 340,
}}>
<TouchableOpacity
onPress={() => {
this.setState({example: 1});
}}>
<Text style={{alignSelf: 'center', marginTop: 15, fontSize: 18}}>
- Example 1 -
</Text>
<Text>Use build-in UI components</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.setState({example: 2});
}}>
<Text style={{alignSelf: 'center', marginTop: 15, fontSize: 18}}>
- Example 2 -
</Text>
<Text>Use canvas only and customize UI components</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.setState({example: 3});
}}>
<Text style={{alignSelf: 'center', marginTop: 15, fontSize: 18}}>
- Example 3 -
</Text>
<Text>Sync two canvases</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.setState({example: 4});
}}>
<Text style={{alignSelf: 'center', marginTop: 15, fontSize: 18}}>
- Example 4 -
</Text>
<Text>Take a photo first</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.setState({example: 5});
}}>
<Text style={{alignSelf: 'center', marginTop: 15, fontSize: 18}}>
- Example 5 -
</Text>
<Text>Load local image</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.setState({example: 7});
}}>
<Text style={{alignSelf: 'center', marginTop: 15, fontSize: 18}}>
- Example 7 -
</Text>
<Text>Multiple canvases in ScrollView</Text>
</TouchableOpacity>
</View>
)}
{this.state.example === 1 && (
<View style={{flex: 1, flexDirection: 'row'}}>
<RNSketchCanvas
containerStyle={{backgroundColor: 'transparent', flex: 1}}
canvasStyle={{backgroundColor: 'transparent', flex: 1}}
onStrokeEnd={(data) => {}}
closeComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Close</Text>
</View>
}
onClosePressed={() => {
this.setState({example: 0});
}}
undoComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Undo</Text>
</View>
}
onUndoPressed={(id) => {
// Alert.alert('do something')
}}
clearComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Clear</Text>
</View>
}
onClearPressed={() => {
// Alert.alert('do something')
}}
eraseComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Eraser</Text>
</View>
}
strokeComponent={(color) => (
<View
style={[{backgroundColor: color}, styles.strokeColorButton]}
/>
)}
strokeSelectedComponent={(color, index, changed) => {
return (
<View
style={[
{backgroundColor: color, borderWidth: 2},
styles.strokeColorButton,
]}
/>
);
}}
strokeWidthComponent={(w) => {
return (
<View style={styles.strokeWidthButton}>
<View
style={{
backgroundColor: 'white',
marginHorizontal: 2.5,
width: Math.sqrt(w / 3) * 10,
height: Math.sqrt(w / 3) * 10,
borderRadius: (Math.sqrt(w / 3) * 10) / 2,
}}
/>
</View>
);
}}
defaultStrokeIndex={0}
defaultStrokeWidth={5}
saveComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Save</Text>
</View>
}
savePreference={() => {
return {
folder: 'RNSketchCanvas',
filename: String(Math.ceil(Math.random() * 100000000)),
transparent: false,
imageType: 'png',
};
}}
onSketchSaved={(success, path) => {
Alert.alert(
success ? 'Image saved!' : 'Failed to save image!',
path,
);
}}
onPathsChange={(pathsCount) => {
console.log('pathsCount', pathsCount);
}}
/>
</View>
)}
{this.state.example === 2 && (
<View style={{flex: 1, flexDirection: 'row'}}>
<View style={{flex: 1, flexDirection: 'column'}}>
<View
style={{flexDirection: 'row', justifyContent: 'space-between'}}>
<TouchableOpacity
style={styles.functionButton}
onPress={() => {
this.setState({example: 0});
}}>
<Text style={{color: 'white'}}>Close</Text>
</TouchableOpacity>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
style={styles.functionButton}
onPress={() => {
this.setState({thickness: 10});
}}>
<Text style={{color: 'white'}}>Thick</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.functionButton}
onPress={() => {
this.setState({thickness: 5});
}}>
<Text style={{color: 'white'}}>Thin</Text>
</TouchableOpacity>
</View>
</View>
<SketchCanvas
localSourceImage={{
filename: 'whale.png',
directory: SketchCanvas.MAIN_BUNDLE,
mode: 'AspectFit',
}}
// localSourceImage={{ filename: 'bulb.png', directory: RNSketchCanvas.MAIN_BUNDLE }}
ref={(ref) => (this.canvas = ref)}
style={{flex: 1}}
strokeColor={this.state.color}
strokeWidth={this.state.thickness}
onStrokeStart={(x, y) => {
console.log('x: ', x, ', y: ', y);
this.setState({message: 'Start'});
}}
onStrokeChanged={(x, y) => {
console.log('x: ', x, ', y: ', y);
this.setState({message: 'Changed'});
}}
onStrokeEnd={() => {
this.setState({message: 'End'});
}}
onPathsChange={(pathsCount) => {
console.log('pathsCount', pathsCount);
}}
/>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
}}>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
style={[styles.functionButton, {backgroundColor: 'red'}]}
onPress={() => {
this.setState({color: '#FF0000'});
}}>
<Text style={{color: 'white'}}>Red</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.functionButton, {backgroundColor: 'black'}]}
onPress={() => {
this.setState({color: '#000000'});
}}>
<Text style={{color: 'white'}}>Black</Text>
</TouchableOpacity>
</View>
<Text style={{marginRight: 8, fontSize: 20}}>
{this.state.message}
</Text>
<TouchableOpacity
style={[
styles.functionButton,
{backgroundColor: 'black', width: 90},
]}
onPress={() => {
console.log(this.canvas.getPaths());
Alert.alert(JSON.stringify(this.canvas.getPaths()));
this.canvas.getBase64(
'jpg',
false,
true,
true,
true,
(err, result) => {
console.log(result);
},
);
}}>
<Text style={{color: 'white'}}>Get Paths</Text>
</TouchableOpacity>
</View>
</View>
</View>
)}
{this.state.example === 3 && (
<View style={{flex: 1, flexDirection: 'column'}}>
<RNSketchCanvas
ref={(ref) => (this.canvas1 = ref)}
user={'user1'}
containerStyle={{backgroundColor: 'transparent', flex: 1}}
canvasStyle={{backgroundColor: 'transparent', flex: 1}}
onStrokeEnd={(data) => {}}
closeComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Close</Text>
</View>
}
onClosePressed={() => {
this.setState({example: 0});
}}
undoComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Undo</Text>
</View>
}
onUndoPressed={(id) => {
this.canvas2.deletePath(id);
}}
clearComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Clear</Text>
</View>
}
onClearPressed={() => {
this.canvas2.clear();
}}
eraseComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Eraser</Text>
</View>
}
strokeComponent={(color) => (
<View
style={[{backgroundColor: color}, styles.strokeColorButton]}
/>
)}
strokeSelectedComponent={(color, index, changed) => {
return (
<View
style={[
{backgroundColor: color, borderWidth: 2},
styles.strokeColorButton,
]}
/>
);
}}
strokeWidthComponent={(w) => {
return (
<View style={styles.strokeWidthButton}>
<View
style={{
backgroundColor: 'white',
marginHorizontal: 2.5,
width: Math.sqrt(w / 3) * 10,
height: Math.sqrt(w / 3) * 10,
borderRadius: (Math.sqrt(w / 3) * 10) / 2,
}}
/>
</View>
);
}}
defaultStrokeIndex={0}
defaultStrokeWidth={5}
saveComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Save</Text>
</View>
}
savePreference={() => {
return {
folder: 'RNSketchCanvas',
filename: String(Math.ceil(Math.random() * 100000000)),
transparent: true,
imageType: 'jpg',
};
}}
onSketchSaved={(success, path) => {
Alert.alert(
success ? 'Image saved!' : 'Failed to save image!',
path,
);
}}
onStrokeEnd={(path) => {
this.canvas2.addPath(path);
}}
onPathsChange={(pathsCount) => {
console.log('pathsCount(user1)', pathsCount);
}}
/>
<RNSketchCanvas
ref={(ref) => (this.canvas2 = ref)}
user={'user2'}
containerStyle={{backgroundColor: 'transparent', flex: 1}}
canvasStyle={{backgroundColor: 'transparent', flex: 1}}
onStrokeEnd={(data) => {}}
undoComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Undo</Text>
</View>
}
onUndoPressed={(id) => {
this.canvas1.deletePath(id);
}}
clearComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Clear</Text>
</View>
}
onClearPressed={() => {
this.canvas1.clear();
}}
eraseComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Eraser</Text>
</View>
}
strokeComponent={(color) => (
<View
style={[{backgroundColor: color}, styles.strokeColorButton]}
/>
)}
strokeSelectedComponent={(color, index, changed) => {
return (
<View
style={[
{backgroundColor: color, borderWidth: 2},
styles.strokeColorButton,
]}
/>
);
}}
strokeWidthComponent={(w) => {
return (
<View style={styles.strokeWidthButton}>
<View
style={{
backgroundColor: 'white',
marginHorizontal: 2.5,
width: Math.sqrt(w / 3) * 10,
height: Math.sqrt(w / 3) * 10,
borderRadius: (Math.sqrt(w / 3) * 10) / 2,
}}
/>
</View>
);
}}
defaultStrokeIndex={0}
defaultStrokeWidth={5}
saveComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Save</Text>
</View>
}
savePreference={() => {
return {
folder: 'RNSketchCanvas',
filename: String(Math.ceil(Math.random() * 100000000)),
transparent: true,
imageType: 'jpg',
};
}}
onSketchSaved={(success, path) => {
Alert.alert(
success ? 'Image saved!' : 'Failed to save image!',
path,
);
}}
onStrokeEnd={(path) => {
this.canvas1.addPath(path);
}}
onPathsChange={(pathsCount) => {
console.log('pathsCount(user2)', pathsCount);
}}
/>
</View>
)}
{this.state.example === 4 &&
(this.state.photoPath === null ? (
<View style={styles.cameraContainer}>
<RNCamera
ref={(ref) => {
this.camera = ref;
}}
style={styles.preview}
type={RNCamera.Constants.Type.back}
flashMode={RNCamera.Constants.FlashMode.on}
permissionDialogTitle={'Permission to use camera'}
permissionDialogMessage={
'We need your permission to use your camera phone'
}
/>
<View
style={{
flex: 0,
flexDirection: 'row',
justifyContent: 'center',
}}>
<TouchableOpacity
onPress={this.takePicture.bind(this)}
style={styles.capture}>
<Text style={{fontSize: 14}}> SNAP </Text>
</TouchableOpacity>
</View>
</View>
) : (
<View style={{flex: 1, flexDirection: 'row'}}>
<RNSketchCanvas
localSourceImage={{
filename: this.state.photoPath,
directory: null,
mode: 'AspectFit',
}}
containerStyle={{backgroundColor: 'transparent', flex: 1}}
canvasStyle={{backgroundColor: 'transparent', flex: 1}}
onStrokeEnd={(data) => {}}
closeComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Close</Text>
</View>
}
onClosePressed={() => {
this.setState({example: 0});
}}
undoComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Undo</Text>
</View>
}
onUndoPressed={(id) => {
// Alert.alert('do something')
}}
clearComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Clear</Text>
</View>
}
onClearPressed={() => {
// Alert.alert('do something')
}}
eraseComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Eraser</Text>
</View>
}
strokeComponent={(color) => (
<View
style={[{backgroundColor: color}, styles.strokeColorButton]}
/>
)}
strokeSelectedComponent={(color, index, changed) => {
return (
<View
style={[
{backgroundColor: color, borderWidth: 2},
styles.strokeColorButton,
]}
/>
);
}}
strokeWidthComponent={(w) => {
return (
<View style={styles.strokeWidthButton}>
<View
style={{
backgroundColor: 'white',
marginHorizontal: 2.5,
width: Math.sqrt(w / 3) * 10,
height: Math.sqrt(w / 3) * 10,
borderRadius: (Math.sqrt(w / 3) * 10) / 2,
}}
/>
</View>
);
}}
defaultStrokeIndex={0}
defaultStrokeWidth={5}
saveComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Save</Text>
</View>
}
savePreference={() => {
return {
folder: 'RNSketchCanvas',
filename: String(Math.ceil(Math.random() * 100000000)),
transparent: false,
imageType: 'png',
};
}}
onSketchSaved={(success, path) => {
Alert.alert(
success ? 'Image saved!' : 'Failed to save image!',
path,
);
}}
onPathsChange={(pathsCount) => {
console.log('pathsCount', pathsCount);
}}
/>
</View>
))}
{this.state.example === 5 && (
<View style={{flex: 1, flexDirection: 'row'}}>
<RNSketchCanvas
localSourceImage={{
filename: 'whale.png',
directory: SketchCanvas.MAIN_BUNDLE,
mode: 'AspectFit',
}}
// localSourceImage={{ filename: 'bulb.png', directory: RNSketchCanvas.MAIN_BUNDLE }}
containerStyle={{backgroundColor: 'transparent', flex: 1}}
canvasStyle={{backgroundColor: 'transparent', flex: 1}}
onStrokeEnd={(data) => {}}
closeComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Close</Text>
</View>
}
onClosePressed={() => {
this.setState({example: 0});
}}
undoComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Undo</Text>
</View>
}
onUndoPressed={(id) => {
// Alert.alert('do something')
}}
clearComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Clear</Text>
</View>
}
onClearPressed={() => {
// Alert.alert('do something')
}}
eraseComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Eraser</Text>
</View>
}
strokeComponent={(color) => (
<View
style={[{backgroundColor: color}, styles.strokeColorButton]}
/>
)}
strokeSelectedComponent={(color, index, changed) => {
return (
<View
style={[
{backgroundColor: color, borderWidth: 2},
styles.strokeColorButton,
]}
/>
);
}}
strokeWidthComponent={(w) => {
return (
<View style={styles.strokeWidthButton}>
<View
style={{
backgroundColor: 'white',
marginHorizontal: 2.5,
width: Math.sqrt(w / 3) * 10,
height: Math.sqrt(w / 3) * 10,
borderRadius: (Math.sqrt(w / 3) * 10) / 2,
}}
/>
</View>
);
}}
defaultStrokeIndex={0}
defaultStrokeWidth={5}
saveComponent={
<View style={styles.functionButton}>
<Text style={{color: 'white'}}>Save</Text>
</View>
}
savePreference={() => {
return {
folder: 'RNSketchCanvas',
filename: String(Math.ceil(Math.random() * 100000000)),
transparent: false,
includeImage: false,
cropToImageSize: false,
imageType: 'jpg',
};
}}
onSketchSaved={(success, path) => {
Alert.alert(
success ? 'Image saved!' : 'Failed to save image!',
path,
);
}}
onPathsChange={(pathsCount) => {
console.log('pathsCount', pathsCount);
}}
/>
</View>
)}
{this.state.example === 7 && (
<View style={{flex: 1, flexDirection: 'row'}}>
<ScrollView
style={{flex: 1}}
contentContainerStyle={{padding: 36}}
scrollEnabled={this.state.scrollEnabled}>
<TouchableOpacity onPress={() => this.setState({example: 0})}>
<Text>Close</Text>
</TouchableOpacity>
<SketchCanvas
localSourceImage={{
filename: 'whale.png',
directory: SketchCanvas.MAIN_BUNDLE,
mode: 'AspectFit',
}}
style={styles.page}
onStrokeStart={() => this.setState({scrollEnabled: false})}
onStrokeEnd={() => this.setState({scrollEnabled: true})}
/>
<SketchCanvas
style={styles.page}
onStrokeStart={() => this.setState({scrollEnabled: false})}
onStrokeEnd={() => this.setState({scrollEnabled: true})}
/>
<SketchCanvas
style={styles.page}
onStrokeStart={() => this.setState({scrollEnabled: false})}
onStrokeEnd={() => this.setState({scrollEnabled: true})}
/>
<SketchCanvas
style={styles.page}
onStrokeStart={() => this.setState({scrollEnabled: false})}
onStrokeEnd={() => this.setState({scrollEnabled: true})}
/>
</ScrollView>
</View>
)}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
strokeColorButton: {
marginHorizontal: 2.5,
marginVertical: 8,
width: 30,
height: 30,
borderRadius: 15,
},
strokeWidthButton: {
marginHorizontal: 2.5,
marginVertical: 8,
width: 30,
height: 30,
borderRadius: 15,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#39579A',
},
functionButton: {
marginHorizontal: 2.5,
marginVertical: 8,
height: 30,
width: 60,
backgroundColor: '#39579A',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 5,
},
cameraContainer: {
flex: 1,
flexDirection: 'column',
backgroundColor: 'black',
alignSelf: 'stretch',
},
preview: {
flex: 1,
justifyContent: 'flex-end',
},
capture: {
flex: 0,
backgroundColor: '#fff',
borderRadius: 5,
padding: 15,
paddingHorizontal: 20,
alignSelf: 'center',
margin: 20,
},
page: {
flex: 1,
height: 300,
elevation: 2,
marginVertical: 8,
backgroundColor: 'white',
shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.75,
shadowRadius: 2,
},
});
AppRegistry.registerComponent('example', () => example);