美文网首页
react-native 画笔(写字板、手写板)--1

react-native 画笔(写字板、手写板)--1

作者: 物联白菜 | 来源:发表于2021-11-24 14:06 被阅读0次

    参考链接:https://www.jianshu.com/p/504c639063b3
    前提:集成好react-native-svg

    以上链接博主大佬用的是ART绘画系统,受此启发我就使用SVG画图了,因为项目里面还有其他操作,例如箭头、圆圈、矩形等类似电脑截图后编辑操作,而ART用法个人不是很熟悉,所以采用SVG,SVG不是很熟悉的可以看一下我之前写的这篇https://www.jianshu.com/p/ef91237a89a4,也可以自己上菜鸟教程简单的练一下再上手,做了个小demo,效果图如下:

    441637732888_.pic.jpg

    把以下代码复制粘贴到一个新页面即可尝试,至于Util.size.width、Util.size.height是屏幕宽高,换一下就好了。这只是画线功能,其他功能效果(例如圆圈、矩形、箭头等功能)请看另外一篇文章: https://www.jianshu.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'},
    })
    
    

    在项目里使用实例,效果图如下


    192451637727165_.pic.jpg

    本来在手势那里,因为画图是在图片范围内,我是尝试用locationX,locationY 而不是pageX、pageY,后来发现
    locationX超过范围后那个点会返回对立的那一面,想着解决太麻烦了,所以采用pageX和pageY,如图所见,手势起始点的位置和移动的位置点减去他的边距即可。目前是画笔,后续操作实现了再更新实现代码。

    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'
        },
    
    
    });
    
    

    相关文章

      网友评论

          本文标题:react-native 画笔(写字板、手写板)--1

          本文链接:https://www.haomeiwen.com/subject/inpntrtx.html