mirror of
https://github.com/danog/telegram-tt.git
synced 2024-12-11 08:39:59 +01:00
[Perf] Fix heavy animation event for fastSmoothScroll
, refactoring
This commit is contained in:
parent
7c275da3f8
commit
7c28bfac70
@ -6,7 +6,7 @@ const ANIMATION_END_EVENT = 'tt-event-heavy-animation-end';
|
|||||||
let timeout: number | undefined;
|
let timeout: number | undefined;
|
||||||
let isAnimating = false;
|
let isAnimating = false;
|
||||||
|
|
||||||
export const dispatchHeavyAnimationEvent = (duration: number) => {
|
export const dispatchHeavyAnimationEvent = (duration?: number) => {
|
||||||
if (!isAnimating) {
|
if (!isAnimating) {
|
||||||
isAnimating = true;
|
isAnimating = true;
|
||||||
document.dispatchEvent(new Event(ANIMATION_START_EVENT));
|
document.dispatchEvent(new Event(ANIMATION_START_EVENT));
|
||||||
@ -17,11 +17,18 @@ export const dispatchHeavyAnimationEvent = (duration: number) => {
|
|||||||
timeout = undefined;
|
timeout = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout = window.setTimeout(() => {
|
if (duration) {
|
||||||
|
timeout = window.setTimeout(() => {
|
||||||
|
isAnimating = false;
|
||||||
|
document.dispatchEvent(new Event(ANIMATION_END_EVENT));
|
||||||
|
timeout = undefined;
|
||||||
|
}, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
isAnimating = false;
|
isAnimating = false;
|
||||||
document.dispatchEvent(new Event(ANIMATION_END_EVENT));
|
document.dispatchEvent(new Event(ANIMATION_END_EVENT));
|
||||||
timeout = undefined;
|
};
|
||||||
}, duration);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default (
|
export default (
|
||||||
|
@ -8,7 +8,6 @@ import {
|
|||||||
FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE,
|
FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE,
|
||||||
} from '../config';
|
} from '../config';
|
||||||
import { dispatchHeavyAnimationEvent } from '../hooks/useHeavyAnimationCheck';
|
import { dispatchHeavyAnimationEvent } from '../hooks/useHeavyAnimationCheck';
|
||||||
import { fastRaf } from './schedulers';
|
|
||||||
import { animateSingle } from './animation';
|
import { animateSingle } from './animation';
|
||||||
|
|
||||||
let isAnimating = false;
|
let isAnimating = false;
|
||||||
@ -37,92 +36,104 @@ export default function fastSmoothScroll(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { offsetTop } = element;
|
const scrollFrom = calculateScrollFrom(container, element, maxDistance, forceDirection);
|
||||||
|
|
||||||
if (forceDirection === undefined) {
|
|
||||||
const offset = offsetTop - container.scrollTop;
|
|
||||||
|
|
||||||
if (offset < -maxDistance) {
|
|
||||||
container.scrollTop += (offset + maxDistance);
|
|
||||||
} else if (offset > maxDistance) {
|
|
||||||
container.scrollTop += (offset - maxDistance);
|
|
||||||
}
|
|
||||||
} else if (forceDirection === FocusDirection.Up) {
|
|
||||||
container.scrollTop = offsetTop + maxDistance;
|
|
||||||
} else if (forceDirection === FocusDirection.Down) {
|
|
||||||
container.scrollTop = Math.max(0, offsetTop - maxDistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getGlobal().settings.byKey.animationLevel === ANIMATION_LEVEL_MIN) {
|
if (getGlobal().settings.byKey.animationLevel === ANIMATION_LEVEL_MIN) {
|
||||||
forceDuration = 0;
|
forceDuration = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
isAnimating = true;
|
scrollWithJs(container, element, scrollFrom, position, margin, forceDuration, forceCurrentContainerHeight);
|
||||||
fastRaf(() => {
|
|
||||||
scrollWithJs(container, element, position, margin, forceDuration, forceCurrentContainerHeight);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isAnimatingScroll() {
|
export function isAnimatingScroll() {
|
||||||
return isAnimating;
|
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(
|
function scrollWithJs(
|
||||||
container: HTMLElement,
|
container: HTMLElement,
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
|
scrollFrom: number,
|
||||||
position: ScrollLogicalPosition | 'centerOrTop',
|
position: ScrollLogicalPosition | 'centerOrTop',
|
||||||
margin = 0,
|
margin = 0,
|
||||||
forceDuration?: number,
|
forceDuration?: number,
|
||||||
forceCurrentContainerHeight?: boolean,
|
forceCurrentContainerHeight?: boolean,
|
||||||
) {
|
) {
|
||||||
const { offsetTop: elementTop, offsetHeight: elementHeight } = element;
|
const { offsetTop: elementTop, offsetHeight: elementHeight } = element;
|
||||||
const { scrollTop, offsetHeight: containerHeight, scrollHeight } = container;
|
const { scrollTop: currentScrollTop, offsetHeight: containerHeight, scrollHeight } = container;
|
||||||
const targetContainerHeight = !forceCurrentContainerHeight && container.dataset.normalHeight
|
const targetContainerHeight = !forceCurrentContainerHeight && container.dataset.normalHeight
|
||||||
? Number(container.dataset.normalHeight)
|
? Number(container.dataset.normalHeight)
|
||||||
: containerHeight;
|
: containerHeight;
|
||||||
|
|
||||||
|
if (currentScrollTop !== scrollFrom) {
|
||||||
|
container.scrollTop = scrollFrom;
|
||||||
|
}
|
||||||
|
|
||||||
let path!: number;
|
let path!: number;
|
||||||
|
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case 'start':
|
case 'start':
|
||||||
path = (elementTop - margin) - scrollTop;
|
path = (elementTop - margin) - scrollFrom;
|
||||||
break;
|
break;
|
||||||
case 'end':
|
case 'end':
|
||||||
path = (elementTop + elementHeight + margin) - (scrollTop + targetContainerHeight);
|
path = (elementTop + elementHeight + margin) - (scrollFrom + targetContainerHeight);
|
||||||
break;
|
break;
|
||||||
// 'nearest' is not supported yet
|
// 'nearest' is not supported yet
|
||||||
case 'nearest':
|
case 'nearest':
|
||||||
case 'center':
|
case 'center':
|
||||||
case 'centerOrTop':
|
case 'centerOrTop':
|
||||||
path = elementHeight < targetContainerHeight
|
path = elementHeight < targetContainerHeight
|
||||||
? (elementTop + elementHeight / 2) - (scrollTop + targetContainerHeight / 2)
|
? (elementTop + elementHeight / 2) - (scrollFrom + targetContainerHeight / 2)
|
||||||
: (elementTop - margin) - scrollTop;
|
: (elementTop - margin) - scrollFrom;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path < 0) {
|
if (path < 0) {
|
||||||
const remainingPath = -scrollTop;
|
const remainingPath = -scrollFrom;
|
||||||
path = Math.max(path, remainingPath);
|
path = Math.max(path, remainingPath);
|
||||||
} else if (path > 0) {
|
} else if (path > 0) {
|
||||||
const remainingPath = scrollHeight - (scrollTop + targetContainerHeight);
|
const remainingPath = scrollHeight - (scrollFrom + targetContainerHeight);
|
||||||
path = Math.min(path, remainingPath);
|
path = Math.min(path, remainingPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path === 0) {
|
if (path === 0) {
|
||||||
isAnimating = false;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const target = container.scrollTop + path;
|
const target = scrollFrom + path;
|
||||||
|
|
||||||
if (forceDuration === 0) {
|
if (forceDuration === 0) {
|
||||||
container.scrollTop = target;
|
container.scrollTop = target;
|
||||||
isAnimating = false;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isAnimating = true;
|
||||||
|
|
||||||
const absPath = Math.abs(path);
|
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 || (
|
const duration = forceDuration || (
|
||||||
@ -130,16 +141,20 @@ function scrollWithJs(
|
|||||||
+ (absPath / FAST_SMOOTH_MAX_DISTANCE) * (FAST_SMOOTH_MAX_DURATION - FAST_SMOOTH_MIN_DURATION)
|
+ (absPath / FAST_SMOOTH_MAX_DISTANCE) * (FAST_SMOOTH_MAX_DURATION - FAST_SMOOTH_MIN_DURATION)
|
||||||
);
|
);
|
||||||
const startAt = Date.now();
|
const startAt = Date.now();
|
||||||
|
const onHeavyAnimationStop = dispatchHeavyAnimationEvent();
|
||||||
|
|
||||||
dispatchHeavyAnimationEvent(duration);
|
|
||||||
animateSingle(() => {
|
animateSingle(() => {
|
||||||
const t = Math.min((Date.now() - startAt) / duration, 1);
|
const t = Math.min((Date.now() - startAt) / duration, 1);
|
||||||
|
|
||||||
const currentPath = path * (1 - transition(t));
|
const currentPath = path * (1 - transition(t));
|
||||||
|
|
||||||
container.scrollTop = Math.round(target - currentPath);
|
container.scrollTop = Math.round(target - currentPath);
|
||||||
|
|
||||||
isAnimating = t < 1;
|
isAnimating = t < 1;
|
||||||
|
|
||||||
|
if (!isAnimating) {
|
||||||
|
onHeavyAnimationStop();
|
||||||
|
}
|
||||||
|
|
||||||
return isAnimating;
|
return isAnimating;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { getGlobal } from '../lib/teact/teactn';
|
import { getGlobal } from '../lib/teact/teactn';
|
||||||
|
|
||||||
import { fastRaf } from './schedulers';
|
|
||||||
import { animate } from './animation';
|
|
||||||
import { IS_IOS } from './environment';
|
|
||||||
import { ANIMATION_LEVEL_MIN } from '../config';
|
import { ANIMATION_LEVEL_MIN } from '../config';
|
||||||
|
import { IS_IOS } from './environment';
|
||||||
|
import { animate } from './animation';
|
||||||
|
|
||||||
const DEFAULT_DURATION = 300;
|
const DEFAULT_DURATION = 300;
|
||||||
|
|
||||||
@ -19,9 +18,7 @@ export default function fastSmoothScrollHorizontal(container: HTMLElement, left:
|
|||||||
...(duration && { behavior: 'smooth' }),
|
...(duration && { behavior: 'smooth' }),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
fastRaf(() => {
|
scrollWithJs(container, left, duration);
|
||||||
scrollWithJs(container, left, duration);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,10 +38,10 @@ function scrollWithJs(container: HTMLElement, left: number, duration: number) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const target = container.scrollLeft + path;
|
const target = scrollLeft + path;
|
||||||
|
|
||||||
if (duration === 0) {
|
if (duration === 0) {
|
||||||
container.scrollTop = target;
|
container.scrollLeft = target;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user