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

2021-11-24  本文已影响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'
    },


});

上一篇下一篇

猜你喜欢

热点阅读