Browse Source

Nathanbennett improve timeline stack performance (#3078)

* Fix redraw order

* Fix error when option is not defined

* Allow template labels

* Add .travis.yml file

* Add experiment travis code

* Fix react example

* Improve redraw performance by not restacking non-visible groups (related to #2835)

* only restack necessary groups on redraw (related to #2835)
gemini
yotamberk 7 years ago
committed by GitHub
parent
commit
ec4699df57
4 changed files with 38 additions and 41 deletions
  1. +2
    -2
      lib/timeline/component/BackgroundGroup.js
  2. +30
    -24
      lib/timeline/component/Group.js
  3. +4
    -14
      lib/timeline/component/ItemSet.js
  4. +2
    -1
      lib/timeline/component/item/Item.js

+ 2
- 2
lib/timeline/component/BackgroundGroup.js View File

@ -22,10 +22,10 @@ BackgroundGroup.prototype = Object.create(Group.prototype);
* Repaint this group
* @param {{start: number, end: number}} range
* @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
* @param {boolean} [restack=false] Force restacking of all items
* @param {boolean} [forceRestack=false] Force restacking of all items
* @return {boolean} Returns true if the group is resized
*/
BackgroundGroup.prototype.redraw = function(range, margin, restack) {
BackgroundGroup.prototype.redraw = function(range, margin, forceRestack) {
var resized = false;
this.visibleItems = this._updateItemsInRange(this.orderedItems, this.visibleItems, range);

+ 30
- 24
lib/timeline/component/Group.js View File

@ -15,6 +15,7 @@ function Group (groupId, data, itemSet) {
this.subgroupOrderer = data && data.subgroupOrder;
this.itemSet = itemSet;
this.isVisible = null;
this.stackDirty = true; // if true, items will be restacked on next redraw
if (data && data.nestedGroups) {
this.nestedGroups = data.nestedGroups;
@ -212,12 +213,12 @@ Group.prototype.getLabelWidth = function() {
* Repaint this group
* @param {{start: number, end: number}} range
* @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
* @param {boolean} [restack=false] Force restacking of all items
* @param {boolean} [forceRestack=false] Force restacking of all items
* @return {boolean} Returns true if the group is resized
*/
Group.prototype.redraw = function(range, margin, restack) {
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)
var markerHeight = this.dom.marker.clientHeight;
@ -228,9 +229,9 @@ Group.prototype.redraw = function(range, margin, restack) {
if (item.displayed) item.redraw();
});
restack = true;
}
forceRestack = true;
}
// recalculate the height of the subgroups
this._calculateSubGroupHeights(margin);
@ -240,12 +241,15 @@ Group.prototype.redraw = function(range, margin, restack) {
this.right = foreground.offsetLeft;
this.width = foreground.offsetWidth;
var lastIsVisible = this.isVisible;
this.isVisible = this._isGroupVisible(range, margin);
// reposition visible items vertically
if (typeof this.itemSet.options.order === 'function') {
// a custom order function
var restack = forceRestack || this.stackDirty || (this.isVisible && !lastIsVisible);
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
// show all items
@ -264,23 +268,24 @@ Group.prototype.redraw = function(range, margin, restack) {
return me.itemSet.options.order(a.data, b.data);
});
stack.stack(customOrderedItems, margin, true /* restack=true */);
}
this.visibleItems = this._updateItemsInRange(this.orderedItems, this.visibleItems, range);
}
else {
// no custom order function, lazy stacking
this.visibleItems = this._updateItemsInRange(this.orderedItems, this.visibleItems, range);
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, restack);
}
else { // no stacking
stack.nostack(this.visibleItems, margin, this.subgroups, this.itemSet.options.stackSubgroups);
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
stack.nostack(this.visibleItems, margin, this.subgroups, this.itemSet.options.stackSubgroups);
}
}
this.stackDirty = false;
}
this._updateSubgroupsSizes();
// recalculate the height of the group
@ -435,7 +440,7 @@ Group.prototype.hide = function() {
Group.prototype.add = function(item) {
this.items[item.id] = item;
item.setParent(this);
this.stackDirty = true;
// add to
if (item.data.subgroup !== undefined) {
this._addToSubgroup(item);
@ -540,6 +545,7 @@ Group.prototype.resetSubgroups = function() {
Group.prototype.remove = function(item) {
delete this.items[item.id];
item.setParent(null);
this.stackDirty = true;
// remove from visible items
var index = this.visibleItems.indexOf(item);

+ 4
- 14
lib/timeline/component/ItemSet.js View File

@ -155,7 +155,6 @@ function ItemSet(body, options) {
this.groupIds = [];
this.selection = []; // list with the ids of all selected nodes
this.stackDirty = true; // if true, all items will be restacked on next redraw
this.popup = null;
@ -418,7 +417,6 @@ ItemSet.prototype.setOptions = function(options) {
*/
ItemSet.prototype.markDirty = function(options) {
this.groupIds = [];
this.stackDirty = true;
if (options && options.refreshItems) {
util.forEach(this.items, function (item) {
@ -617,12 +615,11 @@ ItemSet.prototype.redraw = function() {
var visibleInterval = range.end - range.start;
var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.props.width != this.props.lastWidth);
var scrolled = range.start != this.lastRangeStart;
if (zoomed || scrolled) this.stackDirty = true;
var forceRestack = (zoomed || scrolled);
this.lastVisibleInterval = visibleInterval;
this.lastRangeStart = range.start;
this.props.lastWidth = this.props.width;
var restack = this.stackDirty;
var firstGroup = this._firstGroup();
var firstMargin = {
item: margin.item,
@ -636,17 +633,16 @@ ItemSet.prototype.redraw = function() {
var minHeight = margin.axis + margin.item.vertical;
// redraw the background group
this.groups[BACKGROUND].redraw(range, nonFirstMargin, restack);
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, restack);
var groupResized = group.redraw(range, groupMargin, forceRestack);
resized = groupResized || resized;
height += group.height;
});
height = Math.max(height, minHeight);
this.stackDirty = false;
// update frame height
frame.style.height = asSize(height);
@ -978,7 +974,6 @@ ItemSet.prototype._onUpdate = function(ids) {
}.bind(this));
this._order();
this.stackDirty = true; // force re-stacking of all items next redraw
this.body.emitter.emit('_change', {queue: true});
};
@ -1008,7 +1003,6 @@ ItemSet.prototype._onRemove = function(ids) {
if (count) {
// update order
this._order();
this.stackDirty = true; // force re-stacking of all items next redraw
this.body.emitter.emit('_change', {queue: true});
}
};
@ -1540,7 +1534,6 @@ ItemSet.prototype._onDrag = function (event) {
//make sure we stay in bounds
newOffset = Math.max(0, newOffset);
newOffset = Math.min(me.groupIds.length-1, newOffset);
itemData.group = me.groupIds[newOffset];
}
}
@ -1553,8 +1546,7 @@ ItemSet.prototype._onDrag = function (event) {
}
}.bind(this));
}.bind(this));
this.stackDirty = true; // force re-stacking of all items next redraw
this.body.emitter.emit('_change');
}
};
@ -1607,7 +1599,6 @@ ItemSet.prototype._onDragEnd = function (event) {
}
// force re-stacking of all items next redraw
me.stackDirty = true;
me.body.emitter.emit('_change');
});
}
@ -1624,7 +1615,6 @@ ItemSet.prototype._onDragEnd = function (event) {
// restore original values
props.item.setData(props.data);
me.stackDirty = true; // force re-stacking of all items next redraw
me.body.emitter.emit('_change');
}
});

+ 2
- 1
lib/timeline/component/item/Item.js View File

@ -64,6 +64,7 @@ Item.prototype.setData = function(data) {
if (groupChanged && this.parent != null) {
this.parent.itemSet._moveToGroup(this, data.group);
}
this.parent.stackDirty = true;
var subGroupChanged = data.subgroup != undefined && this.data.subgroup != data.subgroup;
if (subGroupChanged && this.parent != null) {
@ -78,7 +79,7 @@ Item.prototype.setData = function(data) {
/**
* Set a parent for the item
* @param {ItemSet | Group} parent
* @param {Group} parent
*/
Item.prototype.setParent = function(parent) {
if (this.displayed) {

Loading…
Cancel
Save