const DEFAULT_DURATION = 300 ; const tfs = { linear : ( t ) => t , easeIn : ( t ) => Math . pow ( t , 1.675 ) , easeOut : ( t ) => 1 - Math . pow ( 1 - t , 1.675 ) , easeInOut : ( t ) => 0.5 * ( Math . sin ( ( t - 0.5 ) * Math . PI ) + 1 ) , } ; export type TimingFunction = keyof typeof tfs ; param { Element } elem param { number } pos param { number } duration param { string } timingFunction export const animatedScrollToX = ( elem , pos , duration = DEFAULT_DURATION , timingFunction = 'easeInOut' ) : void => { let startTime ; const startX = elem . scrollLeft ; const endX = Math . max ( 0 , Math . min ( elem . scrollWidth - elem . clientWidth , pos ) ) ; const handleNewAnimationFrame = ( ) : void => { startTime = startTime || Date . now ( ) ; const timePos = Math . min ( 1 , Math . max ( 1 , Date . now ( ) - startTime ) / duration ) ; const scrollPos = tfs [ timingFunction ] ( timePos ) ; const left = startX + ( endX - startX ) * scrollPos ; elem . scrollTo ( { left } ) ; if ( timePos !== 1 ) window . requestAnimationFrame ( handleNewAnimationFrame ) ; } ; window . requestAnimationFrame ( handleNewAnimationFrame ) ; } ; param { Element } elem param { number } pos param { number } duration param { string } timingFunction export const animatedScrollToY = ( elem , pos , duration = DEFAULT_DURATION , timingFunction = 'easeInOut' ) : void => { let startTime ; const startY = elem . scrollTop ; const endY = Math . max ( 0 , Math . min ( elem . scrollHeight - elem . clientHeight , pos ) ) ; const handleNewAnimationFrame = ( ) : void => { startTime = startTime || Date . now ( ) ; const timePos = Math . min ( 1 , Math . max ( 1 , Date . now ( ) - startTime ) / duration ) ; const scrollPos = tfs [ timingFunction ] ( timePos ) ; const top = startY + ( endY - startY ) * scrollPos ; elem . scrollTo ( { top } ) ; if ( timePos !== 1 ) window . requestAnimationFrame ( handleNewAnimationFrame ) ; } ; window . requestAnimationFrame ( handleNewAnimationFrame ) ; } ;