diff --git a/docs/timeline/index.html b/docs/timeline/index.html
index 9a623722..321ef258 100644
--- a/docs/timeline/index.html
+++ b/docs/timeline/index.html
@@ -943,7 +943,14 @@ function (option, path) {
Orientation of the timeline items: 'top' or 'bottom' (default). Determines whether items are aligned to the top or bottom of the Timeline.
-
+
+ rollingMode
+ boolean
+ false
+ If true, the timeline will initial in a rolling mode - the current time will always be centered. I the user drags the timeline, the timeline will go out of rolling mode and a toggle button will appear. Clicking that button will go back to rolling mode. Zooming in rolling mode will zoom in to the center without consideration of the mouse position.
+
+
+
rtl
boolean
false
diff --git a/examples/timeline/interaction/rollingMode.html b/examples/timeline/interaction/rollingMode.html
new file mode 100644
index 00000000..80b599f4
--- /dev/null
+++ b/examples/timeline/interaction/rollingMode.html
@@ -0,0 +1,45 @@
+
+
+ Timeline | rolling mode Option
+
+
+
+
+
+
+
+
+
+⛶ Timeline rolling mode option
+
+
+
+
+
+
+
+
diff --git a/lib/timeline/Core.js b/lib/timeline/Core.js
index a5034479..9441b77c 100644
--- a/lib/timeline/Core.js
+++ b/lib/timeline/Core.js
@@ -50,6 +50,7 @@ Core.prototype._create = function (container) {
this.dom.shadowBottomLeft = document.createElement('div');
this.dom.shadowTopRight = document.createElement('div');
this.dom.shadowBottomRight = document.createElement('div');
+ this.dom.rollingModeBtn = document.createElement('div');
this.dom.root.className = 'vis-timeline';
this.dom.background.className = 'vis-panel vis-background';
@@ -69,6 +70,7 @@ Core.prototype._create = function (container) {
this.dom.shadowBottomLeft.className = 'vis-shadow vis-bottom';
this.dom.shadowTopRight.className = 'vis-shadow vis-top';
this.dom.shadowBottomRight.className = 'vis-shadow vis-bottom';
+ this.dom.rollingModeBtn.className = 'vis-rolling-mode-btn';
this.dom.root.appendChild(this.dom.background);
this.dom.root.appendChild(this.dom.backgroundVertical);
@@ -78,6 +80,8 @@ Core.prototype._create = function (container) {
this.dom.root.appendChild(this.dom.rightContainer);
this.dom.root.appendChild(this.dom.top);
this.dom.root.appendChild(this.dom.bottom);
+ this.dom.root.appendChild(this.dom.bottom);
+ this.dom.root.appendChild(this.dom.rollingModeBtn);
this.dom.centerContainer.appendChild(this.dom.center);
this.dom.leftContainer.appendChild(this.dom.left);
@@ -311,6 +315,8 @@ Core.prototype.setOptions = function (options) {
];
util.selectiveExtend(fields, this.options, options);
+ this.dom.rollingModeBtn.style.visibility = 'hidden';
+
if (this.options.rtl) {
this.dom.container.style.direction = "rtl";
this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical-rtl';
diff --git a/lib/timeline/Range.js b/lib/timeline/Range.js
index 3e250f38..577e0863 100644
--- a/lib/timeline/Range.js
+++ b/lib/timeline/Range.js
@@ -14,8 +14,9 @@ var DateUtil = require('./DateUtil');
*/
function Range(body, options) {
var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0);
- this.start = now.clone().add(-3, 'days').valueOf(); // Number
- this.end = now.clone().add(4, 'days').valueOf(); // Number
+ this.start = options.start || now.clone().add(-3, 'days').valueOf();
+ this.end = options.end || now.clone().add(4, 'days').valueOf();
+ this.rolling = false;
this.body = body;
this.deltaDifference = 0;
@@ -55,6 +56,9 @@ function Range(body, options) {
this.body.emitter.on('touch', this._onTouch.bind(this));
this.body.emitter.on('pinch', this._onPinch.bind(this));
+ // on click of rolling mode button
+ this.body.dom.rollingModeBtn.addEventListener('click', this.startRolling.bind(this));
+
this.setOptions(options);
}
@@ -80,11 +84,14 @@ Range.prototype.setOptions = function (options) {
if (options) {
// copy the options that we know
var fields = [
- 'direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable',
- 'moment', 'activate', 'hiddenDates', 'zoomKey', 'rtl', 'horizontalScroll'
+ 'animation', 'direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable',
+ 'moment', 'activate', 'hiddenDates', 'zoomKey', 'rtl', 'showCurrentTime', 'rollMode', 'horizontalScroll'
];
util.selectiveExtend(fields, this.options, options);
+ if (options.rollingMode) {
+ this.startRolling();
+ }
if ('start' in options || 'end' in options) {
// apply a new range. both start and end are optional
this.setRange(options.start, options.end);
@@ -103,6 +110,52 @@ function validateDirection (direction) {
}
}
+/**
+ * Start auto refreshing the current time bar
+ */
+Range.prototype.startRolling = function() {
+ var me = this;
+
+
+ function update () {
+ me.stopRolling();
+ me.rolling = true;
+
+
+ var interval = me.end - me.start;
+ var t = util.convert(new Date(), 'Date').valueOf();
+
+ var start = t - interval / 2;
+ var end = t + interval / 2;
+ var animation = (me.options && me.options.animation !== undefined) ? me.options.animation : true;
+
+ me.setRange(start, end, false);
+
+ // determine interval to refresh
+ var scale = me.conversion(me.body.domProps.center.width).scale;
+ var interval = 1 / scale / 10;
+ if (interval < 30) interval = 30;
+ if (interval > 1000) interval = 1000;
+
+ me.body.dom.rollingModeBtn.style.visibility = "hidden";
+ // start a renderTimer to adjust for the new time
+ me.currentTimeTimer = setTimeout(update, interval);
+ }
+
+ update();
+};
+
+/**
+ * Stop auto refreshing the current time bar
+ */
+Range.prototype.stopRolling = function() {
+ if (this.currentTimeTimer !== undefined) {
+ clearTimeout(this.currentTimeTimer);
+ this.rolling = false;
+ this.body.dom.rollingModeBtn.style.visibility = "visible";
+ }
+};
+
/**
* Set a new start and end range
* @param {Date | Number | String} [start]
@@ -388,6 +441,8 @@ Range.prototype._onDragStart = function(event) {
// when releasing the fingers in opposite order from the touch screen
if (!this.props.touch.allowDragging) return;
+ this.stopRolling();
+
this.props.touch.start = this.start;
this.props.touch.end = this.end;
this.props.touch.dragging = true;
@@ -520,7 +575,7 @@ Range.prototype._onMouseWheel = function(event) {
// Prevent default actions caused by mouse wheel
// (else the page and timeline both scroll)
event.preventDefault();
-
+
// calculate a single scroll jump relative to the range scale
var diff = delta * (this.end - this.start) / 20;
// calculate new start and end
@@ -555,9 +610,13 @@ Range.prototype._onMouseWheel = function(event) {
}
// calculate center, the date to zoom around
- var pointer = this.getPointer({x: event.clientX, y: event.clientY}, this.body.dom.center);
- var pointerDate = this._pointerToDate(pointer);
-
+ var pointerDate
+ if (this.rolling) {
+ pointerDate = (this.start + this.end) / 2;
+ } else {
+ var pointer = this.getPointer({x: event.clientX, y: event.clientY}, this.body.dom.center);
+ pointerDate = this._pointerToDate(pointer);
+ }
this.zoom(scale, pointerDate, delta, event);
// Prevent default actions caused by mouse wheel
@@ -594,6 +653,8 @@ Range.prototype._onPinch = function (event) {
this.props.touch.center = this.getPointer(event.center, this.body.dom.center);
}
+ this.stopRolling();
+
var scale = 1 / (event.scale + this.scaleOffset);
var centerDate = this._pointerToDate(this.props.touch.center);
diff --git a/lib/timeline/Timeline.js b/lib/timeline/Timeline.js
index 7f5d463c..8abe0182 100644
--- a/lib/timeline/Timeline.js
+++ b/lib/timeline/Timeline.js
@@ -58,10 +58,8 @@ function Timeline (container, items, groups, options) {
};
this.options = util.deepExtend({}, this.defaultOptions);
-
// Create the DOM, props, and emitter
this._create(container);
-
if (!options || (options && typeof options.rtl == "undefined")) {
var directionFromDom, domNode = this.dom.root;
while (!directionFromDom && domNode) {
@@ -72,6 +70,7 @@ function Timeline (container, items, groups, options) {
} else {
this.options.rtl = options.rtl;
}
+ this.options.rollingMode = options.rollingMode;
// all components listed here will be repainted automatically
this.components = [];
@@ -134,7 +133,7 @@ function Timeline (container, items, groups, options) {
//Single time autoscale/fit
this.fitDone = false;
this.on('changed', function (){
- if (this.itemsData == null) return;
+ if (this.itemsData == null || this.options.rollingMode) return;
if (!me.fitDone) {
me.fitDone = true;
if (me.options.start != undefined || me.options.end != undefined) {
@@ -144,7 +143,6 @@ function Timeline (container, items, groups, options) {
var start = me.options.start != undefined ? me.options.start : range.min;
var end = me.options.end != undefined ? me.options.end : range.max;
-
me.setWindow(start, end, {animation: false});
}
else {
diff --git a/lib/timeline/component/css/currenttime.css b/lib/timeline/component/css/currenttime.css
index 7d3547a1..658fdfef 100644
--- a/lib/timeline/component/css/currenttime.css
+++ b/lib/timeline/component/css/currenttime.css
@@ -3,4 +3,27 @@
width: 2px;
z-index: 1;
pointer-events: none;
+}
+
+.vis-rolling-mode-btn {
+ height: 40px;
+ width: 40px;
+ position: absolute;
+ top: 7px;
+ right: 20px;
+ border-radius: 50%;
+ font-size: 28px;
+ cursor: pointer;
+ opacity: 0.8;
+ color: white;
+ font-weight: bold;
+ text-align: center;
+ background: #3876c2;
+}
+.vis-rolling-mode-btn:before {
+ content: "\26F6";
+}
+
+.vis-rolling-mode-btn:hover {
+ opacity: 1;
}
\ No newline at end of file
diff --git a/lib/timeline/optionsTimeline.js b/lib/timeline/optionsTimeline.js
index aadcf2ff..4354a213 100644
--- a/lib/timeline/optionsTimeline.js
+++ b/lib/timeline/optionsTimeline.js
@@ -26,6 +26,7 @@ let allOptions = {
//globals :
align: {string},
rtl: {boolean, 'undefined': 'undefined'},
+ rollingMode: {boolean, 'undefined': 'undefined'},
verticalScroll: {boolean, 'undefined': 'undefined'},
horizontalScroll: {boolean, 'undefined': 'undefined'},
autoResize: {boolean},