Browse Source

feat: #1746 rolling mode (#2439)

* Add initial support for rolling mode
* Add rollingModeBtn and simplify example
* Make icon in css:
* Remove icon btn
* Add docs
* Fix zoom in rolling mode and initial start/end times
readme-improvements
yotamberk 8 years ago
committed by Alexander Wunschik
parent
commit
3311b9b17e
7 changed files with 154 additions and 13 deletions
  1. +8
    -1
      docs/timeline/index.html
  2. +45
    -0
      examples/timeline/interaction/rollingMode.html
  3. +6
    -0
      lib/timeline/Core.js
  4. +69
    -8
      lib/timeline/Range.js
  5. +2
    -4
      lib/timeline/Timeline.js
  6. +23
    -0
      lib/timeline/component/css/currenttime.css
  7. +1
    -0
      lib/timeline/optionsTimeline.js

+ 8
- 1
docs/timeline/index.html View File

@ -943,7 +943,14 @@ function (option, path) {
<td>Orientation of the timeline items: 'top' or 'bottom' (default). Determines whether items are aligned to the top or bottom of the Timeline.</td> <td>Orientation of the timeline items: 'top' or 'bottom' (default). Determines whether items are aligned to the top or bottom of the Timeline.</td>
</tr> </tr>
<tr>
<tr>
<td>rollingMode</td>
<td>boolean</td>
<td><code>false</code></td>
<td>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.</td>
</tr>
<tr>
<td>rtl</td> <td>rtl</td>
<td>boolean</td> <td>boolean</td>
<td><code>false</code></td> <td><code>false</code></td>

+ 45
- 0
examples/timeline/interaction/rollingMode.html View File

@ -0,0 +1,45 @@
<html>
<head>
<title>Timeline | rolling mode Option</title>
<meta charset="utf-8">
<script src="../../../dist/vis.js"></script>
<link href="../../../dist/vis.css" rel="stylesheet" type="text/css" />
<script src="../../googleAnalytics.js"></script>
</head>
<body>
<h1><i id="icon">&#9974;</i>Timeline rolling mode option</h1>
<div id="mytimeline"></div>
<script>
var container = document.getElementById('mytimeline');
var items = new vis.DataSet();
for (var i = 10; i >= 0; i--) {
items.add({
id: i,
content: "item " + i,
start: new Date(new Date().getTime() + i*100000)
});
}
// Configuration for the Timeline
// specify options
var options = {
start: new Date(),
end: new Date(new Date().getTime() + 1000000),
rollingMode: true
};
// create a Timeline
timeline = new vis.Timeline(container, items, null, options);
</script>
</body>
</html>

+ 6
- 0
lib/timeline/Core.js View File

@ -50,6 +50,7 @@ Core.prototype._create = function (container) {
this.dom.shadowBottomLeft = document.createElement('div'); this.dom.shadowBottomLeft = document.createElement('div');
this.dom.shadowTopRight = document.createElement('div'); this.dom.shadowTopRight = document.createElement('div');
this.dom.shadowBottomRight = document.createElement('div'); this.dom.shadowBottomRight = document.createElement('div');
this.dom.rollingModeBtn = document.createElement('div');
this.dom.root.className = 'vis-timeline'; this.dom.root.className = 'vis-timeline';
this.dom.background.className = 'vis-panel vis-background'; 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.shadowBottomLeft.className = 'vis-shadow vis-bottom';
this.dom.shadowTopRight.className = 'vis-shadow vis-top'; this.dom.shadowTopRight.className = 'vis-shadow vis-top';
this.dom.shadowBottomRight.className = 'vis-shadow vis-bottom'; 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.background);
this.dom.root.appendChild(this.dom.backgroundVertical); 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.rightContainer);
this.dom.root.appendChild(this.dom.top); 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.bottom);
this.dom.root.appendChild(this.dom.rollingModeBtn);
this.dom.centerContainer.appendChild(this.dom.center); this.dom.centerContainer.appendChild(this.dom.center);
this.dom.leftContainer.appendChild(this.dom.left); this.dom.leftContainer.appendChild(this.dom.left);
@ -311,6 +315,8 @@ Core.prototype.setOptions = function (options) {
]; ];
util.selectiveExtend(fields, this.options, options); util.selectiveExtend(fields, this.options, options);
this.dom.rollingModeBtn.style.visibility = 'hidden';
if (this.options.rtl) { if (this.options.rtl) {
this.dom.container.style.direction = "rtl"; this.dom.container.style.direction = "rtl";
this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical-rtl'; this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical-rtl';

+ 69
- 8
lib/timeline/Range.js View File

@ -14,8 +14,9 @@ var DateUtil = require('./DateUtil');
*/ */
function Range(body, options) { function Range(body, options) {
var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); 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.body = body;
this.deltaDifference = 0; this.deltaDifference = 0;
@ -55,6 +56,9 @@ function Range(body, options) {
this.body.emitter.on('touch', this._onTouch.bind(this)); this.body.emitter.on('touch', this._onTouch.bind(this));
this.body.emitter.on('pinch', this._onPinch.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); this.setOptions(options);
} }
@ -80,11 +84,14 @@ Range.prototype.setOptions = function (options) {
if (options) { if (options) {
// copy the options that we know // copy the options that we know
var fields = [ 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); util.selectiveExtend(fields, this.options, options);
if (options.rollingMode) {
this.startRolling();
}
if ('start' in options || 'end' in options) { if ('start' in options || 'end' in options) {
// apply a new range. both start and end are optional // apply a new range. both start and end are optional
this.setRange(options.start, options.end); 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 * Set a new start and end range
* @param {Date | Number | String} [start] * @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 // when releasing the fingers in opposite order from the touch screen
if (!this.props.touch.allowDragging) return; if (!this.props.touch.allowDragging) return;
this.stopRolling();
this.props.touch.start = this.start; this.props.touch.start = this.start;
this.props.touch.end = this.end; this.props.touch.end = this.end;
this.props.touch.dragging = true; this.props.touch.dragging = true;
@ -520,7 +575,7 @@ Range.prototype._onMouseWheel = function(event) {
// Prevent default actions caused by mouse wheel // Prevent default actions caused by mouse wheel
// (else the page and timeline both scroll) // (else the page and timeline both scroll)
event.preventDefault(); event.preventDefault();
// calculate a single scroll jump relative to the range scale // calculate a single scroll jump relative to the range scale
var diff = delta * (this.end - this.start) / 20; var diff = delta * (this.end - this.start) / 20;
// calculate new start and end // calculate new start and end
@ -555,9 +610,13 @@ Range.prototype._onMouseWheel = function(event) {
} }
// calculate center, the date to zoom around // 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); this.zoom(scale, pointerDate, delta, event);
// Prevent default actions caused by mouse wheel // 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.props.touch.center = this.getPointer(event.center, this.body.dom.center);
} }
this.stopRolling();
var scale = 1 / (event.scale + this.scaleOffset); var scale = 1 / (event.scale + this.scaleOffset);
var centerDate = this._pointerToDate(this.props.touch.center); var centerDate = this._pointerToDate(this.props.touch.center);

+ 2
- 4
lib/timeline/Timeline.js View File

@ -58,10 +58,8 @@ function Timeline (container, items, groups, options) {
}; };
this.options = util.deepExtend({}, this.defaultOptions); this.options = util.deepExtend({}, this.defaultOptions);
// Create the DOM, props, and emitter // Create the DOM, props, and emitter
this._create(container); this._create(container);
if (!options || (options && typeof options.rtl == "undefined")) { if (!options || (options && typeof options.rtl == "undefined")) {
var directionFromDom, domNode = this.dom.root; var directionFromDom, domNode = this.dom.root;
while (!directionFromDom && domNode) { while (!directionFromDom && domNode) {
@ -72,6 +70,7 @@ function Timeline (container, items, groups, options) {
} else { } else {
this.options.rtl = options.rtl; this.options.rtl = options.rtl;
} }
this.options.rollingMode = options.rollingMode;
// all components listed here will be repainted automatically // all components listed here will be repainted automatically
this.components = []; this.components = [];
@ -134,7 +133,7 @@ function Timeline (container, items, groups, options) {
//Single time autoscale/fit //Single time autoscale/fit
this.fitDone = false; this.fitDone = false;
this.on('changed', function (){ this.on('changed', function (){
if (this.itemsData == null) return;
if (this.itemsData == null || this.options.rollingMode) return;
if (!me.fitDone) { if (!me.fitDone) {
me.fitDone = true; me.fitDone = true;
if (me.options.start != undefined || me.options.end != undefined) { 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 start = me.options.start != undefined ? me.options.start : range.min;
var end = me.options.end != undefined ? me.options.end : range.max; var end = me.options.end != undefined ? me.options.end : range.max;
me.setWindow(start, end, {animation: false}); me.setWindow(start, end, {animation: false});
} }
else { else {

+ 23
- 0
lib/timeline/component/css/currenttime.css View File

@ -3,4 +3,27 @@
width: 2px; width: 2px;
z-index: 1; z-index: 1;
pointer-events: none; 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;
} }

+ 1
- 0
lib/timeline/optionsTimeline.js View File

@ -26,6 +26,7 @@ let allOptions = {
//globals : //globals :
align: {string}, align: {string},
rtl: {boolean, 'undefined': 'undefined'}, rtl: {boolean, 'undefined': 'undefined'},
rollingMode: {boolean, 'undefined': 'undefined'},
verticalScroll: {boolean, 'undefined': 'undefined'}, verticalScroll: {boolean, 'undefined': 'undefined'},
horizontalScroll: {boolean, 'undefined': 'undefined'}, horizontalScroll: {boolean, 'undefined': 'undefined'},
autoResize: {boolean}, autoResize: {boolean},

Loading…
Cancel
Save