С помощью requestAnimationFrame.
Например(из фреймворка quasar):
function getScrollPosition (scrollTarget) {
if (scrollTarget === window) {
return window.pageYOffset || window.scrollY || document.body.scrollTop || 0
}
return scrollTarget.scrollTop
}
function setScroll (scrollTarget, offset) {
if (scrollTarget === window) {
window.scrollTo(0, offset)
return
}
scrollTarget.scrollTop = offset
}
function animScrollTo (el, to, duration) {
const pos = getScrollPosition(el)
if (duration <= 0) {
if (pos !== to) {
setScroll(el, to)
}
return
}
requestAnimationFrame(() => {
const newPos = pos + (to - pos) / Math.max(16, duration) * 16
setScroll(el, newPos)
if (newPos !== to) {
animScrollTo(el, to, duration - 16)
}
})
}
animScrollTo(<элемент-контейнер>, <позиция>, <время на анимацию>)
Позицию якоря вычисляйте сами.)
Ну либо можно поискать готовый пакет в npm, который с гарантией там есть.