1
+ const srcImgContainer = document . querySelector ( '.src-img-container' ) ;
2
+ const selectionContainer = document . querySelector ( '.selection-container' ) ;
3
+ const rect = document . querySelector ( '.selection-rect' ) ;
4
+ const targetImg = document . querySelector ( '.target' ) ;
5
+ const draggers = [ 0 , 0 , 0 , 0 ] . map ( ( ) => {
6
+ return createDragger ( srcImgContainer ) ;
7
+ } ) ;
8
+
9
+ let dragger = draggers [ 2 ] ;
10
+ let mousedown = false ;
11
+ let x0 = 0
12
+ let y0 = 0 ;
13
+ let x1 = x0 + 96 ;
14
+ let y1 = y0 + 96 ;
15
+ let lastX = 0 ;
16
+ let lastY = 0 ;
17
+
18
+ /**
19
+ * @param {number } min
20
+ * @param {number } value
21
+ * @param {number } max
22
+ * @return {number }
23
+ */
24
+ function bound ( min , value , max ) {
25
+ return Math . max ( min , Math . min ( value , max ) ) ;
26
+ }
27
+
28
+ /**
29
+ * Gets the Rect for the currently selected coordinates.
30
+ * @return {{
31
+ * top: number,
32
+ * bottom: number,
33
+ * left: number,
34
+ * right: number,
35
+ * width: number,
36
+ * height: number,
37
+ * }}
38
+ */
39
+ function getSelectedRect ( ) {
40
+ return {
41
+ top : Math . min ( y0 , y1 ) ,
42
+ bottom : Math . max ( y0 , y1 ) ,
43
+ left : Math . min ( x0 , x1 ) ,
44
+ right : Math . max ( x0 , x1 ) ,
45
+ width : Math . abs ( x0 - x1 ) ,
46
+ height : Math . abs ( y0 - y1 ) ,
47
+ } ;
48
+ }
49
+
50
+ /**
51
+ * Notifies interested parties that a new area was selected.
52
+ */
53
+ function notifySelected ( ) {
54
+ window . dispatchEvent ( new CustomEvent ( 'area-selected' , {
55
+ detail : getSelectedRect ( ) ,
56
+ } ) ) ;
57
+ }
58
+
59
+ /**
60
+ * Handles a mousedown on a dragger, setting it as the current dragger.
61
+ */
62
+ function mousedownDragger ( ) {
63
+ mousedown = true ;
64
+
65
+ dragger = event . target ;
66
+ event . stopPropagation ( ) ;
67
+ }
68
+
69
+ /**
70
+ * Creates a dragger and appends it to the container.
71
+ * @param {!Element } container
72
+ */
73
+ function createDragger ( container ) {
74
+ const div = document . createElement ( 'div' ) ;
75
+ div . className = 'dragger' ;
76
+ div . onmousedown = mousedownDragger ;
77
+ container . appendChild ( div ) ;
78
+ return div ;
79
+ }
80
+
81
+ /**
82
+ * Updates the UI (the draggers and the highlight rect) for the currently
83
+ * selected coordinates.
84
+ */
85
+ function updateSelected ( ) {
86
+ const {
87
+ top,
88
+ left,
89
+ width,
90
+ height,
91
+ } = getSelectedRect ( ) ;
92
+ draggers [ 0 ] . style . transform = `translate(${ x0 } px, ${ y0 } px)` ;
93
+ draggers [ 1 ] . style . transform = `translate(${ x1 } px, ${ y0 } px)` ;
94
+ draggers [ 2 ] . style . transform = `translate(${ x1 } px, ${ y1 } px)` ;
95
+ draggers [ 3 ] . style . transform = `translate(${ x0 } px, ${ y1 } px)` ;
96
+ rect . style . left = `${ left } px` ;
97
+ rect . style . top = `${ top } px` ;
98
+ rect . style . width = `${ width } px` ;
99
+ rect . style . height = `${ height } px` ;
100
+ }
101
+
102
+ /**
103
+ * Updates the current coordinates based on the last mouse location and whether
104
+ * or not the shift key is pressed. When the shift key is pressed, a 1:1 aspect
105
+ * ration is forced.
106
+ * @param {boolean } shiftKey
107
+ */
108
+ function updateCoordinates ( shiftKey ) {
109
+ const draggerIndex = draggers . indexOf ( dragger ) ;
110
+ const initialRect = targetImg . getBoundingClientRect ( ) ;
111
+ const startX = draggerIndex == 0 || draggerIndex == 3 ? x1 : x0 ;
112
+ const startY = draggerIndex == 0 || draggerIndex == 1 ? y1 : y0 ;
113
+ const x = lastX - initialRect . left ;
114
+ const y = lastY - initialRect . top ;
115
+ const targetRect = targetImg . getBoundingClientRect ( ) ;
116
+ const boundX = bound ( 0 , x , targetRect . width ) ;
117
+ const boundY = bound ( 0 , y , targetRect . height ) ;
118
+
119
+ let xDelta = startX - boundX ;
120
+ let yDelta = startY - boundY ;
121
+
122
+ // Lock aspect ratio to 1:1.
123
+ if ( shiftKey ) {
124
+ const smallerSize = Math . min ( Math . abs ( xDelta ) , Math . abs ( yDelta ) ) ;
125
+ xDelta = bound ( - smallerSize , xDelta , smallerSize ) ;
126
+ yDelta = bound ( - smallerSize , yDelta , smallerSize ) ;
127
+ }
128
+
129
+ // Based on which dragger is moving, update the correct coordinates.
130
+ switch ( draggerIndex ) {
131
+ case 0 :
132
+ x0 = x1 - xDelta ;
133
+ y0 = y1 - yDelta ;
134
+ break ;
135
+ case 1 :
136
+ x1 = x0 - xDelta ;
137
+ y0 = y1 - yDelta ;
138
+ break ;
139
+ case 2 :
140
+ x1 = x0 - xDelta ;
141
+ y1 = y0 - yDelta ;
142
+ break ;
143
+ case 3 :
144
+ x0 = x1 - xDelta ;
145
+ y1 = y0 - yDelta ;
146
+ break ;
147
+ }
148
+
149
+ updateSelected ( ) ;
150
+ event . preventDefault ( ) ;
151
+ }
152
+
153
+ function resetSelection ( ) {
154
+ x0 = 0
155
+ y0 = 0 ;
156
+ x1 = x0 + 96 ;
157
+ y1 = y0 + 96 ;
158
+
159
+ updateSelected ( ) ;
160
+ notifySelected ( ) ;
161
+ }
162
+
163
+ targetImg . addEventListener ( 'load' , ( ) => {
164
+ resetSelection ( ) ;
165
+ } ) ;
166
+
167
+ window . addEventListener ( 'mousedown' , event => {
168
+ const target = event . target . closest ( '.target' ) ;
169
+
170
+ if ( ! target ) {
171
+ return ;
172
+ }
173
+
174
+ const initialRect = targetImg . getBoundingClientRect ( ) ;
175
+ const x = event . x - initialRect . left ;
176
+ const y = event . y - initialRect . top ;
177
+
178
+ dragger = draggers [ 2 ] ;
179
+ mousedown = true ;
180
+ x0 = x ;
181
+ y0 = y ;
182
+ x1 = x0 ;
183
+ y1 = y0 ;
184
+
185
+ updateSelected ( ) ;
186
+ event . preventDefault ( ) ;
187
+ } ) ;
188
+
189
+ window . addEventListener ( 'mouseup' , event => {
190
+ if ( ! mousedown ) {
191
+ return ;
192
+ }
193
+
194
+ mousedown = false ;
195
+ notifySelected ( ) ;
196
+ } ) ;
197
+
198
+
199
+ window . addEventListener ( 'keydown' , event => {
200
+ if ( ! mousedown ) {
201
+ return ;
202
+ }
203
+
204
+ updateCoordinates ( event . shiftKey ) ;
205
+ } ) ;
206
+
207
+ window . addEventListener ( 'keyup' , event => {
208
+ if ( ! mousedown ) {
209
+ return ;
210
+ }
211
+
212
+ updateCoordinates ( event . shiftKey ) ;
213
+ } ) ;
214
+
215
+ window . addEventListener ( 'mousemove' , event => {
216
+ if ( ! mousedown ) {
217
+ return ;
218
+ }
219
+
220
+ lastX = event . x ;
221
+ lastY = event . y ;
222
+
223
+ updateCoordinates ( event . shiftKey ) ;
224
+ } ) ;
225
+
226
+ setTimeout ( ( ) => {
227
+ resetSelection ( ) ;
228
+ } ) ;
0 commit comments