• 百万表格难题


    百万表格难题

    之前很火 stackoverflow 上相关问题

    
    大概思路:  
    
    		不直接显示100W行数据,因为用户可见的数据是有限的(一个可视区不可能同时显示100w行)
    		
    		所以,我们利用这个有限的可视区高度以及提前设定好的 一个可视区显示多少行数据,来进行优化dom。
    		
    	假设: 当前用户可视的部分称为 当前页。   *每页显示10条数据 那么总共 10W页
    			下一部分可视的部分称为  下一页
    			前一部分可视的部分称为  前一页  
    	
    		用户不可见的部分 我们直接移除DOM,只缓存一定数量的DOM (当前页 以及 前一页 后一页)
    		
    	用户交互状态:
    			
    			鼠标滑动浏览
    			
    			点击上下箭头浏览
    			
    			直接点击滚动条浏览。  
    		
    		
    	利用js监控以上状态,分别计算比例即可得知用户 浏览那一页。 然后程序显示当前页 并生成前一页后一页	
    		
    		
    

    JS部分 (别人的解决方案 main.js

    /**
     * Created by hebo on 14-1-3.
     */
    /*
     Visual representation of the approach:
    
     --------
     --------
     --------
     --------
     --------
     --------
     --------
     --------
     --------
     --------
     --------
     --------
     --------
     --------
     --------
    
     ==================================================
    
     [=] - real scrollable height (h)
     [-] - "pages";  total height of all (n) pages is (th) = (ph) * (n)
    
     The overlap between pages is (cj) and is the distance the scrollbar
     will jump when we adjust the scroll position during page switch.
    
     To keep things smooth, we need to minimize both (n) and (cj).
     Setting (ph) at 1/100 of (h) is a good start.
     */
    
    var th = 1000000000;            // virtual height               th = 1,000,000,000                      总内容高()               十亿
    var h =  1000000;               // real scrollable height       h = 1,000,000                           滚动区域高(#content)     一百万
    var ph = h / 100;               // page height                  ph = 1,000,000 / 100 = 10,000           页高                      一万
    var n = Math.ceil(th / ph);     // number of pages              n = 1,000,000,000 / 10,000 = 100,000    页数                      十万
    var vp = 600;                   // viewport height
    var rh = 50;                    // row height                   一页能容纳 10,000 / 50 = 200 行
    
    // 未重叠部分:(h - ph) / (n - 1)
    // 重叠部分: ph - (h - ph) / (n - 1) = (ph * n - ph - h + ph) / (n - 1) = (th - h) / (n - 1)
    var cj = (th - h) / (n - 1);    // "jumpiness" coefficient      cj = (1,000,000,000 - 1,000,000) / (100,000 - 1) = 9990.099901    页间重叠区域高度
    
    var page = 0;                   // current page                 当前页码
    var offset=0;                   // current page offset          当前页的位移
    var prevScrollTop = 0;
    
    var rows = {};                  // cached row nodes
    
    var viewport, content;
    
    
    $(function() {
    	viewport = $("#viewport");
    	content = $("#content");
    
    	viewport.css("height",vp);
    	content.css("height",h);
    
    	viewport.scroll(onScroll);
    	viewport.trigger("scroll");
    });
    
    function onScroll() {
    	var scrollTop = viewport.scrollTop();
    
    	// prevScrollTop 在 onNearScroll() 和 onJump() 中会的得到更新
    	if (Math.abs(scrollTop-prevScrollTop) > vp) {
    		// 使用鼠标拖动滑块才会触发
    		console.log('to onJump()');
    		onJump();
    	}
    	else {
    		// 使用鼠标滚轮或者点击滚动条的箭头按钮会触发
    		console.log('to onNearScroll()');
    		onNearScroll();
    	}
    
    	renderViewport();
    
    	logDebugInfo();
    }
    
    function onNearScroll() {
    	var scrollTop = viewport.scrollTop();
    
    	// console.log('next page: %f, %f', (page + 1)*ph - offset, ph);
    	console.log('next page: scrollTop: %f, ph: %f, ph + (ph - cj) * page: %f', scrollTop, ph, ph + (ph - cj) * page);
    
    	// next page
    	// 滚动多余一页
    	if (scrollTop + offset > (page+1)*ph) {
    		page++;
    		offset = Math.round(page * cj);
    		viewport.scrollTop(prevScrollTop = scrollTop - cj);
    		removeAllRows();
    	}
    	// prev page
    	// 滚动多余一页
    	else if (scrollTop + offset < page*ph) {
    		page--;
    		offset = Math.round(page * cj);
    		viewport.scrollTop(prevScrollTop = scrollTop + cj);
    		removeAllRows();
    	}
    	else {
    		prevScrollTop = scrollTop;
    	}
    }
    
    function onJump() {
    	var scrollTop = viewport.scrollTop();
    	page = Math.floor(scrollTop * ((th-vp) / (h-vp)) * (1/ph));
    	offset = Math.round(page * cj);
    	prevScrollTop = scrollTop;
    
    	removeAllRows();
    }
    
    function removeAllRows() {
    	for (var i in rows) {
    		rows[i].remove();
    		delete rows[i];
    	}
    }
    
    // 渲染的核心是要取得scrollTop 和 offset
    function renderViewport() {
    	// calculate the viewport + buffer
    	var y = viewport.scrollTop() + offset,
    		buffer = vp,
    	// top 和 bottom 是 一组数据中本组最开头一条的行数,和下一组第一条的行数
    	// 默认取三屏,一屏的数据数量可以填满一个 viweport(html元素),向前向后各多取一屏
    		top = Math.floor((y-buffer)/rh),
    		bottom = Math.ceil((y+vp+buffer)/rh);
    
    	top = Math.max(0,top);
    	bottom = Math.min(th/rh, bottom);
    
    	// remove rows no longer in the viewport
    	for (var i in rows) {
    		// if (i < top || i > bottom) {  // 这里做了修改
    		if (i < top || i >= bottom) {
    			rows[i].remove();
    			delete rows[i];
    		}
    	}
    
    	// add new rows
    	// for (var i=top; i<=bottom; i++) {  // 这里做了修改,实际上不可能有第th/rh行,因为只有[0, th/rh - 1]
    	for (var i=top; i<bottom; i++) {
    		if (!rows[i])
    			rows[i] = renderRow(i);
    	}
    }
    
    function renderRow(row) {
    	return $("<div class='row' />")
    		.css({
    			top: row*rh - offset,
    			height: rh
    		})
    		.text("row " + (row+1))
    		.appendTo(content);
    }
    
    function logDebugInfo() {
    	var dbg = $("#debug");
    	dbg.empty();
    	dbg.append("n = " + n + "<br>");
    	dbg.append("ph = " + ph + "<br>");
    	dbg.append("cj = " + cj + "<br>");
    	dbg.append("<hr>");
    	dbg.append("page = " + page + "<br>");
    	dbg.append("offset = " + offset + "<br>");
    	dbg.append("virtual y = " + (prevScrollTop + offset) + "<br>");
    	dbg.append("real y = " + prevScrollTop + "<br>");
    	dbg.append("rows in the DOM = " + $(".row").length + "<br>");
    }
    
    

    html部分

    <!DOCTYPE html>
    <html>
    <head>
        <title>millions of rows</title>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
        <script src="jquery-1.4.2.js"></script>
        <script src="main.js"></script>
        <link rel="stylesheet" href="index.css"/>
    </head>
    <body>
    <div id="viewport">
        <div id="content"></div>
    </div>
    <div id="debug"></div>
        <ul>
            <li>n --  页数 </li>
            <li>ph --  页高 </li>
            <li>cj --  页间重叠区域高度 </li>
            <li>page --  当前页码 </li>
            <li>offset --  当前页的位移 </li>
        </ul>
    </body>
    </html>
    
    Now or nerver .
  • 相关阅读:
    YYHSOI模拟赛题解(T6围栏问题)
    取水
    Spring.Net实现跨数据库服务层事务管理
    使用node.js + jsonserver + mock.js 搭建本地开发mock数据服务
    [转]SQL SERVER整理索引碎片测试
    asp.net mvc 安全测试漏洞 " HTTP 动词篡改的认证旁路" 问题解决
    JavaScript中子类调用父类方法的实现
    asp.net mvc 安全测试漏洞 "跨站点请求伪造" 问题解决
    C#学习记录3下——类的封装,继承,多态
    C#学习记录8——XAML
  • 原文地址:https://www.cnblogs.com/iyueyao/p/3892851.html
Copyright © 2020-2023  润新知