<html> <head> <meta charset="UTF-8"> <title>test</title> <style type="text/css"> .viewpoint { margin: 20px auto; width: 380px; height: 301px; border: 1px solid #333; overflow-y:auto; } .canvas { position: relative; height: auto; } .row { position: absolute; height: 10px; line-height: 10px; } </style> <script src="jquery.js"></script> </head> <body> <div class="viewpoint"> <div class="canvas"></div> </div> <div class="log"></div> <script> var lineHeight = 10; var viewpointHeight = $('.viewpoint').height(); var data = new Array(909).fill(0).map((d, i) => ({ v: i })); var limit = Math.min(Math.ceil(viewpointHeight/ lineHeight) - 1, data.length - 1); $('.canvas').height(lineHeight * data.length); var buffer = { start: 0, end: limit, limit: limit, total: data.length, domain:[0, limit], isAmong(index) { return buffer.start <= index && index <= buffer.end; }, preLoad(dir, current) { if (dir === 0) return false; var start = buffer.start; var end = buffer.end; var cacheTwice = 3; // scroll up if (dir < 0 && start === 0) return false; if (dir < 0 && current < Math.max(0, start + buffer.limit)) { if (buffer.isAmong(current)) { end = start - 1; start = Math.max(0, end - buffer.limit); } else { end = current + buffer.limit; start = Math.max(0, current - 2 * buffer.limit); // cacheTwice = 2; } buffer.domain = [start, end]; buffer.start = start; buffer.end = Math.min(start + cacheTwice * buffer.limit, buffer.end); return true; } // scroll down if (dir > 0 && end === buffer.total) return false; if (dir > 0 && current > end - buffer.limit) { if (buffer.isAmong(current)) { start = end + 1; end = Math.min(buffer.total, start + buffer.limit); } else { start = current - buffer.limit; end = Math.min(buffer.total, current + 2 * buffer.limit); // cacheTwice = 2; } buffer.domain = [start, end]; buffer.end = end; buffer.start = Math.max(buffer.start, end - cacheTwice * buffer.limit); return true; } return false; } }; var scroller = { dir: 0, // -1,0,-1 preIndex: 0, index: 0, on(fn) { this.fn = function(offsetTop) { log('current', offsetTop); scroller.dir = offsetTop - scroller.preIndex; scroller.index = ~~((scroller.preIndex = offsetTop)/ lineHeight); if (buffer.preLoad(scroller.dir, scroller.index)) { fn( scroller.dir > 0 ? 1 : -1, buffer.domain, buffer.start, buffer.end, scroller.index, buffer.total ); } }; }, fire() {this.fn.apply(this, arguments);} }; var dataView = { nodeList: [], init() { var domain = buffer.domain; dataView.render(1, domain); }, fullNodes() { return dataView.nodeList.length >= 3 * limit; }, getNodes(dir, first, last) { if (dataView.fullNodes()) { let selected; if (dir > 0) { selected = dataView.nodeList.slice(0, last - first + 1); dataView.nodeList = dataView.nodeList.slice(last - first + 1).concat(selected); } else { selected = dataView.nodeList.slice(first - last - 1); dataView.nodeList = selected.concat(dataView.nodeList.slice(0, first - last - 1)); } return selected; } var nodes = []; for (let i = first; i <= last; i++) { nodes.push(function() { var $node = $('<div/>').addClass('row'); return { src: $node, set(row, top) { return $node.css('top', top * lineHeight).text(row.v); } }; }()); } if (dir > 0) { dataView.nodeList = dataView.nodeList.concat(nodes); } else { dataView.nodeList = nodes.concat(dataView.nodeList); } return nodes; }, render(dir, [first, last]) { var nodes = dataView.getNodes(dir, first, last); console.log(nodes.length); if (dataView.isFullNodes) { data.slice(first, last + 1).forEach((row, i) => { nodes[i].set(row, first + i); }); return; } var parent = $('<div/>'); data.slice(first, last + 1).forEach((row, i) => { parent.append(nodes[i].set(row, first + i)); }); $('.canvas').append(parent.children()); if (dataView.fullNodes()) { dataView.isFullNodes = true; } } }; document.querySelector('.viewpoint').addEventListener('scroll', function(evt) { // console.log(this.scrollTop); scroller.fire(this.scrollTop); }); scroller.on(function(dir, domain, start, end, offsetTop, total) { console.log(`${dir}, add:[${domain}] [${start}-${end}], ${offsetTop}, nodes:${dataView.nodeList.length}`); dataView.render(dir, domain); }); dataView.init(); var $log = document.querySelector('.log'); function log(...args) { $log.innerHTML = args; } </script> </body> </html>
1 var lockColumnManager = { 2 data: [], 3 totalOffset: 0, 4 add(col) { 5 this.data.unshift(col); 6 this.reCalc(); 7 }, 8 remove(delCol) { 9 this.data = this.data.filter(col => col !== delCol); 10 this.reCalc(); 11 }, 12 reCalc(){ 13 this.totalOffset = this.data.reduce((offset, col) => { 14 offset += col.w; 15 col.offsetLeft = offset; 16 return offset; 17 }, 0); 18 } 19 };