source: gs3-extensions/tabletop-dl/trunk/drag-with-inertia.js@ 37456

Last change on this file since 37456 was 37456, checked in by davidb, 14 months ago

Initial cut at dragging a div around with inertia/momentum after letting go

File size: 8.2 KB
Line 
1// For details about event bubbling, see:
2// https://javascript.info/bubbling-and-capturing
3
4
5// If thinking about adding touch-pad gesutring, see:
6// https://kenneth.io/post/detecting-multi-touch-trackpad-gestures-in-javascript
7
8let DragDEBUG = true;
9
10var activeDrag = {
11
12 // The element to drag
13 draggableElement: null,
14
15 // Define the starting position of the element
16 startX: 0,
17 startY: 0,
18
19 // Define the current position and velocity of the element
20 currentX: 0,
21 currentY: 0,
22
23 lastX: 0,
24 lastY: 0,
25 animationId: null,
26
27 velocityX: 0,
28 velocityY: 0,
29
30 dragging: false
31};
32
33let docXDim = null;
34let docYDim = null;
35
36
37
38function handleDragStart(event, client_x, client_y)
39{
40 if (DragDEBUG) {
41 console.log(` handleDragStart(${client_x},${client_y})`);
42 }
43
44 activeDrag.currentX = activeDrag.lastX = activeDrag.draggableElement.offsetLeft;
45 activeDrag.currentY = activeDrag.lastY = activeDrag.draggableElement.offsetTop;
46
47 activeDrag.startX = client_x - activeDrag.currentX;
48 activeDrag.startY = client_y - activeDrag.currentY;
49
50 // Stop any existing animation
51 cancelAnimationFrame(activeDrag.animationId);
52
53 activeDrag.dragging = true;
54}
55
56
57function handleTouchStart(event)
58{
59 if (DragDEBUG) {
60 console.log("handleTouchtart()");
61 }
62
63 let client_x = event.touches[0].clientX;
64 let client_y = event.touches[0].clientY;
65
66 handleDragStart(event,client_x,client_y);
67}
68
69
70function handleMouseStart(event)
71{
72 if (DragDEBUG) {
73 console.log("handleMouseStart()");
74 }
75
76 if (event.target == event.currentTarget) {
77 // As we know this is our element, don't want to
78 // allow any other events, such as text selection with
79 // child div elements
80
81 event.preventDefault();
82 }
83
84 let client_x = event.clientX;
85 let client_y = event.clientY;
86
87 handleDragStart(event,client_x,client_y);
88}
89
90
91function handleDrag(event, client_x, client_y)
92{
93 if (DragDEBUG) {
94 console.log(` handleDrag(${client_x},${client_y})`);
95 }
96 //event.preventDefault();
97
98 if (event.target == event.currentTarget) {
99 if (DragDEBUG) {
100 console.log(` + event.target == event.currentTarget`);
101 }
102
103
104 const de_x_org = client_x - activeDrag.startX;
105 const de_y_org = client_y - activeDrag.startY;
106
107 let de_x_dim = $(activeDrag.draggableElement).width();
108 let de_y_dim = $(activeDrag.draggableElement).height();
109
110 let out_of_bounds_x = false;
111 let out_of_bounds_y = false;
112
113 //console.log(` (${de_x_org},${de_y_org})`);
114
115 if ((de_x_org >= 0) && ((de_x_org+de_x_dim <= docXDim))) {
116
117 activeDrag.draggableElement.style.left = `${de_x_org}px`;
118
119 // Update the current position of the element, store previous in LastX for velocity calc
120 activeDrag.lastX = activeDrag.currentX;
121 activeDrag.currentX = de_x_org;
122
123 }
124 else {
125 out_of_bounds_x = true;
126
127 // update startX by how much the overshoot is
128 if (de_x_org<0) {
129 activeDrag.startX += de_x_org;
130 }
131 else {
132 activeDrag.startX += (de_x_org+de_x_dim) - docXDim;
133 }
134
135 }
136
137 if ((de_y_org >= 0) && (de_y_org+de_y_dim <= docYDim)) {
138
139 activeDrag.draggableElement.style.top = `${de_y_org}px`;
140
141 // Update the current position of the element, store previous in LastY for velocity calc
142 activeDrag.lastY = activeDrag.currentY;
143 activeDrag.currentY = de_y_org;
144
145 }
146 else {
147 out_of_bounds_y = true;
148
149 // update startY by how much the overshoot is
150 if (de_y_org<0) {
151 activeDrag.startY += de_y_org;
152 }
153 else {
154 activeDrag.startY += (de_y_org+de_y_dim) - docYDim;
155 }
156 }
157
158 if (!event.clientX) {
159 // Touch event
160 //
161 // prevent additional event handler
162 // this is done to stop chrome's annoying back-page operation
163 // triggering on a long right drag
164
165 event.preventDefault();
166 }
167
168 if (out_of_bounds_x || out_of_bounds_y) {
169 // Hard up against an edge
170
171 /*
172 if (event.clientX) {
173 // Mouse event
174 //
175 // Don't want any child dragging events such as selecting text to trigger, unless
176 // actually over the child element
177
178 event.preventDefault();
179 }*/
180 }
181 }
182 else {
183 if (DragDEBUG) {
184 console.log(` - event.target != event.currentTarget`);
185 }
186 }
187}
188
189function handleTouchDrag(event)
190{
191 const client_x = event.touches[0].clientX;
192 const client_y = event.touches[0].clientY;
193
194 handleDrag(event,client_x,client_y);
195}
196
197function handleMouseDrag(event)
198{
199 if (activeDrag.dragging) {
200 const client_x = event.clientX;
201 const client_y = event.clientY;
202
203 handleDrag(event,client_x,client_y);
204 }
205}
206
207
208function handleDragEnd(event)
209{
210 if (DragDEBUG) {
211 console.log(` handleDragEnd(velocityX = ${activeDrag.currentX} - ${activeDrag.lastX}, velocityY = ${activeDrag.currentY} - ${activeDrag.lastY}`);
212 }
213
214 activeDrag.dragging = false;
215
216 // Calculate the velocity of the element based on the last few frames of movement
217 activeDrag.velocityX = activeDrag.currentX - activeDrag.lastX;
218 activeDrag.velocityY = activeDrag.currentY - activeDrag.lastY;
219
220 // Animate the movement of the element
221 activeDrag.animationId = requestAnimationFrame(animateElement);
222}
223
224// Define the animation function
225function animateElement() {
226 // Update the position of the element based on its current velocity
227 activeDrag.currentX += activeDrag.velocityX;
228 activeDrag.currentY += activeDrag.velocityY;
229
230 let x = activeDrag.currentX;
231 let y = activeDrag.currentY;
232
233 // Update the element's position
234 activeDrag.draggableElement.style.left = `${x}px`;
235 activeDrag.draggableElement.style.top = `${y}px`;
236
237 let de_x_dim = $(activeDrag.draggableElement).width();
238 let de_y_dim = $(activeDrag.draggableElement).height();
239
240
241 if (x < -0.05) {
242 // bounce, taking a small dampening hit in velocity/force first
243 activeDrag.velocityX *= 0.75;
244
245 activeDrag.currentX = 0;
246 activeDrag.velocityX = -1 * activeDrag.velocityX;
247 }
248 else if ((x+de_x_dim) > (docXDim+0.05)) {
249 // bounce, taking a small dampening hit in velocity/force first
250 activeDrag.velocityX *= 0.75;
251
252 activeDrag.currentX = docXDim-de_x_dim;
253 activeDrag.velocityX = -1 * activeDrag.velocityX;
254 }
255
256 if (y < -0.05) {
257 // bounce, taking a small dampening hit in velocity/force first
258 activeDrag.velocityY *= 0.75;
259
260 activeDrag.currentY = 0;
261 activeDrag.velocityY = -1 * activeDrag.velocityY;
262 }
263 else if (((y+de_y_dim) > (docYDim+0.05))) {
264 // bounce, taking a small dampening hit in velocity/force first
265 activeDrag.velocityY *= 0.75;
266
267 activeDrag.currentY = docYDim-de_y_dim;
268 activeDrag.velocityY = -1 * activeDrag.velocityY;
269 }
270
271 // Decelerate the velocity over time
272 activeDrag.velocityX *= 0.95;
273 activeDrag.velocityY *= 0.95;
274
275 // Stop the animation when the velocity is low enough
276 if (Math.abs(activeDrag.velocityX) < 0.1 && Math.abs(activeDrag.velocityY) < 0.1) {
277 cancelAnimationFrame(activeDrag.animationId);
278 } else {
279 activeDrag.animationId = requestAnimationFrame(animateElement);
280 }
281}
282
283function handleTouchDragEnd(event)
284{
285 handleDragEnd(event);
286}
287
288function handleMouseDragEnd(event)
289{
290 if (activeDrag.dragging) {
291 handleDragEnd(event)
292 }
293}
294
295// Add the event listeners
296//let lastX, lastY, animationId;
297//let animationId;
298
299// https://stackoverflow.com/questions/56703458/how-to-make-a-draggable-elements-for-touch-and-mousedrag-events
300
301$(document).ready(function() {
302 //console.log("document ready");
303
304 //docXDim = document.body.clientHeight;
305 //docYDim = document.body.clientWidth;
306
307 docXDim = Math.max($(document).width(), $(window).width() );
308 docYDim = Math.max($(document).height(),$(window).height());
309
310 activeDrag.draggableElement = document.getElementById('myWindow');
311
312 // psudeo drag-start
313 activeDrag.draggableElement.addEventListener('touchstart', handleTouchStart);
314 activeDrag.draggableElement.addEventListener('mousedown', handleMouseStart);
315
316 // psudeo drag
317 activeDrag.draggableElement.addEventListener('touchmove', handleTouchDrag);
318 activeDrag.draggableElement.addEventListener('mousemove', handleMouseDrag)
319
320
321 // psudeo drag-end
322 activeDrag.draggableElement.addEventListener('touchend', handleTouchDragEnd);
323 activeDrag.draggableElement.addEventListener('mouseup', handleMouseDragEnd);
324});
Note: See TracBrowser for help on using the repository browser.