Вот страничка для тестов, кроссбраузерно, чистый JS
Поля ввода содержат общее время анимации и шаг анимации в миллисекундах. Вместо ссылок для наглядности кнопки.
Это вариант без привязки к текущему времени, с привязкой писать лень.
С привязкой к текущему времени общее время анимации в медленных браузерах не увеличится, но будут рывки.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
<!DOCTYPE html>
<html>
<head>
<style type='text/css'>
* { margin:0; padding:0; }
html,body { width:100%; height:100%; }
#container { position:relative; margin:0 auto; width:1000px; min-height:100%; background:#c8c8c8; }
input { float:left; margin:10px 10px; padding:3px; width:60px; }
#layer { position:absolute; padding:10px; top:100px; left:100px; width:500px; height:200px; overflow:scroll; background:#ffffff; }
#layer div { width:1000px; height:500px; }
</style>
<script type='text/javascript'>
var myI=null; // Для setInterval, глобальная. Можно сделать локальной, но тогда могут "накапливаться" несколько анимаций.
function fscroll(s) {
var h, w, oh, ow, st, sl, step, p=fById('layer'), i1=parseInt(fById('i1').value,10)||0, i2=parseInt(fById('i2').value,10)||0;
if (i1<i2) { alert('Время анимации меньше чем шаг'); return; } if(myI) clearInterval(myI); myI=null;
oh=((p.offsetHeight>p.scrollHeight) ? p.offsetHeight : p.scrollHeight); // внутренний размер Layer по высоте
ow=((p.offsetWidth>p.scrollWidth) ? p.offsetWidth: p.scrollWidth); // внутренний размер Layer по ширине
st=p.scrollTop; sl=p.scrollLeft; // величина скролла Layer по высоте и ширине
if (!window.getComputedStyle) {
h=parseInt(p.currentStyle['height'],10) || 0; w=parseInt(p.currentStyle['width'],10) || 0; // ширина и высота Layer
} else {
h=parseInt(document.defaultView.getComputedStyle(p, "").getPropertyValue('height'),10) || 0; // высота Layer
w=parseInt(document.defaultView.getComputedStyle(p, "").getPropertyValue('width'),10) || 0; // ширина Layer
}
s=s.toLowerCase(); step=i1/i2; // Коэффициент для расчёта величины скролла при каждом шаге анимации
// alert(st+'\n'+sl+'\n'+oh+'\n'+ow+'\n'+h+'\n'+w);
// if (!h)h=200; if(!w)w=500;
if (s=='top' && st>0) {
step=Math.round(st/step); myI=setInterval(function() { var t=p.scrollTop; t-=step; if(t<=0){t=0; clearInterval(myI); myI=null;} p.scrollTop=t; }, i2);
}
if (s=='left' && sl>0) {
step=Math.round(sl/step); myI=setInterval(function() { var t=p.scrollLeft; t-=step; if(t<=0){t=0; clearInterval(myI); myI=null;} p.scrollLeft=t; }, i2);
}
if (s=='bottom' && st<(oh-h)) {
step=Math.round((oh-st)/step); myI=setInterval(function() { var t=p.scrollTop; t+=step; if(t>=(oh-h)){t=oh-h; clearInterval(myI); myI=null;} p.scrollTop=t; }, i2);
}
if (s=='right' && sl<(ow-w)) {
step=Math.round((ow-sl)/step); myI=setInterval(function() { var t=p.scrollLeft; t+=step; if(t>=(ow-w)){t=ow-w; clearInterval(myI); myI=null;} p.scrollLeft=t; }, i2);
}
}
function fById(o) { return typeof o == 'string' ? document.getElementById(o) : o; }
</script>
</head>
<body>
<div id="container">
<input type="text" id="i1" value="500" maxlength="3" />
<input type="text" id="i2" value="20" maxlength="3" />
<input type="button" value="Top" onclick="fscroll('top')" />
<input type="button" value="Bottom" onclick="fscroll('bottom')" />
<input type="button" value="left" onclick="fscroll('left')" />
<input type="button" value="right" onclick="fscroll('right')" />
<div id="layer">
<div>text text text<br>text text text<br><br><br>text text text<br>text text text<br><br><br>text text text text text text text text text text text text text text text</div>
</div>
</div>
</body>
</html>