From 2688314c8411c60899947814e62d496eb72958da Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Sun, 23 Apr 2023 18:32:53 +0400 Subject: [PATCH] Fix `fastSmoothScroll` path function --- .../middle/message/hooks/useFocusMessage.ts | 10 +- src/config.ts | 2 +- src/util/fastSmoothScroll.ts | 110 ++++++++---------- 3 files changed, 55 insertions(+), 67 deletions(-) diff --git a/src/components/middle/message/hooks/useFocusMessage.ts b/src/components/middle/message/hooks/useFocusMessage.ts index ce2b25a24..76bff9cba 100644 --- a/src/components/middle/message/hooks/useFocusMessage.ts +++ b/src/components/middle/message/hooks/useFocusMessage.ts @@ -4,7 +4,8 @@ import { useLayoutEffect, useMemo } from '../../../../lib/teact/teact'; import fastSmoothScroll from '../../../../util/fastSmoothScroll'; // This is used when the viewport was replaced. -const RELOCATED_FOCUS_OFFSET = 1000; +const BOTTOM_FOCUS_OFFSET = 500; +const RELOCATED_FOCUS_OFFSET = 750; const FOCUS_MARGIN = 20; export default function useFocusMessage( @@ -29,14 +30,15 @@ export default function useFocusMessage( useLayoutEffect(() => { if (isFocused && elementRef.current) { const messagesContainer = elementRef.current.closest('.MessageList')!; + // `noFocusHighlight` is always called with “scroll-to-bottom” buttons + const isToBottom = noFocusHighlight; fastSmoothScroll( messagesContainer, elementRef.current, - // `noFocusHighlight` always called from “scroll-to-bottom” buttons - noFocusHighlight ? 'end' : 'centerOrTop', + isToBottom ? 'end' : 'centerOrTop', FOCUS_MARGIN, - focusDirection !== undefined ? RELOCATED_FOCUS_OFFSET : undefined, + focusDirection !== undefined ? (isToBottom ? BOTTOM_FOCUS_OFFSET : RELOCATED_FOCUS_OFFSET) : undefined, focusDirection, undefined, isResizingContainer, diff --git a/src/config.ts b/src/config.ts index 7a914f805..9110b12c6 100644 --- a/src/config.ts +++ b/src/config.ts @@ -146,7 +146,7 @@ export const ANIMATION_END_DELAY = 100; export const FAST_SMOOTH_MAX_DISTANCE = 1500; export const FAST_SMOOTH_MIN_DURATION = 250; export const FAST_SMOOTH_MAX_DURATION = 600; -export const FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE = 500; // px +export const FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE = 750; // px // Average duration of message sending animation export const API_UPDATE_THROTTLE = Math.round((FAST_SMOOTH_MIN_DURATION + FAST_SMOOTH_MAX_DURATION) / 2); diff --git a/src/util/fastSmoothScroll.ts b/src/util/fastSmoothScroll.ts index 59744169c..b1395baeb 100644 --- a/src/util/fastSmoothScroll.ts +++ b/src/util/fastSmoothScroll.ts @@ -24,88 +24,45 @@ export default function fastSmoothScroll( forceDuration?: number, forceNormalContainerHeight?: boolean, ) { - const scrollFrom = calculateScrollFrom(container, element, maxDistance, forceDirection); - - if (forceDirection === FocusDirection.Static) { - scrollWithJs(container, element, scrollFrom, position, margin, 0); - return; - } - - if (getGlobal().settings.byKey.animationLevel === ANIMATION_LEVEL_MIN) { + if ( + forceDirection === FocusDirection.Static + || getGlobal().settings.byKey.animationLevel === ANIMATION_LEVEL_MIN + ) { forceDuration = 0; } - scrollWithJs(container, element, scrollFrom, position, margin, forceDuration, forceNormalContainerHeight); -} - -export function isAnimatingScroll() { - return isAnimating; -} - -function calculateScrollFrom( - container: HTMLElement, - element: HTMLElement, - maxDistance = FAST_SMOOTH_MAX_DISTANCE, - forceDirection?: FocusDirection, -) { - const { offsetTop: elementTop } = element; - const { scrollTop } = container; - - if (forceDirection === undefined) { - const offset = elementTop - container.scrollTop; - - if (offset < -maxDistance) { - return scrollTop + (offset + maxDistance); - } else if (offset > maxDistance) { - return scrollTop + (offset - maxDistance); - } - } else if (forceDirection === FocusDirection.Up) { - return elementTop + maxDistance; - } else if (forceDirection === FocusDirection.Down) { - return Math.max(0, elementTop - maxDistance); - } - - return scrollTop; -} - -function scrollWithJs( - container: HTMLElement, - element: HTMLElement, - scrollFrom: number, - position: ScrollLogicalPosition | 'centerOrTop', - margin = 0, - forceDuration?: number, - forceNormalContainerHeight?: boolean, -) { const { offsetTop: elementTop, offsetHeight: elementHeight } = element; const { scrollTop: currentScrollTop, offsetHeight: containerHeight, scrollHeight } = container; const targetContainerHeight = forceNormalContainerHeight && container.dataset.normalHeight ? Number(container.dataset.normalHeight) : containerHeight; - if (currentScrollTop !== scrollFrom) { - container.scrollTop = scrollFrom; - } - - let path!: number; - + let scrollTo!: number; switch (position) { case 'start': - path = (elementTop - margin) - scrollFrom + (IS_ANDROID ? 1 : 0); + scrollTo = (elementTop - margin) + (IS_ANDROID ? 1 : 0); break; case 'end': - path = (elementTop + elementHeight + margin) - (scrollFrom + targetContainerHeight); + scrollTo = (elementTop + elementHeight + margin) - targetContainerHeight; break; // 'nearest' is not supported yet case 'nearest': case 'center': case 'centerOrTop': - path = elementHeight < targetContainerHeight - ? (elementTop + elementHeight / 2) - (scrollFrom + targetContainerHeight / 2) - : (elementTop - margin) - scrollFrom; + scrollTo = elementHeight < targetContainerHeight + ? (elementTop + elementHeight / 2 - targetContainerHeight / 2) + : (elementTop - margin); break; } + const scrollFrom = calculateScrollFrom(container, scrollTo, maxDistance, forceDirection); + + if (currentScrollTop !== scrollFrom) { + container.scrollTop = scrollFrom; + } + + let path = scrollTo - scrollFrom; + if (path < 0) { const remainingPath = -scrollFrom; path = Math.max(path, remainingPath); @@ -128,7 +85,7 @@ function scrollWithJs( isAnimating = true; const absPath = Math.abs(path); - const transition = absPath < FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE ? shortTransition : longTransition; + const transition = absPath <= FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE ? shortTransition : longTransition; const duration = forceDuration || ( FAST_SMOOTH_MIN_DURATION + (absPath / FAST_SMOOTH_MAX_DISTANCE) * (FAST_SMOOTH_MAX_DURATION - FAST_SMOOTH_MIN_DURATION) @@ -154,6 +111,35 @@ function scrollWithJs( }); } +export function isAnimatingScroll() { + return isAnimating; +} + +function calculateScrollFrom( + container: HTMLElement, + scrollTo: number, + maxDistance = FAST_SMOOTH_MAX_DISTANCE, + forceDirection?: FocusDirection, +) { + const { scrollTop } = container; + + if (forceDirection === undefined) { + const offset = scrollTo - scrollTop; + + if (offset < -maxDistance) { + return scrollTop + (offset + maxDistance); + } else if (offset > maxDistance) { + return scrollTop + (offset - maxDistance); + } + } else if (forceDirection === FocusDirection.Up) { + return scrollTo + maxDistance; + } else if (forceDirection === FocusDirection.Down) { + return Math.max(0, scrollTo - maxDistance); + } + + return scrollTop; +} + function longTransition(t: number) { return 1 - ((1 - t) ** 5); }