參考鏈接:http://www.reibang.com/p/504c639063b3
前提:集成好react-native-svg
以上鏈接博主大佬用的是ART繪畫系統(tǒng)顺少,受此啟發(fā)我就使用SVG畫圖了,因為項目里面還有其他操作淫痰,例如箭頭把介、圓圈、矩形等類似電腦截圖后編輯操作厢漩,而ART用法個人不是很熟悉膜眠,所以采用SVG,SVG不是很熟悉的可以看一下我之前寫的這篇http://www.reibang.com/p/ef91237a89a4,也可以自己上菜鳥教程簡單的練一下再上手宵膨,做了個小demo架谎,效果圖如下:
把以下代碼復制粘貼到一個新頁面即可嘗試,至于Util.size.width辟躏、Util.size.height是屏幕寬高谷扣,換一下就好了。這只是畫線功能捎琐,其他功能效果(例如圓圈会涎、矩形、箭頭等功能)請看另外一篇文章: http://www.reibang.com/p/06d3bdfae98a
import React, {Component} from 'react';
import {View, Text, StyleSheet, Image, TouchableOpacity, PanResponder, ART, ImageBackground} from 'react-native'
import Util from './common/util'
import Svg, {Path} from "react-native-svg";
class SvgDrawTest extends Component {
constructor(props) {
super(props);
this.allPoint = ''
this.state = {
// drawPath: 'M25 10 L98 65 L70 25 L16 77 L11 30 L0 4 L90 50 L50 10 L11 22 L77 95 L20 25'
drawPath: ''
}
}
componentWillMount() {
this._panResponderDrawLine = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
let tempfirstX = evt.nativeEvent.pageX
let tempFirstY = evt.nativeEvent.pageY
this.firstPoint = ' M' + tempfirstX + ' ' + tempFirstY
this.allPoint = this.allPoint + this.firstPoint //上一次的畫的全部的點(this.allPoint)瑞凑,拼接上當前這次畫的M點末秃,在svg中M為線的起始點,拼接上后在 onPanResponderMove 中將當前移動的所有點再次拼接,當前和之前的拼接完之后,更新頁面線條
},
onPanResponderMove: (evt, gestureState) => {
let pointX = evt.nativeEvent.pageX
let pointY = evt.nativeEvent.pageY
// console.log(`X:${pointX}`, `Y:${pointY}`)
let point = ' L' + pointX + ' ' + pointY
this.allPoint += point
console.log('point====', this.allPoint)
let drawPath = this.allPoint
this.setState({
drawPath
})
},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => { },
onPanResponderTerminate: (evt, gestureState) => { },
onShouldBlockNativeResponder: (evt, gestureState) => {
return true;
},
})
}
clearOut(){
this.allPoint = ''
this.setState({
drawPath:''
})
}
render() {
return (
<View>
<View style={{width:Util.size.width,height:Util.size.height-60, backgroundColor: 'green'}}
{...this._panResponderDrawLine.panHandlers}
>
<Svg height="100%" width="100%">
<Path
d={this.state.drawPath}
fill="none"
stroke="red"
strokeWidth="5"
/>
</Svg>
</View>
<TouchableOpacity onPress={()=>this.clearOut()} style={styles.btn}>
<Text style={{color:'#333',fontSize:16}}>清空</Text>
</TouchableOpacity>
</View>
);
}
}
export default SvgDrawTest;
const styles = StyleSheet.create({
btn:{elevation:5,backgroundColor:'#fff',paddingHorizontal:30,paddingVertical:10,borderRadius:999,justifyContent:'center',alignItems:'center'},
})
在項目里使用實例籽御,效果圖如下
本來在手勢那里蛔溃,因為畫圖是在圖片范圍內,我是嘗試用locationX,locationY 而不是pageX篱蝇、pageY,后來發(fā)現(xiàn)
locationX超過范圍后那個點會返回對立的那一面贺待,想著解決太麻煩了,所以采用pageX和pageY,如圖所見零截,手勢起始點的位置和移動的位置點減去他的邊距即可麸塞。目前是畫筆,后續(xù)操作實現(xiàn)了再更新實現(xiàn)代碼涧衙。
import React, {Component} from 'react';
import {View, Text, Image, StyleSheet, PanResponder,TouchableOpacity} from 'react-native'
import Svg, {Path} from "react-native-svg";
let marginY = 50 + 15 + 5 //頭部導航height 50 下方內容padding 15 圖片與父盒子距離 5
let marginX = 15 + 5 // 下方內容padding 15 圖片與父盒子距離 5
class Comp3 extends Component {
constructor(props) {
super(props);
this.allPoint = ''
this.state = {
drawPath: ''
}
}
componentWillMount() {
this._panResponderDrawLine = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
let tempfirstX = evt.nativeEvent.pageX.toFixed(0)-marginX
let tempFirstY = evt.nativeEvent.pageY.toFixed(0)-marginY
this.firstPoint = ' M' + tempfirstX + ' ' + tempFirstY
this.allPoint += this.firstPoint //上一次的畫的全部的點(this.allPoint)哪工,拼接上當前這次畫的M點,在svg中M為線的起始點,拼接上后在 onPanResponderMove 中將當前移動的所有點再次拼接,當前和之前的拼接完之后弧哎,更新頁面線條
},
onPanResponderMove: (evt, gestureState) => {
let pointX = evt.nativeEvent.pageX.toFixed(0)-marginX
let pointY = evt.nativeEvent.pageY.toFixed(0)-marginY
// console.log(`X:${pointX}`, `Y:${pointY}`)
let point = ` L${pointX} ${pointY}`
this.allPoint += point
let drawPath = this.allPoint
this.setState({
drawPath
})
},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => { },
onPanResponderTerminate: (evt, gestureState) => {},
onShouldBlockNativeResponder: (evt, gestureState) => {
return true;
},
})
}
render() {
return (
<View style={styles.content_left}>
<View style={{flex: 1}} {...this._panResponderDrawLine.panHandlers}>
<Image
source={require('../images/default.jpg')}
style={{height: '100%', width: '100%', position: 'absolute'}}
/>
<Svg height="100%" width="100%">
<Path
d={this.state.drawPath}
fill="none"
stroke="red"
/>
</Svg>
</View>
<View style={styles.camera_bottom}>
<View style={[styles.bottom_item, {flex: 1}]}>
<Text style={styles.bottom_title}>選擇</Text>
<View style={styles.bottom_icon}>
<View>
<Image source={require('../images/opera/icon1.png')}
style={{width: 15, height: 15}}/>
</View>
<View>
<Image source={require('../images/opera/icon2.png')}
style={{width: 15, height: 15}}/>
</View>
</View>
</View>
<View style={[styles.bottom_item, {flex: 2}]}>
<Text style={styles.bottom_title}>線條選擇</Text>
<View style={styles.bottom_icon}>
<View>
<Image source={require('../images/opera/icon3.png')}
style={{width: 15, height: 15}}/>
</View>
<View>
<Image source={require('../images/opera/icon4.png')}
style={{width: 15, height: 15}}/>
</View>
<View>
<Image source={require('../images/opera/icon5.png')}
style={{width: 15, height: 15}}/>
</View>
<View>
<Image source={require('../images/opera/icon6.png')}
style={{width: 15, height: 15}}/>
</View>
</View>
</View>
<View style={[styles.bottom_item, {flex: 2}]}>
<View style={styles.bottom_icon}>
<Text style={styles.bottom_title}>粗細</Text>
<Text style={styles.bottom_title}>色彩</Text>
</View>
<View style={styles.bottom_icon}>
<View style={{flexDirection: 'row', alignItems: 'center'}}>
<Image source={require('../images/opera/icon7.png')}
style={{width: 60, height: 3}}/>
<View>
<Image source={require('../images/opera/triangle.png')}
style={{width: 15, height: 15}}/>
</View>
</View>
<View style={styles.colorSelect}>
<View style={{width: 16, height: 16, backgroundColor: 'red', borderRadius: 2}}/>
<Image source={require('../images/opera/triangle.png')}
style={{width: 15, height: 15}}/>
</View>
</View>
</View>
<View style={[styles.bottom_item, {flex: 2, borderRightWidth: 0}]}>
<Text style={styles.bottom_title}>操作</Text>
<View style={styles.bottom_icon}>
<View style={styles.bottom_btn}>
<Text style={{fontSize: 12}}>撤銷</Text>
</View>
<TouchableOpacity onPress={()=>{this.setState({drawPath:''});this.allPoint = ''}} style={styles.bottom_btn}>
<Text style={{fontSize: 12}}>清空</Text>
</TouchableOpacity>
<View style={[styles.bottom_btn, {backgroundColor: '#203990'}]}>
<Text style={{fontSize: 12, color: '#fff'}}>保存</Text>
</View>
</View>
</View>
</View>
</View>
);
}
}
export default Comp3;
const styles = StyleSheet.create({
/**內容*/
content_left: {
backgroundColor: '#fff',
flex: 3,
marginRight: 15,
padding: 5,
},
content_left_camera: {
flex: 1,
},
camera_bottom: {
backgroundColor: '#fff',
height: '25%',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center'
},
bottom_item: {
paddingHorizontal: 10,
borderRightWidth: 1,
borderColor: '#eee',
height: '70%',
justifyContent: 'space-between'
},
bottom_icon: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
bottom_title: {fontSize: 12, marginBottom: 10},
bottom_btn: {
borderWidth: 1,
borderRadius: 999,
width: '30%',
borderColor: '#A5AAC1',
justifyContent: 'center',
alignItems: 'center',
paddingVertical: 3
},
colorSelect: {
flexDirection: 'row',
alignItems: 'center',
borderWidth: 1,
borderRadius: 5,
paddingHorizontal: 2,
paddingVertical: 2,
borderColor: '#eee'
},
});