废话少说先上效果
使用的第三方库
- @react-native-community/art 图形绘制
- react-native-gesture-handler 手势处理
关键代码
下面代码展示的如何通过四个点的坐标绘制一个距形。
//点的直径
const POINT_WIDTH = 40;
//点的半径
const POINT_RADIUS = POINT_WIDTH/2;
//屏幕宽度
const screenWidth = Dimensions.get('screen').width;
//View 容器高度
const HEIGHT = 200;
//坐标不可以紧贴最外层View,否则绘制左上角触摸点的时候会出现绘制不全的问题。
//左上角坐标
const [leftTop, setLeftTop] = useState({x: POINT_RADIUS, y: POINT_RADIUS});
//右上角坐标
const [rightTop, setRightTop] = useState({x: screenWidth - POINT_RADIUS, y: POINT_RADIUS});
//左下角坐标
const [leftBottom, setLeftBottom] = useState({x: POINT_RADIUS, y: HEIGHT - POINT_RADIUS});
//右下角坐标
const [rightBottom, setRightBottom] = useState({x: screenWidth - POINT_RADIUS, y: HEIGHT - POINT_RADIUS});
//渲染点组成的边框
const renderLine = () => {
let react = Path()
.moveTo(leftTop.x - POINT_RADIUS, leftTop.y - POINT_RADIUS)
.lineTo(rightTop.x + POINT_RADIUS , rightTop.y - POINT_RADIUS)
.lineTo(rightBottom.x + POINT_RADIUS, rightBottom.y + POINT_RADIUS)
.lineTo(leftBottom.x - POINT_RADIUS, leftBottom.y + POINT_RADIUS)
.close() // close 封闭
return(
<Surface width={screenWidth} height={HEIGHT}>
<Shape
d={react}
strokeWidth={0}
fill="rgba(207, 21, 21, 0.29)"
/>
</Surface>
)
}
接着我们借助 PanGestureHandler 这个手势处理组建,去监听用户在四边形四个顶点的滑动事件,去改变四个顶点的坐标,这样我们就可以得到我们最后想要的效果了。
完整代码
import React, {useState, useEffect, useRef} from "react";
import {View, StyleSheet, Dimensions, Text} from "react-native";
import {Surface, Shape, Path} from '@react-native-community/art';
import {PanGestureHandler, State, GestureHandlerRootView} from "react-native-gesture-handler"
//点的直径
const POINT_WIDTH = 40;
//点的半径
const POINT_RADIUS = POINT_WIDTH/2;
//屏幕宽度
const screenWidth = Dimensions.get('screen').width;
//View 容器高度
const HEIGHT = 200;
const CutView = () => {
//坐标不可以紧贴最外层View,否则绘制左上角触摸点的时候会出现绘制不全的问题。
//左上角坐标
const [leftTop, setLeftTop] = useState({x: POINT_RADIUS, y: POINT_RADIUS});
//右上角坐标
const [rightTop, setRightTop] = useState({x: screenWidth - POINT_RADIUS, y: POINT_RADIUS});
//左下角坐标
const [leftBottom, setLeftBottom] = useState({x: POINT_RADIUS, y: HEIGHT - POINT_RADIUS});
//右下角坐标
const [rightBottom, setRightBottom] = useState({x: screenWidth - POINT_RADIUS, y: HEIGHT - POINT_RADIUS});
//当前组件距离页面顶部的距离
const [pageY, setPageY] = useState(0);
const container = useRef(null);
//处理左上角点滚动
const onLeftTop = ({nativeEvent}) => {
let {absoluteX, absoluteY} = nativeEvent;
let y = absoluteY - pageY;
//计算可以滚动的范围
if(absoluteX <= POINT_RADIUS){
absoluteX = POINT_RADIUS;
}
if(absoluteX >= rightTop.x - POINT_WIDTH){
absoluteX = rightTop.x - POINT_WIDTH;
}
if(y <= POINT_RADIUS){
y = POINT_RADIUS
}
if(y>= leftBottom.y - POINT_WIDTH){
y = leftBottom.y - POINT_WIDTH
}
setLeftTop({
x: absoluteX,
y
})
setLeftBottom({
x: absoluteX,
y: leftBottom.y
})
setRightTop({
x: rightTop.x,
y
})
}
//处理右上角滚动
const onRightTop = ({nativeEvent}) => {
let {absoluteX, absoluteY} = nativeEvent;
let y = absoluteY - pageY;
//计算可以滚动的范围
if(absoluteX >= screenWidth - POINT_RADIUS){
absoluteX = screenWidth - POINT_RADIUS;
}
if(absoluteX <= leftTop.x + POINT_WIDTH){
absoluteX = leftTop.x + POINT_WIDTH;
}
if(y <= POINT_RADIUS){
y = POINT_RADIUS;
}
if(y >= rightBottom.y - POINT_WIDTH){
y = rightBottom.y - POINT_WIDTH;
}
setRightTop({
x: absoluteX,
y
})
setLeftTop({
x: leftTop.x,
y
})
setRightBottom({
x: absoluteX,
y: rightBottom.y
})
}
//处理左下角滚动
const onLeftBottom = ({nativeEvent}) => {
let {absoluteX, absoluteY} = nativeEvent;
let y = absoluteY - pageY;
//计算可以滚动的范围
if(absoluteX <= POINT_RADIUS){
absoluteX = POINT_RADIUS;
}
if(absoluteX >= rightBottom.x - POINT_WIDTH){
absoluteX = rightBottom.x - POINT_WIDTH;
}
if(y <= leftTop.y + POINT_WIDTH){
y = leftTop.y + POINT_WIDTH;
}
if(y >= HEIGHT - POINT_RADIUS){
y = HEIGHT - POINT_RADIUS;
}
setLeftBottom({
x: absoluteX,
y
})
setLeftTop({
x: absoluteX,
y: leftTop.y
})
setRightBottom({
x: rightBottom.x,
y
})
}
//处理右下角滚动
const onRightBottom = ({nativeEvent}) => {
let {absoluteX, absoluteY} = nativeEvent;
let y = absoluteY - pageY;
//计算可以滚动的范围
if(absoluteX <= leftBottom.x + POINT_WIDTH){
absoluteX = leftBottom.x + POINT_WIDTH;
}
if(absoluteX >= screenWidth - POINT_RADIUS){
absoluteX = screenWidth - POINT_RADIUS;
}
if(y <= rightTop.y + POINT_WIDTH){
y = rightTop.y + POINT_WIDTH;
}
if(y >= HEIGHT - POINT_RADIUS){
y = HEIGHT - POINT_RADIUS;
}
setRightBottom({
x: absoluteX,
y
})
setRightTop({
x: absoluteX,
y: rightTop.y
})
setLeftBottom({
x: leftBottom.x,
y
})
}
//渲染左上角的点
const renderLeftTop = () => {
//由于位置是在左上角所以画点的时候要重新计算下
let left = leftTop.x - POINT_RADIUS;
let top = leftTop.y - POINT_RADIUS;
return(
<PanGestureHandler
onGestureEvent={onLeftTop}
>
<View
style={[styles.point,
{width: POINT_WIDTH, height: POINT_WIDTH, borderRadius: POINT_WIDTH/2},
{left, top}
]}
/>
</PanGestureHandler>
)
}
//渲染右上角的点
const renderRightTop = () => {
//由于位置是在左上角所以画点的时候要重新计算下
let left = rightTop.x - POINT_RADIUS;
let top = rightTop.y - POINT_RADIUS;
return(
<PanGestureHandler
onGestureEvent={onRightTop}
>
<View
style={[styles.point,
{width: POINT_WIDTH, height: POINT_WIDTH, borderRadius: POINT_WIDTH/2},
{left, top}
]}
/>
</PanGestureHandler>
)
}
//渲染左下角的点
const renderLeftBottom = () => {
//由于位置是在左上角所以画点的时候要重新计算下
let left = leftBottom.x - POINT_RADIUS;
let top = leftBottom.y - POINT_RADIUS;
return(
<PanGestureHandler
onGestureEvent={onLeftBottom}
>
<View
style={[styles.point,
{width: POINT_WIDTH, height: POINT_WIDTH, borderRadius: POINT_WIDTH/2},
{left, top}
]}
/>
</PanGestureHandler>
)
}
//渲染右下角的点
const renderRightBottom = () => {
//由于位置是在左上角所以画点的时候要重新计算下
let left = rightBottom.x - POINT_RADIUS;
let top = rightBottom.y - POINT_RADIUS;
return(
<PanGestureHandler
onGestureEvent={onRightBottom}
>
<View
style={[styles.point,
{width: POINT_WIDTH, height: POINT_WIDTH, borderRadius: POINT_WIDTH/2},
{left, top}
]}
/>
</PanGestureHandler>
)
}
//渲染点组成的边框
const renderLine = () => {
let react = Path()
.moveTo(leftTop.x - POINT_RADIUS, leftTop.y - POINT_RADIUS)
.lineTo(rightTop.x + POINT_RADIUS , rightTop.y - POINT_RADIUS)
.lineTo(rightBottom.x + POINT_RADIUS, rightBottom.y + POINT_RADIUS)
.lineTo(leftBottom.x - POINT_RADIUS, leftBottom.y + POINT_RADIUS)
.close() // close 封闭
return(
<Surface width={screenWidth} height={HEIGHT}>
<Shape
d={react}
strokeWidth={0}
fill="rgba(207, 21, 21, 0.29)"
/>
</Surface>
)
}
return(
<View
ref={container}
style={styles.container}
onLayout={()=>{
container?.current?.measure((x,y,width,height,pageX, pageY)=>{
setPageY(pageY);
})
}}
>
{renderLine()}
{renderLeftTop()}
{renderRightTop()}
{renderLeftBottom()}
{renderRightBottom()}
</View>
)
}
export default CutView;
const styles = StyleSheet.create({
container: {
width: screenWidth,
height: HEIGHT,
backgroundColor: 'black'
},
point: {
position: 'absolute',
backgroundColor: 'red'
}
})