Skip to content

React Native React Native SVG: Drag an adjustable polygon | ShillehTek

October 23, 2023

Video Tutorial (Optional)

Watch first if you want the full walkthrough in real time. For more context, watch Part 1 before starting.

Project Overview

React Native + React Native SVG: In this tutorial you use React Native with react-native-svg to render a 4-corner polygon whose vertices can be adjusted by touch, letting users drag corners to reshape it on screen.

In Part 1, we drew free-form on a canvas using react-native-svg. Here, we extend that by adding an adjustable shape (a polygon) that the user can move by dragging its corners. The same approach can be adapted to other shapes with minor code changes.

Subscribe: Youtube

Support: https://www.buymeacoffee.com/mmshilleh

  • Time: 20 to 40 minutes
  • Skill level: Beginner
  • What you will build: A touch-adjustable 4-vertex polygon rendered with react-native-svg
React Native app using react-native-svg showing a grey polygon with four draggable corner points
Finished example: a four-corner polygon where each vertex can be adjusted by touch.

Parts List

From ShillehTek

  • None for this software-only tutorial

External

Note: This tutorial assumes you already have a React Native project set up and that react-native-svg is available in your app.

Step-by-Step Guide

Step 1 - Review Part 1 for context

Goal: Start from the same foundation used for free-form drawing with react-native-svg.

What to do: If you have not already, read and/or watch Part 1 before continuing.

Expected result: You understand how react-native-svg renders content using screen coordinates.

Step 2 - Render a polygon and its draggable vertices

Goal: Draw a four-vertex polygon (Path) and show each corner (Circle) so a user can adjust the shape.

What to do: Add the following component code to your React Native project. It creates a polygon from 4 vertices, renders it, and draws a small circle at each vertex.

Code:

import React, { useState } from 'react';
import { View, StyleSheet, Dimensions, TouchableOpacity, Text } from 'react-native';
import { Svg, Path, Circle } from 'react-native-svg';

const { height, width } = Dimensions.get('window');

export default () => {
  const windowWidth = Dimensions.get('window').width;
  const [polygonVertices, setPolygonVertices] = useState([
    { x: windowWidth / 4, y: windowWidth / 4 },
    { x: windowWidth * 3 / 4, y: windowWidth / 4 },
    { x: windowWidth * 3 / 4, y: windowWidth * 3 / 4 },
    { x: windowWidth / 4, y: windowWidth * 3 / 4 },
  ]);
  const [draggedVertexIndex, setDraggedVertexIndex] = useState(null);

  const polygonPath = polygonVertices
    .map((vertex) => `${vertex.x},${vertex.y}`)
    .join(' ');
 
  const getActiveVertex = (x, y) => {
    const threshold = 20; 
    for (let i = 0; i < polygonVertices.length; i++) {
      const vertex = polygonVertices[i];
      const distance = Math.sqrt((x - vertex.x) ** 2 + (y - vertex.y) ** 2);
      if (distance <= threshold) {
        return i;
      }
    }
    return null;
  };

  const onTouchEndPolygon = () => {
    setDraggedVertexIndex(null);
  };

  const onTouchMovePolygon = (event) => {
    const locationX = event.nativeEvent.locationX;
    const locationY = event.nativeEvent.locationY;
    let activeVertex;
    if (draggedVertexIndex === null) {
      activeVertex = getActiveVertex(locationX, locationY);
      setDraggedVertexIndex(activeVertex);
    }
    const updatedVertices = [...polygonVertices];
    updatedVertices[draggedVertexIndex] = {
      x: event.nativeEvent.locationX,
      y: event.nativeEvent.locationY,
    };
    setPolygonVertices(updatedVertices);
  };

  const handleReset = () => { setPolygonVertices([
    { x: windowWidth / 4, y: windowWidth / 4 },
    { x: windowWidth * 3 / 4, y: windowWidth / 4 },
    { x: windowWidth * 3 / 4, y: windowWidth * 3 / 4 },
    { x: windowWidth / 4, y: windowWidth * 3 / 4 },
  ])}

  return (
    <View style={styles.container}>
      <View style={styles.svgContainer} onTouchMove={onTouchMovePolygon} onTouchEnd={onTouchEndPolygon}>
        <Svg> 
          <Path d={`M${polygonPath}Z`} fill="grey" stroke="grey" strokeWidth={2} />
          {polygonVertices.map((vertex, index) => (
            <Circle
              key={index}
              cx={vertex.x}
              cy={vertex.y}
              r={4}
              fill="black"
            />
          ))}
        </Svg>
      </View>
      <TouchableOpacity style={styles.clearButton} onPress={handleReset}>
        <Text style={styles.clearButtonText}>Reset</Text>
      </TouchableOpacity>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  svgContainer: {
    height: height * 0.7,
    width,
    borderColor: 'black',
    backgroundColor: 'white',
    borderWidth: 1,
  },
  clearButton: {
    marginTop: 10,
    backgroundColor: 'black',
    paddingVertical: 10,
    paddingHorizontal: 20,
    borderRadius: 5,
  },
  clearButtonText: {
    color: 'white',
    fontSize: 16,
    fontWeight: 'bold',
  },
});

Expected result: You see a filled four-corner polygon and four visible points (one per vertex), plus a Reset button.

Step 3 - Implement vertex selection and dragging logic

Goal: Move the correct polygon vertex based on where the user touches and drags.

What to do: Use these behaviors (already implemented in the code above):

  • Render a shape by connecting four vertices using SVG.
  • When the user touches near a point, determine which vertex to move with getActiveVertex using a distance threshold.
  • Continue moving the selected vertex as the user drags (update that vertex on touch move).
  • Release the vertex selection on touch end.

Expected result: Dragging near a corner updates that corner and the polygon reshapes in real time. Tapping Reset returns the polygon to its default position.

Conclusion

You built an adjustable four-vertex polygon in React Native using react-native-svg, where each corner can be repositioned with touch input. SVG makes this straightforward because you can work directly in screen coordinates.

Want to support our work or build your next project faster? Grab parts and tools from ShillehTek.com. If you want help customizing this concept for your app or product, check out our consulting services.