replace getBoundingClientRect() with offset/scrollTop & clientHeight

as they are an order of magnitude faster in most browsers,
getBoundingClientRect() tends to cause relayout.
This commit is contained in:
Bruno Windels 2019-02-21 19:28:55 +01:00
parent ecb074862e
commit 03784e586c

View file

@ -576,9 +576,10 @@ module.exports = React.createClass({
} }
const scrollNode = this._getScrollNode(); const scrollNode = this._getScrollNode();
const wrapperRect = ReactDOM.findDOMNode(this).getBoundingClientRect(); const scrollBottom = scrollNode.scrollTop + scrollNode.clientHeight;
const boundingRect = node.getBoundingClientRect();
const scrollDelta = boundingRect.bottom + pixelOffset - wrapperRect.bottom; const nodeBottom = node.offsetTop + node.clientHeight;
const scrollDelta = nodeBottom + pixelOffset - scrollBottom;
debuglog("ScrollPanel: scrolling to token '" + scrollToken + "'+" + debuglog("ScrollPanel: scrolling to token '" + scrollToken + "'+" +
pixelOffset + " (delta: "+scrollDelta+")"); pixelOffset + " (delta: "+scrollDelta+")");
@ -595,37 +596,43 @@ module.exports = React.createClass({
return; return;
} }
const scrollNode = this._getScrollNode();
const scrollBottom = scrollNode.scrollTop + scrollNode.clientHeight;
const itemlist = this.refs.itemlist; const itemlist = this.refs.itemlist;
const wrapperRect = ReactDOM.findDOMNode(this).getBoundingClientRect();
const messages = itemlist.children; const messages = itemlist.children;
let newScrollState = null; let node = null;
let nodeBottom;
// TODO: change this to not use getBoundingClientRect and save the domnode
for (let i = messages.length-1; i >= 0; --i) { for (let i = messages.length-1; i >= 0; --i) {
const node = messages[i]; if (!messages[i].dataset.scrollTokens) {
if (!node.dataset.scrollTokens) continue; continue;
}
node = messages[i];
const boundingRect = node.getBoundingClientRect(); nodeBottom = node.offsetTop + node.clientHeight;
newScrollState = {
stuckAtBottom: false,
trackedScrollToken: node.dataset.scrollTokens.split(',')[0],
pixelOffset: wrapperRect.bottom - boundingRect.bottom,
};
// If the bottom of the panel intersects the ClientRect of node, use this node // If the bottom of the panel intersects the ClientRect of node, use this node
// as the scrollToken. // as the scrollToken.
// If this is false for the entire for-loop, we default to the last node // If this is false for the entire for-loop, we default to the last node
// (which is why newScrollState is set on every iteration). // (which is why newScrollState is set on every iteration).
if (boundingRect.top < wrapperRect.bottom) { if (nodeBottom >= scrollBottom) {
// Use this node as the scrollToken // Use this node as the scrollToken
break; break;
} }
} }
// This is only false if there were no nodes with `node.dataset.scrollTokens` set.
if (newScrollState) { if (!node) {
this.scrollState = newScrollState;
debuglog("ScrollPanel: saved scroll state", this.scrollState);
} else {
debuglog("ScrollPanel: unable to save scroll state: found no children in the viewport"); debuglog("ScrollPanel: unable to save scroll state: found no children in the viewport");
return;
} }
debuglog("ScrollPanel: saved scroll state", this.scrollState);
this.scrollState = {
stuckAtBottom: false,
trackedScrollToken: node.dataset.scrollTokens.split(',')[0],
pixelOffset: scrollBottom - nodeBottom,
};
}, },
_restoreSavedScrollState: function() { _restoreSavedScrollState: function() {