@@ -17,6 +17,7 @@ export const UfsGlobal = {
1717 closest,
1818 addLoadingAnimation,
1919 addLoadingAnimationAtPos,
20+ addEventListener,
2021 enableDragAndZoom,
2122 getContentClientRect,
2223 dataURLToCanvas,
@@ -46,6 +47,7 @@ export const UfsGlobal = {
4647 getWatchingVideoSrc,
4748 } ,
4849 Utils : {
50+ lerp,
4951 getNumberFormatter,
5052 deepClone,
5153 debounce,
@@ -276,11 +278,20 @@ function addLoadingAnimation(
276278 if ( element ) element . classList . remove ( "ufs-loading-" + id ) ;
277279 } ;
278280}
281+ function addEventListener ( target , event , callback , options ) {
282+ target . addEventListener ( event , callback , options ) ;
283+ return ( ) => target . removeEventListener ( event , callback , options ) ;
284+ }
279285function enableDragAndZoom ( element , container , callback ) {
280286 // set style
281- element . style . cssText += `
287+ const className = "ufs-drag-and-zoom" ;
288+ element . classList . add ( className ) ;
289+
290+ let style = document . createElement ( "style" ) ;
291+ style . textContent = `
292+ .${ className } {
282293 cursor: grab;
283- position: relative;
294+ position: relative !important ;
284295 -webkit-user-select: none !important;
285296 -moz-user-select: none !important;
286297 -ms-user-select: none !important;
@@ -291,85 +302,122 @@ function enableDragAndZoom(element, container, callback) {
291302 min-width: unset !important;
292303 min-height: unset !important;
293304 -webkit-user-drag: none !important;
294- ` ;
305+ }` ;
306+ ( container || element ) . appendChild ( style ) ;
295307
296- // Variables to track the current position
297- let lastX = 0 ;
298- let lastY = 0 ;
308+ // config
299309 let dragging = false ;
300- let mouse = { x : 0 , y : 0 } ;
310+ const lerpSpeed = 0.3 ;
311+ const last = { x : 0 , y : 0 } ;
312+ const mouse = { x : 0 , y : 0 } ;
313+ const animTarget = {
314+ left : parseFloat ( element . style . left ) ,
315+ top : parseFloat ( element . style . top ) ,
316+ width : parseFloat ( element . style . width ) ,
317+ height : parseFloat ( element . style . height ) ,
318+ } ;
319+
320+ let animationId ;
321+ function animate ( ) {
322+ for ( let prop in animTarget ) {
323+ element . style [ prop ] =
324+ lerp ( parseFloat ( element . style [ prop ] ) , animTarget [ prop ] , lerpSpeed ) +
325+ "px" ;
326+ }
327+
328+ animationId = requestAnimationFrame ( animate ) ;
329+ }
330+
331+ animate ( ) ;
301332
302333 // Mouse down event listener
303- ( container || element ) . addEventListener ( "mousedown" , function ( e ) {
334+ let _down = addEventListener ( container || element , "mousedown" , function ( e ) {
304335 e . preventDefault ( ) ;
305336 dragging = true ;
306- lastX = e . clientX ;
307- lastY = e . clientY ;
337+ last . x = e . clientX ;
338+ last . y = e . clientY ;
308339 element . style . cursor = "grabbing" ;
309340 } ) ;
310341
311342 // Mouse move event listener
312- document . addEventListener ( "mousemove" , function ( e ) {
313- mouse = { x : e . clientX , y : e . clientY } ;
343+ let _move = addEventListener ( document , "mousemove" , function ( e ) {
344+ mouse . x = e . clientX ;
345+ mouse . y = e . clientY ;
314346 if ( dragging ) {
315- let x = e . clientX - lastX + element . offsetLeft ;
316- let y = e . clientY - lastY + element . offsetTop ;
347+ let delX = e . clientX - last . x ;
348+ let delY = e . clientY - last . y ;
317349
318- element . style . left = x + "px" ;
319- element . style . top = y + "px" ;
320- callback ?. ( { x , y , type : "move" } ) ;
350+ animTarget . left += delX ;
351+ animTarget . top += delY ;
352+ callback ?. ( { ... animTarget , type : "move" } ) ;
321353
322- lastX = e . clientX ;
323- lastY = e . clientY ;
354+ last . x = e . clientX ;
355+ last . y = e . clientY ;
324356 }
325357 } ) ;
326358
327359 // Mouse up event listener
328- document . addEventListener ( "mouseup" , function ( ) {
360+ let _up = addEventListener ( document , "mouseup" , function ( ) {
329361 dragging = false ;
330362 element . style . cursor = "grab" ;
331363 } ) ;
332364
333365 // Mouse leave event listener
334- document . addEventListener ( "mouseleave" , function ( ) {
366+ let _leave = addEventListener ( document , "mouseleave" , function ( ) {
335367 dragging = false ;
336368 element . style . cursor = "grab" ;
337369 } ) ;
338370
339371 // Mouse wheel event listener for zooming
340- ( container || element ) . addEventListener ( "wheel" , function ( e ) {
372+ let _wheel = addEventListener ( container || element , "wheel" , function ( e ) {
341373 e . preventDefault ( ) ;
342374
343375 let curScale = parseFloat ( element . style . width ) / element . width ;
344376 let delta = - e . wheelDeltaY || - e . wheelDelta ;
345- let factor = Math . abs ( ( 0.1 * delta ) / 120 ) ;
346- let scale = delta > 0 ? curScale * ( 1 - factor ) : curScale * ( 1 + factor ) ;
377+ let factor = Math . abs ( ( 0.3 * delta ) / 120 ) ;
378+ let newScale =
379+ delta > 0 ? curScale * ( 1 - factor ) : curScale * ( 1 + factor ) ;
347380
348- // Adjust scale at mouse position
349- let offsetX = mouse . x - element . offsetLeft ;
350- let offsetY = mouse . y - element . offsetTop ;
381+ let newWidth = element . width * newScale ;
382+ let newHeight = element . height * newScale ;
351383
352- let newWidth = element . width * scale ;
353- let newHeight = element . height * scale ;
354-
355- if ( newWidth < 3 || newHeight < 3 ) {
384+ if ( newWidth < 10 || newHeight < 10 ) {
356385 return ;
357386 }
358387
359- let newLeft =
360- element . offsetLeft -
361- ( newWidth - element . width ) * ( offsetX / element . width ) ;
388+ let left = parseFloat ( element . style . left ) ;
389+ let top = parseFloat ( element . style . top ) ;
390+ let offsetX = mouse . x - left ;
391+ let offsetY = mouse . y - top ;
392+
393+ let newLeft = left - ( newWidth - element . width ) * ( offsetX / element . width ) ;
362394 let newTop =
363- element . offsetTop -
364- ( newHeight - element . height ) * ( offsetY / element . height ) ;
395+ top - ( newHeight - element . height ) * ( offsetY / element . height ) ;
365396
366- element . style . width = newWidth + "px" ;
367- element . style . height = newHeight + "px" ;
368- element . style . left = newLeft + "px" ;
369- element . style . top = newTop + "px" ;
397+ animTarget . left = newLeft ;
398+ animTarget . top = newTop ;
399+ animTarget . width = newWidth ;
400+ animTarget . height = newHeight ;
370401
371402 callback ?. ( { type : "scale" } ) ;
372403 } ) ;
404+
405+ let listeners = [ _down , _move , _up , _leave , _wheel ] ;
406+
407+ return {
408+ animateTo : ( x , y , w , h ) => {
409+ animTarget . left = x ;
410+ animTarget . top = y ;
411+ animTarget . width = w ;
412+ animTarget . height = h ;
413+ } ,
414+ destroy : ( ) => {
415+ cancelAnimationFrame ( animationId ) ;
416+ style . remove ( ) ;
417+ element . classList . remove ( className ) ;
418+ listeners . forEach ( ( l ) => l ?. ( ) ) ;
419+ } ,
420+ } ;
373421}
374422// prettier-ignore
375423function getContentClientRect ( target ) {
@@ -859,6 +907,11 @@ function getWatchingVideoSrc() {
859907// #endregion
860908
861909// #region Utils
910+
911+ function lerp ( from , to , speed ) {
912+ return from + ( to - from ) * speed ;
913+ }
914+
862915const numberFormatCached = { } ;
863916/**
864917 * Get number formatter
0 commit comments