From aee5c1e3fd91891ed0de4e45831b823520b12fab Mon Sep 17 00:00:00 2001 From: jos Date: Mon, 4 May 2015 12:30:24 +0200 Subject: [PATCH] Renamed option `animate` to `animation`, and changed it to be either a boolean or an object `{duration: number, easingFunction: string}` --- HISTORY.md | 2 + docs/timeline.html | 12 +++-- examples/timeline/13_past_and_future.html | 2 +- examples/timeline/22_window_adjustment.html | 29 ++++++------ lib/timeline/Core.js | 51 +++++++++++---------- lib/timeline/Graph2d.js | 4 +- lib/timeline/Range.js | 45 ++++++++++-------- lib/timeline/Timeline.js | 27 +++++------ lib/util.js | 19 -------- 9 files changed, 95 insertions(+), 96 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 2f9d01d9..75d98f06 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -24,6 +24,8 @@ http://visjs.org `4-8h` to `h4-h8`. - Deprecated option `showCustomTime`. Use method `addCustomTime()` instead. - Deprecated event `finishedRedraw` as it's redundant. +- Renamed option `animate` to `animation`, and changed it to be either a boolean + or an object `{duration: number, easingFunction: string}`. ### Network diff --git a/docs/timeline.html b/docs/timeline.html index 832044ca..2c9f8aaa 100644 --- a/docs/timeline.html +++ b/docs/timeline.html @@ -880,7 +880,8 @@ var options = { Adjust the visible window such that it fits all items. See also function focus(id). Available options: @@ -890,7 +891,7 @@ var options = { none Adjust the visible window such that the selected item (or multiple items) are centered on screen. See also function fit(). Available options: @@ -952,7 +953,7 @@ var options = { none Move the window such that given time is centered on screen. Parameter time can be a Date, Number, or String. Available options: @@ -1047,7 +1048,7 @@ var options = { Select one or multiple items by their id. The currently selected items will be unselected. To unselect all selected items, call `setSelection([])`. Available options: @@ -1057,7 +1058,7 @@ var options = { none Set the current visible window. The parameters start and end can be a Date, Number, or String. If the parameter value of start or end is null, the parameter will be left unchanged. Available options: @@ -1065,6 +1066,7 @@ var options = { +

Events

Timeline fires events when changing the visible window by dragging, when diff --git a/examples/timeline/13_past_and_future.html b/examples/timeline/13_past_and_future.html index 87cff2e0..f11c91fd 100644 --- a/examples/timeline/13_past_and_future.html +++ b/examples/timeline/13_past_and_future.html @@ -81,7 +81,7 @@ // set a custom range from -2 minute to +3 minutes current time var start = new Date((new Date()).getTime() - 2 * 60 * 1000); var end = new Date((new Date()).getTime() + 3 * 60 * 1000); - timeline.setWindow(start, end, {animate: false}); + timeline.setWindow(start, end, {animation: false}); diff --git a/examples/timeline/22_window_adjustment.html b/examples/timeline/22_window_adjustment.html index 3b4c1f71..cda8325d 100644 --- a/examples/timeline/22_window_adjustment.html +++ b/examples/timeline/22_window_adjustment.html @@ -8,6 +8,9 @@ font-family: arial, sans-serif; font-size: 11pt; } + input { + margin: 2px 0; + } @@ -17,14 +20,16 @@

This example demonstrates functions to adjust the visible window of the Timeline.

-
-
-
-
-
-
-
-
+

+
+
+
+
+
+
+
+
+

@@ -61,9 +66,7 @@ timeline.setWindow('2014-01-01', '2014-04-01'); }; document.getElementById('window2').onclick = function() { - timeline.setWindow('2014-01-01', '2014-04-01', { - animate: false - }); + timeline.setWindow('2014-01-01', '2014-04-01', {animation: false}); }; document.getElementById('fit').onclick = function() { timeline.fit(); @@ -77,9 +80,7 @@ timeline.focus(2); }; document.getElementById('focus2').onclick = function() { - timeline.focus([5, 6], { - animate: 3000 // ms - }); + timeline.focus([5, 6], {animation: {duration: 3000, easingFunction: 'linear'}}); // ms }; document.getElementById('focus3').onclick = function() { var selection = timeline.getSelection(); diff --git a/lib/timeline/Core.js b/lib/timeline/Core.js index 30c0dd15..e255251f 100644 --- a/lib/timeline/Core.js +++ b/lib/timeline/Core.js @@ -432,11 +432,12 @@ Core.prototype.getVisibleItems = function() { /** * Set Core window such that it fits all items * @param {Object} [options] Available options: - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. + * `animation: boolean | {duration: number, easingFunction: string}` + * If true (default), the range is animated + * smoothly to the new window. An object can be + * provided to specify duration and easing function. + * Default duration is 500 ms, and default easing + * function is 'easeInOutQuad'. */ Core.prototype.fit = function(options) { var range = this._getDataRange(); @@ -446,8 +447,8 @@ Core.prototype.fit = function(options) { return; } - var animate = (options && options.animate !== undefined) ? options.animate : true; - this.range.setRange(range.start, range.end, animate); + var animation = (options && options.animation !== undefined) ? options.animation : true; + this.range.setRange(range.start, range.end, animation); }; /** @@ -492,22 +493,23 @@ Core.prototype._getDataRange = function() { * @param {Date | Number | String | Object} [start] Start date of visible window * @param {Date | Number | String} [end] End date of visible window * @param {Object} [options] Available options: - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. + * `animation: boolean | {duration: number, easingFunction: string}` + * If true (default), the range is animated + * smoothly to the new window. An object can be + * provided to specify duration and easing function. + * Default duration is 500 ms, and default easing + * function is 'easeInOutQuad'. */ Core.prototype.setWindow = function(start, end, options) { - var animate; + var animation; if (arguments.length == 1) { var range = arguments[0]; - animate = (range.animate !== undefined) ? range.animate : true; - this.range.setRange(range.start, range.end, animate); + animation = (range.animation !== undefined) ? range.animation : true; + this.range.setRange(range.start, range.end, animation); } else { - animate = (options && options.animate !== undefined) ? options.animate : true; - this.range.setRange(start, end, animate); + animation = (options && options.animation !== undefined) ? options.animation : true; + this.range.setRange(start, end, animation); } }; @@ -515,11 +517,12 @@ Core.prototype.setWindow = function(start, end, options) { * Move the window such that given time is centered on screen. * @param {Date | Number | String} time * @param {Object} [options] Available options: - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. + * `animation: boolean | {duration: number, easingFunction: string}` + * If true (default), the range is animated + * smoothly to the new window. An object can be + * provided to specify duration and easing function. + * Default duration is 500 ms, and default easing + * function is 'easeInOutQuad'. */ Core.prototype.moveTo = function(time, options) { var interval = this.range.end - this.range.start; @@ -527,9 +530,9 @@ Core.prototype.moveTo = function(time, options) { var start = t - interval / 2; var end = t + interval / 2; - var animate = (options && options.animate !== undefined) ? options.animate : true; + var animation = (options && options.animation !== undefined) ? options.animation : true; - this.range.setRange(start, end, animate); + this.range.setRange(start, end, animation); }; /** diff --git a/lib/timeline/Graph2d.js b/lib/timeline/Graph2d.js index bb56fe29..75b3c3f5 100644 --- a/lib/timeline/Graph2d.js +++ b/lib/timeline/Graph2d.js @@ -151,10 +151,10 @@ Graph2d.prototype.setItems = function(items) { var start = this.options.start != undefined ? this.options.start : null; var end = this.options.end != undefined ? this.options.end : null; - this.setWindow(start, end, {animate: false}); + this.setWindow(start, end, {animation: false}); } else { - this.fit({animate: false}); + this.fit({animation: false}); } } }; diff --git a/lib/timeline/Range.js b/lib/timeline/Range.js index 6ce5bc0d..69e2ac87 100644 --- a/lib/timeline/Range.js +++ b/lib/timeline/Range.js @@ -40,7 +40,7 @@ function Range(body, options) { this.props = { touch: {} }; - this.animateTimer = null; + this.animationTimer = null; // drag listeners for dragging this.body.emitter.on('panstart', this._onDragStart.bind(this)); @@ -103,27 +103,35 @@ function validateDirection (direction) { * Set a new start and end range * @param {Date | Number | String} [start] * @param {Date | Number | String} [end] - * @param {boolean | number} [animate=false] If true, the range is animated - * smoothly to the new window. - * If animate is a number, the - * number is taken as duration - * Default duration is 500 ms. + * @param {boolean | {duration: number, easingFunction: string}} [animation=false] + * If true (default), the range is animated + * smoothly to the new window. An object can be + * provided to specify duration and easing function. + * Default duration is 500 ms, and default easing + * function is 'easeInOutQuad'. * @param {Boolean} [byUser=false] * */ -Range.prototype.setRange = function(start, end, animate, byUser) { +Range.prototype.setRange = function(start, end, animation, byUser) { if (byUser !== true) { byUser = false; } - var _start = start != undefined ? util.convert(start, 'Date').valueOf() : null; - var _end = end != undefined ? util.convert(end, 'Date').valueOf() : null; + var finalStart = start != undefined ? util.convert(start, 'Date').valueOf() : null; + var finalEnd = end != undefined ? util.convert(end, 'Date').valueOf() : null; this._cancelAnimation(); - if (animate) { + if (animation) { // true or an Object var me = this; var initStart = this.start; var initEnd = this.end; - var duration = typeof animate === 'number' ? animate : 500; + var duration = (typeof animation === 'object' && 'duration' in animation) ? animation.duration : 500; + var easingName = (typeof animation === 'object' && 'easingFunction' in animation) ? animation.easingFunction : 'easeInOutQuad'; + var easingFunction = util.easingFunctions[easingName]; + if (!easingFunction) { + throw new Error('Unknown easing function ' + JSON.stringify(easingName) + '. ' + + 'Choose from: ' + Object.keys(util.easingFunctions).join(', ')); + } + var initTime = new Date().valueOf(); var anyChanged = false; @@ -131,9 +139,10 @@ Range.prototype.setRange = function(start, end, animate, byUser) { if (!me.props.touch.dragging) { var now = new Date().valueOf(); var time = now - initTime; + var ease = easingFunction(time / duration); var done = time > duration; - var s = (done || _start === null) ? _start : util.easeInOutQuad(time, initStart, _start, duration); - var e = (done || _end === null) ? _end : util.easeInOutQuad(time, initEnd, _end, duration); + var s = (done || finalStart === null) ? finalStart : initStart + (finalStart - initStart) * ease; + var e = (done || finalEnd === null) ? finalEnd : initEnd + (finalEnd - initEnd) * ease; changed = me._applyRange(s, e); DateUtil.updateHiddenDates(me.body, me.options.hiddenDates); @@ -150,7 +159,7 @@ Range.prototype.setRange = function(start, end, animate, byUser) { else { // animate with as high as possible frame rate, leave 20 ms in between // each to prevent the browser from blocking - me.animateTimer = setTimeout(next, 20); + me.animationTimer = setTimeout(next, 20); } } }; @@ -158,7 +167,7 @@ Range.prototype.setRange = function(start, end, animate, byUser) { return next(); } else { - var changed = this._applyRange(_start, _end); + var changed = this._applyRange(finalStart, finalEnd); DateUtil.updateHiddenDates(this.body, this.options.hiddenDates); if (changed) { var params = {start: new Date(this.start), end: new Date(this.end), byUser:byUser}; @@ -173,9 +182,9 @@ Range.prototype.setRange = function(start, end, animate, byUser) { * @private */ Range.prototype._cancelAnimation = function () { - if (this.animateTimer) { - clearTimeout(this.animateTimer); - this.animateTimer = null; + if (this.animationTimer) { + clearTimeout(this.animationTimer); + this.animationTimer = null; } }; diff --git a/lib/timeline/Timeline.js b/lib/timeline/Timeline.js index 7cc6445a..99714624 100644 --- a/lib/timeline/Timeline.js +++ b/lib/timeline/Timeline.js @@ -177,10 +177,10 @@ Timeline.prototype.setItems = function(items) { var start = this.options.start != undefined ? this.options.start : dataRange.start; var end = this.options.end != undefined ? this.options.end : dataRange.end; - this.setWindow(start, end, {animate: false}); + this.setWindow(start, end, {animation: false}); } else { - this.fit({animate: false}); + this.fit({animation: false}); } } }; @@ -230,11 +230,12 @@ Timeline.prototype.setData = function (data) { * @param {Object} [options] Available options: * `focus: boolean` * If true, focus will be set to the selected item(s) - * `animate: boolean | number` + * `animation: boolean | {duration: number, easingFunction: string}` * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. + * smoothly to the new window. An object can be + * provided to specify duration and easing function. + * Default duration is 500 ms, and default easing + * function is 'easeInOutQuad'. * Only applicable when option focus is true. */ Timeline.prototype.setSelection = function(ids, options) { @@ -258,12 +259,12 @@ Timeline.prototype.getSelection = function() { * are centered on screen. * @param {String | String[]} id An item id or array with item ids * @param {Object} [options] Available options: - * `animate: boolean | number` + * `animation: boolean | {duration: number, easingFunction: string}` * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. - * Only applicable when option focus is true + * smoothly to the new window. An object can be + * provided to specify duration and easing function. + * Default duration is 500 ms, and default easing + * function is 'easeInOutQuad'. */ Timeline.prototype.focus = function(id, options) { if (!this.itemsData || id == undefined) return; @@ -299,8 +300,8 @@ Timeline.prototype.focus = function(id, options) { var middle = (start + end) / 2; var interval = Math.max((this.range.end - this.range.start), (end - start) * 1.1); - var animate = (options && options.animate !== undefined) ? options.animate : true; - this.range.setRange(middle - interval / 2, middle + interval / 2, animate); + var animation = (options && options.animation !== undefined) ? options.animation : true; + this.range.setRange(middle - interval / 2, middle + interval / 2, animation); } }; diff --git a/lib/util.js b/lib/util.js index 60d997d9..6b8ca6d4 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1319,25 +1319,6 @@ exports.binarySearchValue = function(orderedItems, target, field, sidePreference return -1; }; -/** - * Quadratic ease-in-out - * http://gizma.com/easing/ - * @param {number} t Current time - * @param {number} start Start value - * @param {number} end End value - * @param {number} duration Duration - * @returns {number} Value corresponding with current time - */ -exports.easeInOutQuad = function (t, start, end, duration) { - var change = end - start; - t /= duration/2; - if (t < 1) return change/2*t*t + start; - t--; - return -change/2 * (t*(t-2) - 1) + start; -}; - - - /* * Easing Functions - inspired from http://gizma.com/easing/ * only considering the t value for the range [0, 1] => [0, 1]