From ce9b10aefef55782f29f23735f7eea65368b41fa Mon Sep 17 00:00:00 2001 From: KDSBrowne Date: Wed, 18 Sep 2024 14:03:52 -0400 Subject: [PATCH] fix: Correct cursor position calculation for touch events (#20748) * improve cursor position on mobile * force ci re-run * force ci re-run * replace addEventListner with removeEventListener on unmount --- .../whiteboard/cursors/component.jsx | 210 +++++++++--------- 1 file changed, 109 insertions(+), 101 deletions(-) diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/cursors/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/cursors/component.jsx index 6433b939b6..2b304f98b5 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/cursors/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/cursors/component.jsx @@ -28,6 +28,13 @@ const TOOL_CURSORS = { grabbing: 'grabbing', moving: 'move', }; + +// Offset native tldraw eraser animation container +const setOverlayPosition = () => { + const overlay = document.getElementsByClassName('tl-overlay')[0]; + if (overlay) overlay.style.left = '0px'; +}; + const Cursors = (props) => { const cursorWrapper = React.useRef(); const [active, setActive] = React.useState(false); @@ -100,122 +107,123 @@ const Cursors = (props) => { const banners = document.querySelectorAll('[data-test="notificationBannerBar"]'); let yOffset = 0; let xOffset = 0; + const calcPresOffset = () => { - yOffset - += (parseFloat(presentationContainer?.style?.height) - - (parseFloat(presentation?.style?.height) - + (currentUser.presenter ? PRES_TOOLBAR_HEIGHT : 0)) - ) / 2; - xOffset - += (parseFloat(presentationContainer?.style?.width) - - parseFloat(presentation?.style?.width) - ) / 2; + const presContainerHeight = parseFloat(presentationContainer?.style?.height); + const presHeight = parseFloat(presentation?.style?.height); + const presToolbarHeight = currentUser.presenter ? PRES_TOOLBAR_HEIGHT : 0; + + yOffset += (presContainerHeight - (presHeight + presToolbarHeight)) / 2; + + const presContainerWidth = parseFloat(presentationContainer?.style?.width); + const presWidth = parseFloat(presentation?.style?.width); + + xOffset += (presContainerWidth - presWidth) / 2; }; - // If the presentation container is the full screen element we don't - // need any offsets + + // If the presentation container is the full screen element we don't need any offsets const { webkitFullscreenElement, fullscreenElement } = document; const fsEl = webkitFullscreenElement || fullscreenElement; + if (fsEl?.getAttribute('data-test') === 'presentationContainer') { calcPresOffset(); - return setPos({ x: x - xOffset, y: y - yOffset }); - } - if (nav) yOffset += parseFloat(nav?.style?.height); - if (panel) xOffset += parseFloat(panel?.style?.width); - if (subPanel) xOffset += parseFloat(subPanel?.style?.width); - - // offset native tldraw eraser animation container - const overlay = document.getElementsByClassName('tl-overlay')[0]; - if (overlay) overlay.style.left = '0px'; - - if (type === 'touchmove') { - calcPresOffset(); - if (!active) { - setActive(true); - } - const newX = event?.changedTouches[0]?.clientX - xOffset; - const newY = event?.changedTouches[0]?.clientY - yOffset; - return setPos({ x: newX, y: newY }); - } - - if (document?.documentElement?.dir === 'rtl') { - xOffset = 0; - if (presentationContainer && presentation) { - calcPresOffset(); - } - if (sl.includes('custom')) { - if (webcams) { - if (camPosition === 'contentTop' || !camPosition) { - yOffset += (parseFloat(webcams?.style?.height || 0) + BOTTOM_CAM_HANDLE_HEIGHT); - } - if (camPosition === 'contentBottom') { - yOffset -= BOTTOM_CAM_HANDLE_HEIGHT; - } - if (camPosition === 'contentRight') { - xOffset += (parseFloat(webcams?.style?.width || 0) + SMALL_OFFSET); - } - } - } - if (sl?.includes('smart')) { - if (panel || subPanel) { - const dockPos = webcams?.getAttribute('data-position'); - if (dockPos === 'contentTop') { - yOffset += (parseFloat(webcams?.style?.height || 0) + SMALL_OFFSET); - } - } - } - if (webcams && sl?.includes('videoFocus')) { - xOffset += parseFloat(nav?.style?.width); - yOffset += (parseFloat(panel?.style?.height || 0) - XL_OFFSET); - } } else { - if (sl.includes('custom')) { - if (webcams) { - if (camPosition === 'contentTop' || !camPosition) { - yOffset += (parseFloat(webcams?.style?.height) || 0) + XS_OFFSET; - } - if (camPosition === 'contentBottom') { - yOffset -= BOTTOM_CAM_HANDLE_HEIGHT; - } - if (camPosition === 'contentLeft') { - xOffset += (parseFloat(webcams?.style?.width) || 0) + SMALL_OFFSET; - } - } - } + const calculateOffsets = () => { + if (nav) yOffset += parseFloat(nav?.style?.height); + if (panel) xOffset += parseFloat(panel?.style?.width); + if (subPanel) xOffset += parseFloat(subPanel?.style?.width); - if (sl.includes('smart')) { - if (panel || subPanel) { - const dockPos = webcams?.getAttribute('data-position'); - if (dockPos === 'contentLeft') { - xOffset += (parseFloat(webcams?.style?.width || 0) + SMALL_OFFSET); + if (document?.documentElement?.dir === 'rtl') { + xOffset = 0; + if (presentationContainer && presentation) { + calcPresOffset(); } - if (dockPos === 'contentTop') { - yOffset += (parseFloat(webcams?.style?.height || 0) + SMALL_OFFSET); + if (sl.includes('custom')) { + if (webcams) { + if (camPosition === 'contentTop' || !camPosition) { + yOffset += (parseFloat(webcams?.style?.height || 0) + BOTTOM_CAM_HANDLE_HEIGHT); + } + if (camPosition === 'contentBottom') { + yOffset -= BOTTOM_CAM_HANDLE_HEIGHT; + } + if (camPosition === 'contentRight') { + xOffset += (parseFloat(webcams?.style?.width || 0) + SMALL_OFFSET); + } + } + } + if (sl?.includes('smart')) { + if (panel || subPanel) { + const dockPos = webcams?.getAttribute('data-position'); + if (dockPos === 'contentTop') { + yOffset += (parseFloat(webcams?.style?.height || 0) + SMALL_OFFSET); + } + } + } + if (webcams && sl?.includes('videoFocus')) { + xOffset += parseFloat(nav?.style?.width); + yOffset += (parseFloat(panel?.style?.height || 0) - XL_OFFSET); + } + } else { + if (sl.includes('custom')) { + if (webcams) { + if (camPosition === 'contentTop' || !camPosition) { + yOffset += (parseFloat(webcams?.style?.height) || 0) + XS_OFFSET; + } + if (camPosition === 'contentBottom') { + yOffset -= BOTTOM_CAM_HANDLE_HEIGHT; + } + if (camPosition === 'contentLeft') { + xOffset += (parseFloat(webcams?.style?.width) || 0) + SMALL_OFFSET; + } + } + } + + if (sl.includes('smart')) { + if (panel || subPanel) { + const dockPos = webcams?.getAttribute('data-position'); + if (dockPos === 'contentLeft') { + xOffset += (parseFloat(webcams?.style?.width || 0) + SMALL_OFFSET); + } + if (dockPos === 'contentTop') { + yOffset += (parseFloat(webcams?.style?.height || 0) + SMALL_OFFSET); + } + } + if (!panel && !subPanel) { + if (webcams) { + xOffset = parseFloat(webcams?.style?.width || 0) + SMALL_OFFSET; + } + } + } + if (sl?.includes('videoFocus')) { + if (webcams) { + xOffset = parseFloat(subPanel?.style?.width); + yOffset = parseFloat(panel?.style?.height); + } + } + if (presentationContainer && presentation) { + calcPresOffset(); } } - if (!panel && !subPanel) { - if (webcams) { - xOffset = parseFloat(webcams?.style?.width || 0) + SMALL_OFFSET; - } + + if (banners) { + banners.forEach((el) => { + yOffset += parseFloat(window.getComputedStyle(el).height); + }); } - } - if (sl?.includes('videoFocus')) { - if (webcams) { - xOffset = parseFloat(subPanel?.style?.width); - yOffset = parseFloat(panel?.style?.height); - } - } - if (presentationContainer && presentation) { - calcPresOffset(); - } + }; + + calculateOffsets(); } - if (banners) { - banners.forEach((el) => { - yOffset += parseFloat(window.getComputedStyle(el).height); - }); + const posX = type === 'touchmove' ? event.changedTouches[0].clientX : x; + const posY = type === 'touchmove' ? event.changedTouches[0].clientY : y; + + if (type === 'touchmove' && !active) { + setActive(true); } - return setPos({ x: event.x - xOffset, y: event.y - yOffset }); + setOverlayPosition(); + return setPos({ x: posX - xOffset, y: posY - yOffset }); }; React.useEffect(() => { @@ -231,7 +239,7 @@ const Cursors = (props) => { return () => { currentCursor?.removeEventListener('mouseenter', start); - currentCursor?.addEventListener('touchstart', start); + currentCursor?.removeEventListener('touchstart', start); currentCursor?.removeEventListener('mouseleave', end); currentCursor?.removeEventListener('mousedown', handleGrabbing); currentCursor?.removeEventListener('mouseup', handleReleaseGrab);