add initial version of non-linear virtual scroll

very-old
Vitaliy Filippov 2016-07-09 22:42:24 +03:00
commit 3e578801ca
1 changed files with 182 additions and 0 deletions

182
test.htm Normal file
View File

@ -0,0 +1,182 @@
<div style="position: relative">
<div id="scroll" style="position: relative; overflow-y: scroll; width: 300px; height: 300px; border: 1px solid gray;" onscroll="scrollother()">
<div style="height: 1500px" id="content"></div>
</div>
<div tabindex="1" style="z-index: 1; position: absolute; top: 0; width: 283px; height: 300px; overflow: hidden;">
<div id="realcontent" style="z-index: 1; width: 100%; position: absolute"></div>
</div>
</div>
<button onclick="scrolltoend()">end</button>
<style>
.item { border: 1px solid gray; box-sizing: border-box; text-align: center; }
</style>
<script>
/*<!--*/
//http://stackoverflow.com/questions/11730596/non-linear-scrolling
var s = document.getElementById('scroll');
var c = document.getElementById('content');
var rc = document.getElementById('realcontent');
var items = [];
var maxdiff = 0, minheight = 30;
for (var i = 0; i < 100; i++)
items.push(30+Math.round(Math.random()*120));
items = [31,109,107,97,140,133,49,69,123,43,137,128,31,101,120,148,119,30,31,37,43,119,105,106,91,148,36,85,76,97,148,31,53,126,138,51,85,65,53,78,39,49,133,49,103,146,143,148,115,49,55,98,44,84,57,127,98,98,104,116,150,82,123,62,134,109,89,71,36,50,146,146,53,35,86,143,86,138,39,81,72,106,88,117,127,97,127,147,102,42,124,99,32,51,84,45,106,139,140,69];
var totalItems = items.length;
c.style.height = (items.length*minheight)+'px';
rc.parentNode.style.width = c.offsetWidth+'px';
window.addEventListener('resize', function() {
rc.parentNode.style.width = c.offsetWidth+'px';
});
var curFirst = 0, curCount = 0, curPos = 0, curVisibleHeight = 0, initLast = 0;
rc.parentNode.addEventListener('keydown', handlekey);
rc.parentNode.addEventListener('DOMMouseScroll', mouse_wheel);
rc.parentNode.onmousewheel = mouse_wheel;
rc.parentNode.focus();
function mouse_wheel(event)
{
event = event||window.event;
var direction = ((event.wheelDelta) ? event.wheelDelta/120 : event.detail/-3) || false;
if (direction)
{
if (event.preventDefault)
event.preventDefault();
event.returnValue = false;
scrollbypx(-direction * 30);
}
}
function handlekey(ev)
{
if (ev.keyCode == 38 || ev.keyCode == 40 || ev.keyCode == 33 || ev.keyCode == 34) // up, down, pgup, pgdown
{
var px;
if (ev.keyCode == 38) px = -30;
else if (ev.keyCode == 40) px = 30;
else if (ev.keyCode == 33) px = -270;
else if (ev.keyCode == 34) px = 270;
scrollbypx(px);
}
else if (ev.keyCode == 36)
s.scrollTop = 0;
else if (ev.keyCode == 35)
s.scrollTop = s.scrollHeight-s.offsetHeight+1;
}
function scrolltoend()
{
s.scrollTop += 1000;
}
var tm;
function scrollasync()
{
if (!tm)
tm = setTimeout(scrollother, 200);
}
var skip = 0;
// скроллинг в пикселях (в пределах видимости - можно использовать для up/down, pgup/pgdown, колеса мыши)
function scrollbypx(px)
{
var newLast = initLast + s.scrollTop / (s.scrollHeight-s.offsetHeight+1) * (totalItems-initLast);
if (newLast > totalItems)
newLast = totalItems;
var nl = Math.ceil(newLast)-1;
var no = newLast-nl;
if (px < 0)
{
// считаем от последнего
px += no*items[nl];
while (nl > curFirst && px < 0)
{
nl--;
px += items[nl];
}
no = (px < 0 ? 0 : px)/items[nl];
newLast = nl+no;
if (newLast < initLast)
{
newLast = initLast;
nl = Math.ceil(newLast)-1;
no = newLast-nl;
}
}
else
{
// считаем от первого
px -= (items[curFirst]-curOffset);
var nf = curFirst;
while (nf < totalItems-1 && px > 0)
{
nf++;
px -= items[nf];
}
nfo = (px > 0 ? items[nf] : px+items[nf]);
for (var h = -nfo, i = nf; i < totalItems && h <= s.offsetHeight; i++)
h += items[i];
if (h <= s.offsetHeight)
newLast = totalItems;
else
newLast = i-(h-s.offsetHeight)/items[i-1];
}
//skip++;
s.scrollTop = (newLast-initLast)/(totalItems-initLast)*(s.scrollHeight-s.offsetHeight+1);
renderitems();
}
function renderitems()
{
var ih = '', h = 0;
for (var i = 0; i < curCount; i++)
{
ih += '<div class="item" style="height: '+items[curFirst+i]+'px">'+(curFirst+i)+'</div>';
h += items[curFirst+i];
}
rc.innerHTML = ih;
rc.style.top = (-curOffset)+'px';
rc.style.height = h+'px';
}
function calcBack(nl, no)
{
var i, h = no*items[nl];
for (i = nl-1; i >= 0 && h < s.offsetHeight; i--)
h += items[i];
curFirst = i+1;
curOffset = h-s.offsetHeight;
curCount = nl-i;
}
// нелинейный скроллинг (в % от элементов)
function scrollother()
{
if (skip)
return skip--;
tm = null;
var i, h;
if (!curCount)
{
h = 0;
for (i = 0; i < totalItems && h < s.offsetHeight; i++)
h += items[i];
initLast = i-(h-s.offsetHeight)/items[i-1];
}
var newLast = initLast + s.scrollTop / (s.scrollHeight-s.offsetHeight+1) * (totalItems-initLast);
if (newLast > totalItems)
newLast = totalItems;
var nl = Math.ceil(newLast)-1;
var no = newLast-nl;
calcBack(nl, no);
renderitems();
}
scrollother();
//-->
</script>