From f416bfecc61075d3dff69821b2d4503d6e1c4037 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Tue, 29 Nov 2022 02:37:28 +0100 Subject: fix(frontend): reduce Android rendering errors --- subprojects/frontend/src/editor/EditorTheme.ts | 12 +++- .../frontend/src/editor/scrollbarViewPlugin.ts | 74 ++++++++++++---------- 2 files changed, 52 insertions(+), 34 deletions(-) (limited to 'subprojects/frontend/src/editor') diff --git a/subprojects/frontend/src/editor/EditorTheme.ts b/subprojects/frontend/src/editor/EditorTheme.ts index c3cbffc8..89bc8932 100644 --- a/subprojects/frontend/src/editor/EditorTheme.ts +++ b/subprojects/frontend/src/editor/EditorTheme.ts @@ -52,6 +52,8 @@ export default styled('div', { }, }; + const scrollerThumbOpacity = theme.palette.mode === 'dark' ? 0.16 : 0.28; + const generalStyle: CSSObject = { background: theme.palette.background.default, '&, .cm-editor': { @@ -70,6 +72,7 @@ export default styled('div', { zIndex: 300, width: 1, marginRight: -1, + pointerEvents: 'none', }, '.cm-scroller': { color: theme.palette.text.secondary, @@ -84,12 +87,17 @@ export default styled('div', { '.cm-scroller-thumb': { position: 'absolute', background: theme.palette.text.secondary, - opacity: theme.palette.mode === 'dark' ? 0.16 : 0.28, + opacity: scrollerThumbOpacity, transition: theme.transitions.create('opacity', { duration: theme.transitions.duration.shortest, }), + touchAction: 'none', + WebkitTapHighlightColor: 'transparent', '&:hover': { opacity: 0.75, + '@media (hover: none)': { + opacity: scrollerThumbOpacity, + }, }, '&.active': { opacity: 1, @@ -445,6 +453,7 @@ export default styled('div', { background: theme.palette.text.primary, border: 'none', cursor: 'pointer', + WebkitTapHighlightColor: 'transparent', [theme.breakpoints.down('sm')]: { margin: '2px 0', }, @@ -471,6 +480,7 @@ export default styled('div', { background: 'transparent', border: 'none', cursor: 'pointer', + WebkitTapHighlightColor: 'transparent', // Use an inner `span` element to match the height of other text highlights. span: { color: theme.palette.text.secondary, diff --git a/subprojects/frontend/src/editor/scrollbarViewPlugin.ts b/subprojects/frontend/src/editor/scrollbarViewPlugin.ts index 9ee70441..c1eb2bbd 100644 --- a/subprojects/frontend/src/editor/scrollbarViewPlugin.ts +++ b/subprojects/frontend/src/editor/scrollbarViewPlugin.ts @@ -22,6 +22,43 @@ export const SCROLLBAR_WIDTH = 12; export const ANNOTATION_WIDTH = SCROLLBAR_WIDTH / 2; export const MIN_ANNOTATION_HEIGHT = 1; +function handleDrag( + element: HTMLElement, + callback: (movementX: number, movementY: number) => void, +) { + let pointerId: number | undefined; + element.addEventListener('pointerdown', (event) => { + if (pointerId === undefined) { + ({ pointerId } = event); + element.setPointerCapture(pointerId); + element.classList.add(THUMB_ACTIVE_CLASS); + } else { + event.preventDefault(); + // Avoid implicit pointer capture, see https://w3c.github.io/pointerevents/#dfn-implicit-pointer-capture + element.releasePointerCapture(event.pointerId); + } + }); + + element.addEventListener('pointermove', (event) => { + if (event.pointerId !== pointerId) { + return; + } + callback(event.movementX, event.movementY); + event.preventDefault(); + }); + + function scrollEnd(event: PointerEvent) { + if (event.pointerId !== pointerId) { + return; + } + pointerId = undefined; + element.classList.remove(THUMB_ACTIVE_CLASS); + } + + element.addEventListener('pointerup', scrollEnd, { passive: true }); + element.addEventListener('pointercancel', scrollEnd, { passive: true }); +} + export default function scrollbarViewPlugin( editorStore: EditorStore, ): ViewPlugin { @@ -63,44 +100,15 @@ export default function scrollbarViewPlugin( const thumbY = ownerDocument.createElement('div'); thumbY.className = `${THUMB_CLASS} ${THUMB_Y_CLASS}`; - const scrollY = (event: MouseEvent) => { - scrollDOM.scrollBy({ top: event.movementY / factorY }); - event.preventDefault(); - }; - const stopScrollY = () => { - thumbY.classList.remove(THUMB_ACTIVE_CLASS); - window.removeEventListener('mousemove', scrollY); - window.removeEventListener('mouseup', stopScrollY); - }; - thumbY.addEventListener( - 'mousedown', - () => { - thumbY.classList.add(THUMB_ACTIVE_CLASS); - window.addEventListener('mousemove', scrollY); - window.addEventListener('mouseup', stopScrollY, { passive: true }); - }, - { passive: true }, + handleDrag(thumbY, (_movementX, movementY) => + scrollDOM.scrollBy({ top: movementY / factorY }), ); holder.appendChild(thumbY); const thumbX = ownerDocument.createElement('div'); thumbX.className = `${THUMB_CLASS} ${THUMB_X_CLASS}`; - const scrollX = (event: MouseEvent) => { - scrollDOM.scrollBy({ left: event.movementX / factorX }); - }; - const stopScrollX = () => { - thumbX.classList.remove(THUMB_ACTIVE_CLASS); - window.removeEventListener('mousemove', scrollX); - window.removeEventListener('mouseup', stopScrollX); - }; - thumbX.addEventListener( - 'mousedown', - () => { - thumbX.classList.add(THUMB_ACTIVE_CLASS); - window.addEventListener('mousemove', scrollX); - window.addEventListener('mouseup', stopScrollX, { passive: true }); - }, - { passive: true }, + handleDrag(thumbX, (movementX) => + scrollDOM.scrollBy({ left: movementX / factorX }), ); holder.appendChild(thumbX); -- cgit v1.2.3-54-g00ecf