【发布时间】:2016-07-07 20:23:06
【问题描述】:
前段时间我为AngularJS(使用Angular Material)建立了一个树表结构。
我的目标是让它只在大屏幕上工作(1280 或更高),但现在我想更新它,让它在更小的设备(主要是平板电脑)上工作,而不限制数据。由于性能的原因,我想让 HTML 尽可能简单(树表可以有 1000 多行,因此为单行创建更复杂的 HTML 将延长追加和呈现表行所需的时间(行是动态的,所以它不仅仅是关于初始渲染))。
我想出了一个想法,我将保留“固定”部分与包含名称的第一个单元格并滚动包含所有指标并将同步滚动的第二部分。
当前单行 HTML:
<div class="tb-header layout-row">
<div class="tb-title"><span>Name</span></div>
<div class="tb-metrics">
<div class="layout-row">
<div class="tb-cell flex-10">812</div>
<div class="tb-cell flex-7">621</div>
<div class="tb-cell flex-4">76.5</div>
<div class="tb-cell flex-7">289</div>
<div class="tb-cell flex-4">46.5</div>
<div class="tb-cell flex-7">308</div>
<div class="tb-cell flex-4">49.6</div>
<div class="tb-cell flex-7">390</div>
<div class="tb-cell flex-4">48.0</div>
<div class="tb-cell flex-7">190</div>
<div class="tb-cell flex-4">23.4</div>
<div class="tb-cell flex-7">0</div>
<div class="tb-cell flex-4">0.0</div>
<div class="tb-cell flex-8">6.4</div>
<div class="tb-cell flex-8">0.0</div>
<div class="tb-cell flex-8"></div>
</div>
</div>
我的想法是在父容器上使用touchmove 事件(包装整个树并绑定为指令)并检查touchmove 何时从指标部分开始,然后计算我应该移动指标的值。那部分工作正常。
当我想在.tb-metrics > 上应用偏移量时,问题就开始了。
我的第一次尝试是使用 jQuery:
function moveMetrics( offset ) {
var ofx = offset < 0 ? (offset < -getMetricsScrollWidth() ? -getMetricsScrollWidth() : offset) : 0;
$('.tb-metrics').children().css('transform', 'translateX(' + ofx + 'px)');
/*...*/
}
不幸的是,当表包含更多行时,此解决方案非常慢(我无法缓存行,因为它们是动态的)。
在我的第二次尝试中,a 尝试尽可能多地避免 DOM 操作。
为此,我决定将<script> 标签添加到包含适用于.metrics > .layout-row 的css 的dom。
解决方案:
function moveMetrics( offset ) {
var ofx = offset < 0 ? (offset < -getMetricsScrollWidth() ? -getMetricsScrollWidth() : offset) : 0
, style = $( '#tbMetricsVirtualScroll' )
;
if ( !style.length ) {
body.append( '<style id="tbMetricsVirtualScroll">.tb-metrics > * {transform: translateX(' + ofx + 'px)}</style>' );
style = $( '#tbMetricsVirtualScroll' );
} else {
style.text( '.tb-metrics > * {transform: translateX(' + ofx + 'px)}' );
}
/*...*/
}
但是,当表包含大量行时,它似乎并没有快多少。所以这不是 DOM 操作,而是渲染/绘制视图似乎是这里的瓶颈。
我尝试创建某种虚拟滚动,但由于不同数据集的树结构不同,并且可以具有“无限”个级别(每行可以包含新 ng-repeat 中的子行),这真的很难任务。
如果我能在不使用虚拟滚动条的情况下提高性能,我将不胜感激。
编辑:
Chrome时间线截图显示,大部分滚动时间都被渲染消耗了(我猜是因为复杂的DOM结构)
编辑 2:
我不会说我实现了绝对平滑的滚动,但我发现了一些可以显着提高性能的地方(其中一些并不明显,经过这么小的改动,结果比我预期的要好)。
- 简化类选择器:
.tb-header > .tb-metrics > .tb-cell比.tb-specific-cell慢很多(似乎解析更复杂的选择器需要更多时间?) - 从转换后的元素中移除不透明度和框阴影
- 尝试将转换后的元素分配到新层(使用 css
will-change和/或translateZ(0))
【问题讨论】:
-
你应该使用响应式的材料设计,并且会自动转换你的大表格
-
@koningdavid 我试图实现它,但问题是,我正在使用循环指令,所以每一行都可以有它自己的子行(和另一个 ng-repeat)所以它使它更多复杂的
-
在移动设备上平滑滚动的关键部分之一是滚动整个文档,而不是使用
overflow: auto滚动某个不同的容器。很难为不同的容器实现完全平滑的滚动。它适用于你的情况吗?是滚动整个文档还是一个不同的容器? -
如果您有大量数据,您可能希望显示大约 10 行,但它们纯粹是占位符...当用户滚动时...您确定他们理论上会拥有什么滚动到并放入该数据...到您的占位符中,仅将大量数据保留在内存中...并且仅在需要时呈现可见元素。
标签: javascript jquery html css angularjs