1
+ import React , { useState , useEffect , useRef } from "react" ;
2
+ import debounce from 'lodash.debounce' ;
3
+
4
+
5
+ const CanvasExample = props => {
6
+
7
+ const { toggle, addressInfo, backgroundImage, ...rest } = props ;
8
+ const canvasRef = useRef ( null ) ;
9
+ const [ coordinates , setCoordinates ] = useState ( [ ] ) ;
10
+ const [ polyComplete , setPolyComplete ] = useState ( false ) ;
11
+ const hitboxSize = 4 ;
12
+ const black = "rgb(0,0,0)" ;
13
+ const lightGreen = "rgba(0,255,0,0.2)" ;
14
+
15
+
16
+ useEffect ( ( ) => {
17
+ debounce ( ( ) => {
18
+ clearScreen ( ) ;
19
+ } , 50 ) ;
20
+
21
+ } , [ canvasRef . current ] )
22
+
23
+
24
+ const clearScreen = ( ) => {
25
+ let ctx = canvasRef . current . getContext ( "2d" ) ;
26
+ ctx . clearRect ( 0 , 0 , canvasRef . current . width , canvasRef . current . height ) ;
27
+ }
28
+
29
+
30
+ useEffect ( ( ) => {
31
+ //draw dots if there is 1 or more coordinates
32
+ if ( coordinates . length >= 1 ) {
33
+ reDrawDots ( ) ;
34
+ }
35
+ //draw lines if there are 2 or more coordinates
36
+ if ( coordinates . length >= 2 ) {
37
+ reDrawLines ( ) ;
38
+ }
39
+ } , [ coordinates ] )
40
+
41
+
42
+ const reDrawLines = ( ) => {
43
+ let ctx = canvasRef . current . getContext ( "2d" ) ;
44
+ ctx . beginPath ( ) ;
45
+ let first = true ;
46
+ for ( let i = 0 ; i < coordinates . length ; i ++ ) {
47
+ let coord = coordinates [ i ] ;
48
+ if ( first ) {
49
+ ctx . moveTo ( coord . x , coord . y ) ;
50
+ first = false ;
51
+ }
52
+ else {
53
+ ctx . lineTo ( coord . x , coord . y ) ;
54
+ }
55
+ }
56
+ if ( polyComplete ) {
57
+ ctx . closePath ( ) ;
58
+ ctx . fillStyle = lightGreen ;
59
+ ctx . fill ( ) ;
60
+ }
61
+ ctx . stroke ( ) ;
62
+ }
63
+
64
+ const reDrawDots = ( ) => {
65
+ let ctx = canvasRef . current . getContext ( "2d" ) ;
66
+ for ( let i = 0 ; i < coordinates . length ; i ++ ) {
67
+ let coord = coordinates [ i ] ;
68
+ ctx . beginPath ( ) ;
69
+ ctx . arc ( coord . x , coord . y , hitboxSize , 0 , 2 * Math . PI ) ;
70
+ ctx . fillStyle = black ;
71
+ ctx . fill ( ) ;
72
+ }
73
+ }
74
+
75
+
76
+ const canvasClick = e => {
77
+ if ( ! polyComplete ) {
78
+ let first = coordinates [ 0 ] ;
79
+ let rect = canvasRef . current . getBoundingClientRect ( ) ;
80
+ //client x any y are just window coords we need the coords relative to inside the canvas
81
+
82
+ let current = { x : Math . round ( e . clientX - rect . left ) , y : Math . round ( e . clientY - rect . top ) } ;
83
+ let deepcopy = Array . from ( coordinates ) ;
84
+
85
+ //check to see if we are close enough to complete the polygon
86
+ if ( first ) {
87
+ let distX = Math . abs ( current . x - first . x ) ;
88
+ let distY = Math . abs ( current . y - first . y ) ;
89
+ if ( distX <= hitboxSize
90
+ && distY <= hitboxSize
91
+ && deepcopy . length > 2 ) {
92
+ //if we are close enough, do not add the last coordinate,
93
+ // just complete the polygon
94
+ setPolyComplete ( true ) ;
95
+ } else {
96
+ //add the coordinate, draw the line, draw the dot
97
+ deepcopy . push ( current ) ;
98
+ }
99
+ }
100
+ else {
101
+ //add the coordinate, draw the dot
102
+ deepcopy . push ( current ) ;
103
+ }
104
+ setCoordinates ( deepcopy ) ;
105
+ }
106
+ }
107
+
108
+
109
+ const resetCoords = ( ) => {
110
+ clearScreen ( ) ;
111
+ setCoordinates ( [ ] ) ;
112
+ setPolyComplete ( false ) ;
113
+ }
114
+
115
+ const undo = ( ) => {
116
+ clearScreen ( ) ;
117
+ setCoordinates ( coordinates . slice ( 0 , - 1 ) ) ;
118
+ setPolyComplete ( false ) ;
119
+ }
120
+
121
+ const finish = async ( ) => {
122
+
123
+ if ( coordinates != [ ] ) {
124
+ const coords = coordinates . map ( c => c . x + " " + c . y ) . toString ( ) ;
125
+ //your api call here to save the coords
126
+ }
127
+ }
128
+
129
+
130
+ return (
131
+ < div >
132
+ { backgroundImage &&
133
+ < canvas
134
+ ref = { canvasRef }
135
+ onClick = { ( e ) => canvasClick ( e ) }
136
+ width = { 1000 }
137
+ height = { 700 }
138
+ style = { { borderWidth :"1px" , borderColor :"black" , borderStyle :"solid"
139
+ , background : 'url(' + backgroundImage + ')' } }
140
+ >
141
+
142
+ </ canvas >
143
+ }
144
+
145
+ < div style = { { display :'flex' , flex :1 , flexDirection :'row' , width :'100%' , paddingTop :'1rem' } } >
146
+ < div style = { { display :'flex' , flex :1 , flexDirection :'row' , width :'100%' } } >
147
+ < button title = "Undo" onClick = { ( ) => undo ( ) } />
148
+ < button title = "Reset" onClick = { ( ) => resetCoords ( ) } />
149
+ < button title = "Complete" onClick = { ( ) => finish ( ) } />
150
+ </ div >
151
+ </ div >
152
+ </ div >
153
+ ) ;
154
+
155
+ }
156
+ export default CanvasExample ;
0 commit comments