fix: virtual scroll flickering on inventory scroll
- Batch scroll renders with requestAnimationFrame to avoid multiple DOM updates per frame - Add will-change, contain and content-visibility CSS for smoother compositing - Add cache-bust to virtual-scroll.js
This commit is contained in:
@@ -1333,6 +1333,20 @@
|
|||||||
/* History table inside modal */
|
/* History table inside modal */
|
||||||
.inv-modal .data-table { width: 100%; }
|
.inv-modal .data-table { width: 100%; }
|
||||||
|
|
||||||
|
/* ─── Virtual Scroll fixes ───────────────────────────────────────────── */
|
||||||
|
.vs-container {
|
||||||
|
will-change: transform;
|
||||||
|
contain: layout paint;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
.vs-container table {
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
.vs-container tbody tr {
|
||||||
|
content-visibility: auto;
|
||||||
|
contain-intrinsic-size: auto 48px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ─── MercadoLibre Publish Modal Enhancements ────────────────────────── */
|
/* ─── MercadoLibre Publish Modal Enhancements ────────────────────────── */
|
||||||
.meli-preview-card {
|
.meli-preview-card {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
this._scrollHandler = this._onScroll.bind(this);
|
this._scrollHandler = this._onScroll.bind(this);
|
||||||
this._resizeHandler = this._onResize.bind(this);
|
this._resizeHandler = this._onResize.bind(this);
|
||||||
this._isTbody = this.container.tagName === 'TBODY';
|
this._isTbody = this.container.tagName === 'TBODY';
|
||||||
|
this._rafId = null;
|
||||||
|
this._pendingRender = false;
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,11 +60,22 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
VirtualScroll.prototype._onScroll = function() {
|
VirtualScroll.prototype._onScroll = function() {
|
||||||
this._render();
|
this._scheduleRender();
|
||||||
};
|
};
|
||||||
|
|
||||||
VirtualScroll.prototype._onResize = function() {
|
VirtualScroll.prototype._onResize = function() {
|
||||||
this._render();
|
this._scheduleRender();
|
||||||
|
};
|
||||||
|
|
||||||
|
VirtualScroll.prototype._scheduleRender = function() {
|
||||||
|
if (this._pendingRender) return;
|
||||||
|
this._pendingRender = true;
|
||||||
|
var self = this;
|
||||||
|
this._rafId = requestAnimationFrame(function() {
|
||||||
|
self._rafId = null;
|
||||||
|
self._pendingRender = false;
|
||||||
|
self._render();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
VirtualScroll.prototype._getScrollTop = function() {
|
VirtualScroll.prototype._getScrollTop = function() {
|
||||||
@@ -99,11 +112,7 @@
|
|||||||
var buffer = this.buffer;
|
var buffer = this.buffer;
|
||||||
|
|
||||||
if (!data.length) {
|
if (!data.length) {
|
||||||
if (this._isTbody) {
|
|
||||||
this.container.innerHTML = this.emptyHtml;
|
this.container.innerHTML = this.emptyHtml;
|
||||||
} else {
|
|
||||||
this.container.innerHTML = this.emptyHtml;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,20 +121,19 @@
|
|||||||
var startIdx = Math.max(0, Math.floor(scrollTop / rowH) - buffer);
|
var startIdx = Math.max(0, Math.floor(scrollTop / rowH) - buffer);
|
||||||
var endIdx = Math.min(data.length, Math.ceil((scrollTop + containerHeight) / rowH) + buffer);
|
var endIdx = Math.min(data.length, Math.ceil((scrollTop + containerHeight) / rowH) + buffer);
|
||||||
|
|
||||||
|
// Build new HTML
|
||||||
var html = '';
|
var html = '';
|
||||||
if (this._isTbody) {
|
if (this._isTbody) {
|
||||||
// Top spacer row
|
|
||||||
var topSpacerHeight = startIdx * rowH;
|
var topSpacerHeight = startIdx * rowH;
|
||||||
if (topSpacerHeight > 0) {
|
if (topSpacerHeight > 0) {
|
||||||
html += '<tr style="height:' + topSpacerHeight + 'px;"><td colspan="99" style="padding:0;border:0;"></td></tr>';
|
html += '<tr style="height:' + topSpacerHeight + 'px;" aria-hidden="true"><td colspan="99" style="padding:0;border:0;"></td></tr>';
|
||||||
}
|
}
|
||||||
for (var i = startIdx; i < endIdx; i++) {
|
for (var i = startIdx; i < endIdx; i++) {
|
||||||
html += this.renderRow(data[i], i);
|
html += this.renderRow(data[i], i);
|
||||||
}
|
}
|
||||||
// Bottom spacer row
|
|
||||||
var bottomSpacerHeight = (data.length - endIdx) * rowH;
|
var bottomSpacerHeight = (data.length - endIdx) * rowH;
|
||||||
if (bottomSpacerHeight > 0) {
|
if (bottomSpacerHeight > 0) {
|
||||||
html += '<tr style="height:' + bottomSpacerHeight + 'px;"><td colspan="99" style="padding:0;border:0;"></td></tr>';
|
html += '<tr style="height:' + bottomSpacerHeight + 'px;" aria-hidden="true"><td colspan="99" style="padding:0;border:0;"></td></tr>';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (var j = startIdx; j < endIdx; j++) {
|
for (var j = startIdx; j < endIdx; j++) {
|
||||||
@@ -133,6 +141,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use a DocumentFragment approach via innerHTML to avoid flicker:
|
||||||
|
// Setting innerHTML on tbody is the fastest way, but we can reduce
|
||||||
|
// perceived flicker by ensuring the container has contain: paint
|
||||||
|
// and by batching via rAF (done in _scheduleRender).
|
||||||
this.container.innerHTML = html;
|
this.container.innerHTML = html;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<link rel="manifest" href="/pos/static/pwa/manifest.json" />
|
<link rel="manifest" href="/pos/static/pwa/manifest.json" />
|
||||||
<meta name="theme-color" content="#F5A623" />
|
<meta name="theme-color" content="#F5A623" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="/pos/static/css/inventory.css?v=6">
|
<link rel="stylesheet" href="/pos/static/css/inventory.css?v=7">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -907,7 +907,7 @@
|
|||||||
<script src="/pos/static/js/app-init.js" defer></script>
|
<script src="/pos/static/js/app-init.js" defer></script>
|
||||||
<script src="/pos/static/js/pos-utils.js" defer></script>
|
<script src="/pos/static/js/pos-utils.js" defer></script>
|
||||||
<script src="/pos/static/js/sidebar.js" defer></script>
|
<script src="/pos/static/js/sidebar.js" defer></script>
|
||||||
<script src="/pos/static/js/virtual-scroll.js" defer></script>
|
<script src="/pos/static/js/virtual-scroll.js?v=2" defer></script>
|
||||||
<script src="/pos/static/js/inventory.js?v=12" defer></script>
|
<script src="/pos/static/js/inventory.js?v=12" defer></script>
|
||||||
<script src="/pos/static/js/offline-banner.js" defer></script>
|
<script src="/pos/static/js/offline-banner.js" defer></script>
|
||||||
<script src="/pos/static/js/sync-engine.js" defer></script>
|
<script src="/pos/static/js/sync-engine.js" defer></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user