Browse Source

Group redraw performance (#3409)

* Implement group redraw @grimalschi performance enhancement

* Fix JSDoc for redraw

* Remove commented out in itemset

* Remove fasdom

* Clean up queue functions in group redraw

* Fix mistake in comments

* Remove extra read-write from _didResize

* Resolve review comments
revert-3409-performance
Yotam Berkowitz 7 years ago
committed by GitHub
parent
commit
9830616875
2 changed files with 138 additions and 57 deletions
  1. +108
    -51
      lib/timeline/component/Group.js
  2. +30
    -6
      lib/timeline/component/ItemSet.js

+ 108
- 51
lib/timeline/component/Group.js View File

@ -206,19 +206,7 @@ Group.prototype.getLabelWidth = function() {
return this.props.label.width;
};
/**
* Repaint this group
* @param {{start: number, end: number}} range
* @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
* @param {boolean} [forceRestack=false] Force restacking of all items
* @return {boolean} Returns true if the group is resized
*/
Group.prototype.redraw = function(range, margin, forceRestack) {
var resized = false;
// force recalculation of the height of the items when the marker height changed
// (due to the Timeline being attached to the DOM or changed from display:none to visible)
Group.prototype._didMarkerHeightChange = function() {
var markerHeight = this.dom.marker.clientHeight;
if (markerHeight != this.lastMarkerHeight) {
this.lastMarkerHeight = markerHeight;
@ -226,27 +214,22 @@ Group.prototype.redraw = function(range, margin, forceRestack) {
item.dirty = true;
if (item.displayed) item.redraw();
});
return true;
}
}
forceRestack = true;
}
// recalculate the height of the subgroups
this._calculateSubGroupHeights(margin);
// calculate actual size and position
Group.prototype._calculateGroupSizeAndPosition = function() {
var foreground = this.dom.foreground;
this.top = foreground.offsetTop;
this.right = foreground.offsetLeft;
this.width = foreground.offsetWidth;
}
var lastIsVisible = this.isVisible;
this.isVisible = this._isGroupVisible(range, margin);
var restack = forceRestack || this.stackDirty || (this.isVisible && !lastIsVisible);
Group.prototype._redrawItems = function(forceRestack, lastIsVisible, margin, range) {
var restack = forceRestack || this.stackDirty || this.isVisible && !lastIsVisible;
this._updateSubgroupsSizes();
// if restacking, reposition visible items vertically
if(restack) {
// if restacking, reposition visible items vertically
if (restack) {
if (typeof this.itemSet.options.order === 'function') {
// a custom order function
// brute force restack of all items
@ -255,7 +238,7 @@ Group.prototype.redraw = function(range, margin, forceRestack) {
var me = this;
var limitSize = false;
util.forEach(this.items, function (item) {
if (!item.dom) { // If this item has never been displayed then the dom property will not be defined, this means we need to measure the size
if (!item.displayed) {
item.redraw();
me.visibleItems.push(item);
}
@ -268,41 +251,41 @@ Group.prototype.redraw = function(range, margin, forceRestack) {
});
stack.stack(customOrderedItems, margin, true /* restack=true */);
this.visibleItems = this._updateItemsInRange(this.orderedItems, this.visibleItems, range);
}
else {
} else {
// no custom order function, lazy stacking
this.visibleItems = this._updateItemsInRange(this.orderedItems, this.visibleItems, range);
if (this.itemSet.options.stack) { // TODO: ugly way to access options...
stack.stack(this.visibleItems, margin, true /* restack=true */);
}
else { // no stacking
if (this.itemSet.options.stack) {
// TODO: ugly way to access options...
stack.stack(this.visibleItems, margin, true /* restack=true */);
} else {
// no stacking
stack.nostack(this.visibleItems, margin, this.subgroups, this.itemSet.options.stackSubgroups);
}
}
this.stackDirty = false;
}
// recalculate the height of the group
var height = this._calculateHeight(margin);
}
// calculate actual size and position
foreground = this.dom.foreground;
this.top = foreground.offsetTop;
this.right = foreground.offsetLeft;
this.width = foreground.offsetWidth;
Group.prototype._didResize = function(resized, height) {
resized = util.updateProperty(this, 'height', height) || resized;
// recalculate size of label
resized = util.updateProperty(this.props.label, 'width', this.dom.inner.clientWidth) || resized;
resized = util.updateProperty(this.props.label, 'height', this.dom.inner.clientHeight) || resized;
var labelWidth = this.dom.inner.clientWidth;
var labelHeight = this.dom.inner.clientHeight;
resized = util.updateProperty(this.props.label, 'width', labelWidth) || resized;
resized = util.updateProperty(this.props.label, 'height', labelHeight) || resized;
return resized;
}
// apply new height
this.dom.background.style.height = height + 'px';
this.dom.foreground.style.height = height + 'px';
Group.prototype._applyGroupHeight = function(height) {
this.dom.background.style.height = height + 'px';
this.dom.foreground.style.height = height + 'px';
this.dom.label.style.height = height + 'px';
}
// update vertical position of items after they are re-stacked and the height of the group is calculated
// update vertical position of items after they are re-stacked and the height of the group is calculated
Group.prototype._updateItemsVerticalPosition = function(resized, margin) {
for (var i = 0, ii = this.visibleItems.length; i < ii; i++) {
var item = this.visibleItems[i];
item.repositionY(margin);
@ -312,10 +295,84 @@ Group.prototype.redraw = function(range, margin, forceRestack) {
}
if (!this.isVisible && this.height) {
return resized = false;
return false;
}
return resized;
}
/**
* Repaint this group
* @param {{start: number, end: number}} range
* @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
* @param {boolean} [forceRestack=false] Force restacking of all items
* @param {boolean} [returnQueue=false] return the queue or if the group resized
* @return {boolean} Returns true if the group is resized or the redraw queue if returnQueue=true
*/
Group.prototype.redraw = function(range, margin, forceRestack, returnQueue) {
var resized = false;
var lastIsVisible = this.isVisible;
var height;
var queue = [
// force recalculation of the height of the items when the marker height changed
// (due to the Timeline being attached to the DOM or changed from display:none to visible)
(function () {
forceRestack = this._didMarkerHeightChange.bind(this);
}).bind(this),
// recalculate the height of the subgroups
this._updateSubGroupHeights.bind(this, margin),
// calculate actual size and position
this._calculateGroupSizeAndPosition.bind(this),
// check if group is visible
(function() {
this.isVisible = this._isGroupVisible.bind(this)(range, margin);
}).bind(this),
// redraw Items if needed
(function() {
this._redrawItems.bind(this)(forceRestack, lastIsVisible, margin, range)
}).bind(this),
// update subgroups
this._updateSubgroupsSizes.bind(this),
// recalculate the height of the group
(function() {
height = this._calculateHeight.bind(this)(margin);
}).bind(this),
// calculate actual size and position again
this._calculateGroupSizeAndPosition.bind(this),
// check if resized
(function() {
resized = this._didResize.bind(this)(resized, height)
}).bind(this),
// apply group height
(function() {
this._applyGroupHeight.bind(this)(height)
}).bind(this),
// update vertical position of items after they are re-stacked and the height of the group is calculated
(function() {
return resized = this._updateItemsVerticalPosition.bind(this)(resized, margin)
}).bind(this)
]
if (returnQueue) {
return queue;
} else {
var result;
queue.forEach(function (fn) {
result = fn();
});
return result;
}
};
/**
@ -324,7 +381,7 @@ Group.prototype.redraw = function(range, margin, forceRestack) {
* @param {{item: vis.Item}} margin
* @private
*/
Group.prototype._calculateSubGroupHeights = function (margin) {
Group.prototype._updateSubGroupHeights = function (margin) {
if (Object.keys(this.subgroups).length > 0) {
var me = this;

+ 30
- 6
lib/timeline/component/ItemSet.js View File

@ -681,13 +681,37 @@ ItemSet.prototype.redraw = function() {
// redraw the background group
this.groups[BACKGROUND].redraw(range, nonFirstMargin, forceRestack);
// redraw all regular groups
util.forEach(this.groups, function (group) {
var groupMargin = (group == firstGroup) ? firstMargin : nonFirstMargin;
var groupResized = group.redraw(range, groupMargin, forceRestack);
resized = groupResized || resized;
height += group.height;
var redrawQueue = {};
var redrawQueueLength = 0;
// collect redraw functions
util.forEach(this.groups, function (group, key) {
if (key === BACKGROUND) return;
var groupMargin = group == firstGroup ? firstMargin : nonFirstMargin;
var returnQueue = true;
redrawQueue[key] = group.redraw(range, groupMargin, forceRestack, returnQueue);
redrawQueueLength = redrawQueue[key].length;
});
if (redrawQueueLength) {
var redrawResults = {};
for (var i = 0; i < redrawQueueLength; i++) {
util.forEach(redrawQueue, function (fns, key) {
redrawResults[key] = fns[i]();
});
}
// redraw all regular groups
util.forEach(this.groups, function (group, key) {
if (key === BACKGROUND) return;
var groupResized = redrawResults[key];
resized = groupResized || resized;
height += group.height;
});
height = Math.max(height, minHeight);
}
height = Math.max(height, minHeight);
// update frame height

Loading…
Cancel
Save