add initial version of non-linear virtual scroll
commit
3e578801ca
|
@ -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>
|
Loading…
Reference in New Issue