Skip to content
This repository has been archived by the owner on Jul 29, 2019. It is now read-only.

Commit

Permalink
Group redraw performance (#3409)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
yotamberk authored Sep 18, 2017
1 parent b0481d3 commit 9830616
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 57 deletions.
159 changes: 108 additions & 51 deletions lib/timeline/component/Group.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,47 +206,30 @@ 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;
util.forEach(this.items, function (item) {
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
Expand All @@ -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);
}
Expand All @@ -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);
Expand All @@ -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;
}
};

/**
Expand All @@ -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;

Expand Down
36 changes: 30 additions & 6 deletions lib/timeline/component/ItemSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 9830616

Please sign in to comment.