Browse Source

Moved Slider and Camera into separate modules

v3_develop
jos 10 years ago
parent
commit
30fb003077
4 changed files with 493 additions and 479 deletions
  1. +9
    -1
      index.js
  2. +135
    -0
      lib/graph3d/Camera.js
  3. +3
    -478
      lib/graph3d/Graph3d.js
  4. +346
    -0
      lib/graph3d/Slider.js

+ 9
- 1
index.js View File

@ -8,11 +8,19 @@ exports.DataView = require('./lib/DataView');
// Graph3d // Graph3d
exports.Graph3d = require('./lib/graph3d/Graph3d'); exports.Graph3d = require('./lib/graph3d/Graph3d');
exports.graph3d = {
Camera: require('./lib/graph3d/Camera'),
Filter: require('./lib/graph3d/Filter'),
Point2d: require('./lib/graph3d/Point2d'),
Point3d: require('./lib/graph3d/Point3d'),
Slider: require('./lib/graph3d/Slider'),
StepNumber: require('./lib/graph3d/StepNumber')
};
// Timeline // Timeline
exports.Timeline = require('./lib/timeline/Timeline'); exports.Timeline = require('./lib/timeline/Timeline');
exports.Graph2d = require('./lib/timeline/Graph2d'); exports.Graph2d = require('./lib/timeline/Graph2d');
exports.timeline= {
exports.timeline = {
DataStep: require('./lib/timeline/DataStep'), DataStep: require('./lib/timeline/DataStep'),
Range: require('./lib/timeline/Range'), Range: require('./lib/timeline/Range'),
stack: require('./lib/timeline/Stack'), stack: require('./lib/timeline/Stack'),

+ 135
- 0
lib/graph3d/Camera.js View File

@ -0,0 +1,135 @@
var Point3d = require('./Point3d');
/**
* @class Camera
* The camera is mounted on a (virtual) camera arm. The camera arm can rotate
* The camera is always looking in the direction of the origin of the arm.
* This way, the camera always rotates around one fixed point, the location
* of the camera arm.
*
* Documentation:
* http://en.wikipedia.org/wiki/3D_projection
*/
Camera = function () {
this.armLocation = new Point3d();
this.armRotation = {};
this.armRotation.horizontal = 0;
this.armRotation.vertical = 0;
this.armLength = 1.7;
this.cameraLocation = new Point3d();
this.cameraRotation = new Point3d(0.5*Math.PI, 0, 0);
this.calculateCameraOrientation();
};
/**
* Set the location (origin) of the arm
* @param {Number} x Normalized value of x
* @param {Number} y Normalized value of y
* @param {Number} z Normalized value of z
*/
Camera.prototype.setArmLocation = function(x, y, z) {
this.armLocation.x = x;
this.armLocation.y = y;
this.armLocation.z = z;
this.calculateCameraOrientation();
};
/**
* Set the rotation of the camera arm
* @param {Number} horizontal The horizontal rotation, between 0 and 2*PI.
* Optional, can be left undefined.
* @param {Number} vertical The vertical rotation, between 0 and 0.5*PI
* if vertical=0.5*PI, the graph is shown from the
* top. Optional, can be left undefined.
*/
Camera.prototype.setArmRotation = function(horizontal, vertical) {
if (horizontal !== undefined) {
this.armRotation.horizontal = horizontal;
}
if (vertical !== undefined) {
this.armRotation.vertical = vertical;
if (this.armRotation.vertical < 0) this.armRotation.vertical = 0;
if (this.armRotation.vertical > 0.5*Math.PI) this.armRotation.vertical = 0.5*Math.PI;
}
if (horizontal !== undefined || vertical !== undefined) {
this.calculateCameraOrientation();
}
};
/**
* Retrieve the current arm rotation
* @return {object} An object with parameters horizontal and vertical
*/
Camera.prototype.getArmRotation = function() {
var rot = {};
rot.horizontal = this.armRotation.horizontal;
rot.vertical = this.armRotation.vertical;
return rot;
};
/**
* Set the (normalized) length of the camera arm.
* @param {Number} length A length between 0.71 and 5.0
*/
Camera.prototype.setArmLength = function(length) {
if (length === undefined)
return;
this.armLength = length;
// Radius must be larger than the corner of the graph,
// which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the
// graph
if (this.armLength < 0.71) this.armLength = 0.71;
if (this.armLength > 5.0) this.armLength = 5.0;
this.calculateCameraOrientation();
};
/**
* Retrieve the arm length
* @return {Number} length
*/
Camera.prototype.getArmLength = function() {
return this.armLength;
};
/**
* Retrieve the camera location
* @return {Point3d} cameraLocation
*/
Camera.prototype.getCameraLocation = function() {
return this.cameraLocation;
};
/**
* Retrieve the camera rotation
* @return {Point3d} cameraRotation
*/
Camera.prototype.getCameraRotation = function() {
return this.cameraRotation;
};
/**
* Calculate the location and rotation of the camera based on the
* position and orientation of the camera arm
*/
Camera.prototype.calculateCameraOrientation = function() {
// calculate location of the camera
this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical);
this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical);
this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical);
// calculate rotation of the camera
this.cameraRotation.x = Math.PI/2 - this.armRotation.vertical;
this.cameraRotation.y = 0;
this.cameraRotation.z = -this.armRotation.horizontal;
};
module.exports = Camera;

+ 3
- 478
lib/graph3d/Graph3d.js View File

@ -4,7 +4,9 @@ var DataView = require('../DataView');
var util = require('../util'); var util = require('../util');
var Point3d = require('./Point3d'); var Point3d = require('./Point3d');
var Point2d = require('./Point2d'); var Point2d = require('./Point2d');
var Camera = require('./Camera');
var Filter = require('./Filter'); var Filter = require('./Filter');
var Slider = require('./Slider');
var StepNumber = require('./StepNumber'); var StepNumber = require('./StepNumber');
/** /**
@ -49,7 +51,7 @@ function Graph3d(container, data, options) {
this.animationInterval = 1000; // milliseconds this.animationInterval = 1000; // milliseconds
this.animationPreload = false; this.animationPreload = false;
this.camera = new Graph3d.Camera();
this.camera = new Camera();
this.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window? this.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window?
this.dataTable = null; // The original data table this.dataTable = null; // The original data table
@ -98,138 +100,6 @@ function Graph3d(container, data, options) {
// Extend Graph3d with an Emitter mixin // Extend Graph3d with an Emitter mixin
Emitter(Graph3d.prototype); Emitter(Graph3d.prototype);
/**
* @class Camera
* The camera is mounted on a (virtual) camera arm. The camera arm can rotate
* The camera is always looking in the direction of the origin of the arm.
* This way, the camera always rotates around one fixed point, the location
* of the camera arm.
*
* Documentation:
* http://en.wikipedia.org/wiki/3D_projection
*/
Graph3d.Camera = function () {
this.armLocation = new Point3d();
this.armRotation = {};
this.armRotation.horizontal = 0;
this.armRotation.vertical = 0;
this.armLength = 1.7;
this.cameraLocation = new Point3d();
this.cameraRotation = new Point3d(0.5*Math.PI, 0, 0);
this.calculateCameraOrientation();
};
/**
* Set the location (origin) of the arm
* @param {Number} x Normalized value of x
* @param {Number} y Normalized value of y
* @param {Number} z Normalized value of z
*/
Graph3d.Camera.prototype.setArmLocation = function(x, y, z) {
this.armLocation.x = x;
this.armLocation.y = y;
this.armLocation.z = z;
this.calculateCameraOrientation();
};
/**
* Set the rotation of the camera arm
* @param {Number} horizontal The horizontal rotation, between 0 and 2*PI.
* Optional, can be left undefined.
* @param {Number} vertical The vertical rotation, between 0 and 0.5*PI
* if vertical=0.5*PI, the graph is shown from the
* top. Optional, can be left undefined.
*/
Graph3d.Camera.prototype.setArmRotation = function(horizontal, vertical) {
if (horizontal !== undefined) {
this.armRotation.horizontal = horizontal;
}
if (vertical !== undefined) {
this.armRotation.vertical = vertical;
if (this.armRotation.vertical < 0) this.armRotation.vertical = 0;
if (this.armRotation.vertical > 0.5*Math.PI) this.armRotation.vertical = 0.5*Math.PI;
}
if (horizontal !== undefined || vertical !== undefined) {
this.calculateCameraOrientation();
}
};
/**
* Retrieve the current arm rotation
* @return {object} An object with parameters horizontal and vertical
*/
Graph3d.Camera.prototype.getArmRotation = function() {
var rot = {};
rot.horizontal = this.armRotation.horizontal;
rot.vertical = this.armRotation.vertical;
return rot;
};
/**
* Set the (normalized) length of the camera arm.
* @param {Number} length A length between 0.71 and 5.0
*/
Graph3d.Camera.prototype.setArmLength = function(length) {
if (length === undefined)
return;
this.armLength = length;
// Radius must be larger than the corner of the graph,
// which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the
// graph
if (this.armLength < 0.71) this.armLength = 0.71;
if (this.armLength > 5.0) this.armLength = 5.0;
this.calculateCameraOrientation();
};
/**
* Retrieve the arm length
* @return {Number} length
*/
Graph3d.Camera.prototype.getArmLength = function() {
return this.armLength;
};
/**
* Retrieve the camera location
* @return {Point3d} cameraLocation
*/
Graph3d.Camera.prototype.getCameraLocation = function() {
return this.cameraLocation;
};
/**
* Retrieve the camera rotation
* @return {Point3d} cameraRotation
*/
Graph3d.Camera.prototype.getCameraRotation = function() {
return this.cameraRotation;
};
/**
* Calculate the location and rotation of the camera based on the
* position and orientation of the camera arm
*/
Graph3d.Camera.prototype.calculateCameraOrientation = function() {
// calculate location of the camera
this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical);
this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical);
this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical);
// calculate rotation of the camera
this.cameraRotation.x = Math.PI/2 - this.armRotation.vertical;
this.cameraRotation.y = 0;
this.cameraRotation.z = -this.armRotation.horizontal;
};
/** /**
* Calculate the scaling values, dependent on the range in x, y, and z direction * Calculate the scaling values, dependent on the range in x, y, and z direction
*/ */
@ -2372,351 +2242,6 @@ Graph3d.prototype._hideTooltip = function () {
} }
}; };
/**
* @constructor Slider
*
* An html slider control with start/stop/prev/next buttons
* @param {Element} container The element where the slider will be created
* @param {Object} options Available options:
* {boolean} visible If true (default) the
* slider is visible.
*/
function Slider(container, options) {
if (container === undefined) {
throw 'Error: No container element defined';
}
this.container = container;
this.visible = (options && options.visible != undefined) ? options.visible : true;
if (this.visible) {
this.frame = document.createElement('DIV');
//this.frame.style.backgroundColor = '#E5E5E5';
this.frame.style.width = '100%';
this.frame.style.position = 'relative';
this.container.appendChild(this.frame);
this.frame.prev = document.createElement('INPUT');
this.frame.prev.type = 'BUTTON';
this.frame.prev.value = 'Prev';
this.frame.appendChild(this.frame.prev);
this.frame.play = document.createElement('INPUT');
this.frame.play.type = 'BUTTON';
this.frame.play.value = 'Play';
this.frame.appendChild(this.frame.play);
this.frame.next = document.createElement('INPUT');
this.frame.next.type = 'BUTTON';
this.frame.next.value = 'Next';
this.frame.appendChild(this.frame.next);
this.frame.bar = document.createElement('INPUT');
this.frame.bar.type = 'BUTTON';
this.frame.bar.style.position = 'absolute';
this.frame.bar.style.border = '1px solid red';
this.frame.bar.style.width = '100px';
this.frame.bar.style.height = '6px';
this.frame.bar.style.borderRadius = '2px';
this.frame.bar.style.MozBorderRadius = '2px';
this.frame.bar.style.border = '1px solid #7F7F7F';
this.frame.bar.style.backgroundColor = '#E5E5E5';
this.frame.appendChild(this.frame.bar);
this.frame.slide = document.createElement('INPUT');
this.frame.slide.type = 'BUTTON';
this.frame.slide.style.margin = '0px';
this.frame.slide.value = ' ';
this.frame.slide.style.position = 'relative';
this.frame.slide.style.left = '-100px';
this.frame.appendChild(this.frame.slide);
// create events
var me = this;
this.frame.slide.onmousedown = function (event) {me._onMouseDown(event);};
this.frame.prev.onclick = function (event) {me.prev(event);};
this.frame.play.onclick = function (event) {me.togglePlay(event);};
this.frame.next.onclick = function (event) {me.next(event);};
}
this.onChangeCallback = undefined;
this.values = [];
this.index = undefined;
this.playTimeout = undefined;
this.playInterval = 1000; // milliseconds
this.playLoop = true;
}
/**
* Select the previous index
*/
Slider.prototype.prev = function() {
var index = this.getIndex();
if (index > 0) {
index--;
this.setIndex(index);
}
};
/**
* Select the next index
*/
Slider.prototype.next = function() {
var index = this.getIndex();
if (index < this.values.length - 1) {
index++;
this.setIndex(index);
}
};
/**
* Select the next index
*/
Slider.prototype.playNext = function() {
var start = new Date();
var index = this.getIndex();
if (index < this.values.length - 1) {
index++;
this.setIndex(index);
}
else if (this.playLoop) {
// jump to the start
index = 0;
this.setIndex(index);
}
var end = new Date();
var diff = (end - start);
// calculate how much time it to to set the index and to execute the callback
// function.
var interval = Math.max(this.playInterval - diff, 0);
// document.title = diff // TODO: cleanup
var me = this;
this.playTimeout = setTimeout(function() {me.playNext();}, interval);
};
/**
* Toggle start or stop playing
*/
Slider.prototype.togglePlay = function() {
if (this.playTimeout === undefined) {
this.play();
} else {
this.stop();
}
};
/**
* Start playing
*/
Slider.prototype.play = function() {
// Test whether already playing
if (this.playTimeout) return;
this.playNext();
if (this.frame) {
this.frame.play.value = 'Stop';
}
};
/**
* Stop playing
*/
Slider.prototype.stop = function() {
clearInterval(this.playTimeout);
this.playTimeout = undefined;
if (this.frame) {
this.frame.play.value = 'Play';
}
};
/**
* Set a callback function which will be triggered when the value of the
* slider bar has changed.
*/
Slider.prototype.setOnChangeCallback = function(callback) {
this.onChangeCallback = callback;
};
/**
* Set the interval for playing the list
* @param {Number} interval The interval in milliseconds
*/
Slider.prototype.setPlayInterval = function(interval) {
this.playInterval = interval;
};
/**
* Retrieve the current play interval
* @return {Number} interval The interval in milliseconds
*/
Slider.prototype.getPlayInterval = function(interval) {
return this.playInterval;
};
/**
* Set looping on or off
* @pararm {boolean} doLoop If true, the slider will jump to the start when
* the end is passed, and will jump to the end
* when the start is passed.
*/
Slider.prototype.setPlayLoop = function(doLoop) {
this.playLoop = doLoop;
};
/**
* Execute the onchange callback function
*/
Slider.prototype.onChange = function() {
if (this.onChangeCallback !== undefined) {
this.onChangeCallback();
}
};
/**
* redraw the slider on the correct place
*/
Slider.prototype.redraw = function() {
if (this.frame) {
// resize the bar
this.frame.bar.style.top = (this.frame.clientHeight/2 -
this.frame.bar.offsetHeight/2) + 'px';
this.frame.bar.style.width = (this.frame.clientWidth -
this.frame.prev.clientWidth -
this.frame.play.clientWidth -
this.frame.next.clientWidth - 30) + 'px';
// position the slider button
var left = this.indexToLeft(this.index);
this.frame.slide.style.left = (left) + 'px';
}
};
/**
* Set the list with values for the slider
* @param {Array} values A javascript array with values (any type)
*/
Slider.prototype.setValues = function(values) {
this.values = values;
if (this.values.length > 0)
this.setIndex(0);
else
this.index = undefined;
};
/**
* Select a value by its index
* @param {Number} index
*/
Slider.prototype.setIndex = function(index) {
if (index < this.values.length) {
this.index = index;
this.redraw();
this.onChange();
}
else {
throw 'Error: index out of range';
}
};
/**
* retrieve the index of the currently selected vaue
* @return {Number} index
*/
Slider.prototype.getIndex = function() {
return this.index;
};
/**
* retrieve the currently selected value
* @return {*} value
*/
Slider.prototype.get = function() {
return this.values[this.index];
};
Slider.prototype._onMouseDown = function(event) {
// only react on left mouse button down
var leftButtonDown = event.which ? (event.which === 1) : (event.button === 1);
if (!leftButtonDown) return;
this.startClientX = event.clientX;
this.startSlideX = parseFloat(this.frame.slide.style.left);
this.frame.style.cursor = 'move';
// add event listeners to handle moving the contents
// we store the function onmousemove and onmouseup in the graph, so we can
// remove the eventlisteners lateron in the function mouseUp()
var me = this;
this.onmousemove = function (event) {me._onMouseMove(event);};
this.onmouseup = function (event) {me._onMouseUp(event);};
util.addEventListener(document, 'mousemove', this.onmousemove);
util.addEventListener(document, 'mouseup', this.onmouseup);
util.preventDefault(event);
};
Slider.prototype.leftToIndex = function (left) {
var width = parseFloat(this.frame.bar.style.width) -
this.frame.slide.clientWidth - 10;
var x = left - 3;
var index = Math.round(x / width * (this.values.length-1));
if (index < 0) index = 0;
if (index > this.values.length-1) index = this.values.length-1;
return index;
};
Slider.prototype.indexToLeft = function (index) {
var width = parseFloat(this.frame.bar.style.width) -
this.frame.slide.clientWidth - 10;
var x = index / (this.values.length-1) * width;
var left = x + 3;
return left;
};
Slider.prototype._onMouseMove = function (event) {
var diff = event.clientX - this.startClientX;
var x = this.startSlideX + diff;
var index = this.leftToIndex(x);
this.setIndex(index);
util.preventDefault();
};
Slider.prototype._onMouseUp = function (event) {
this.frame.style.cursor = 'auto';
// remove event listeners
util.removeEventListener(document, 'mousemove', this.onmousemove);
util.removeEventListener(document, 'mouseup', this.onmouseup);
util.preventDefault();
};
/**--------------------------------------------------------------------------**/ /**--------------------------------------------------------------------------**/

+ 346
- 0
lib/graph3d/Slider.js View File

@ -0,0 +1,346 @@
var util = require('../util');
/**
* @constructor Slider
*
* An html slider control with start/stop/prev/next buttons
* @param {Element} container The element where the slider will be created
* @param {Object} options Available options:
* {boolean} visible If true (default) the
* slider is visible.
*/
function Slider(container, options) {
if (container === undefined) {
throw 'Error: No container element defined';
}
this.container = container;
this.visible = (options && options.visible != undefined) ? options.visible : true;
if (this.visible) {
this.frame = document.createElement('DIV');
//this.frame.style.backgroundColor = '#E5E5E5';
this.frame.style.width = '100%';
this.frame.style.position = 'relative';
this.container.appendChild(this.frame);
this.frame.prev = document.createElement('INPUT');
this.frame.prev.type = 'BUTTON';
this.frame.prev.value = 'Prev';
this.frame.appendChild(this.frame.prev);
this.frame.play = document.createElement('INPUT');
this.frame.play.type = 'BUTTON';
this.frame.play.value = 'Play';
this.frame.appendChild(this.frame.play);
this.frame.next = document.createElement('INPUT');
this.frame.next.type = 'BUTTON';
this.frame.next.value = 'Next';
this.frame.appendChild(this.frame.next);
this.frame.bar = document.createElement('INPUT');
this.frame.bar.type = 'BUTTON';
this.frame.bar.style.position = 'absolute';
this.frame.bar.style.border = '1px solid red';
this.frame.bar.style.width = '100px';
this.frame.bar.style.height = '6px';
this.frame.bar.style.borderRadius = '2px';
this.frame.bar.style.MozBorderRadius = '2px';
this.frame.bar.style.border = '1px solid #7F7F7F';
this.frame.bar.style.backgroundColor = '#E5E5E5';
this.frame.appendChild(this.frame.bar);
this.frame.slide = document.createElement('INPUT');
this.frame.slide.type = 'BUTTON';
this.frame.slide.style.margin = '0px';
this.frame.slide.value = ' ';
this.frame.slide.style.position = 'relative';
this.frame.slide.style.left = '-100px';
this.frame.appendChild(this.frame.slide);
// create events
var me = this;
this.frame.slide.onmousedown = function (event) {me._onMouseDown(event);};
this.frame.prev.onclick = function (event) {me.prev(event);};
this.frame.play.onclick = function (event) {me.togglePlay(event);};
this.frame.next.onclick = function (event) {me.next(event);};
}
this.onChangeCallback = undefined;
this.values = [];
this.index = undefined;
this.playTimeout = undefined;
this.playInterval = 1000; // milliseconds
this.playLoop = true;
}
/**
* Select the previous index
*/
Slider.prototype.prev = function() {
var index = this.getIndex();
if (index > 0) {
index--;
this.setIndex(index);
}
};
/**
* Select the next index
*/
Slider.prototype.next = function() {
var index = this.getIndex();
if (index < this.values.length - 1) {
index++;
this.setIndex(index);
}
};
/**
* Select the next index
*/
Slider.prototype.playNext = function() {
var start = new Date();
var index = this.getIndex();
if (index < this.values.length - 1) {
index++;
this.setIndex(index);
}
else if (this.playLoop) {
// jump to the start
index = 0;
this.setIndex(index);
}
var end = new Date();
var diff = (end - start);
// calculate how much time it to to set the index and to execute the callback
// function.
var interval = Math.max(this.playInterval - diff, 0);
// document.title = diff // TODO: cleanup
var me = this;
this.playTimeout = setTimeout(function() {me.playNext();}, interval);
};
/**
* Toggle start or stop playing
*/
Slider.prototype.togglePlay = function() {
if (this.playTimeout === undefined) {
this.play();
} else {
this.stop();
}
};
/**
* Start playing
*/
Slider.prototype.play = function() {
// Test whether already playing
if (this.playTimeout) return;
this.playNext();
if (this.frame) {
this.frame.play.value = 'Stop';
}
};
/**
* Stop playing
*/
Slider.prototype.stop = function() {
clearInterval(this.playTimeout);
this.playTimeout = undefined;
if (this.frame) {
this.frame.play.value = 'Play';
}
};
/**
* Set a callback function which will be triggered when the value of the
* slider bar has changed.
*/
Slider.prototype.setOnChangeCallback = function(callback) {
this.onChangeCallback = callback;
};
/**
* Set the interval for playing the list
* @param {Number} interval The interval in milliseconds
*/
Slider.prototype.setPlayInterval = function(interval) {
this.playInterval = interval;
};
/**
* Retrieve the current play interval
* @return {Number} interval The interval in milliseconds
*/
Slider.prototype.getPlayInterval = function(interval) {
return this.playInterval;
};
/**
* Set looping on or off
* @pararm {boolean} doLoop If true, the slider will jump to the start when
* the end is passed, and will jump to the end
* when the start is passed.
*/
Slider.prototype.setPlayLoop = function(doLoop) {
this.playLoop = doLoop;
};
/**
* Execute the onchange callback function
*/
Slider.prototype.onChange = function() {
if (this.onChangeCallback !== undefined) {
this.onChangeCallback();
}
};
/**
* redraw the slider on the correct place
*/
Slider.prototype.redraw = function() {
if (this.frame) {
// resize the bar
this.frame.bar.style.top = (this.frame.clientHeight/2 -
this.frame.bar.offsetHeight/2) + 'px';
this.frame.bar.style.width = (this.frame.clientWidth -
this.frame.prev.clientWidth -
this.frame.play.clientWidth -
this.frame.next.clientWidth - 30) + 'px';
// position the slider button
var left = this.indexToLeft(this.index);
this.frame.slide.style.left = (left) + 'px';
}
};
/**
* Set the list with values for the slider
* @param {Array} values A javascript array with values (any type)
*/
Slider.prototype.setValues = function(values) {
this.values = values;
if (this.values.length > 0)
this.setIndex(0);
else
this.index = undefined;
};
/**
* Select a value by its index
* @param {Number} index
*/
Slider.prototype.setIndex = function(index) {
if (index < this.values.length) {
this.index = index;
this.redraw();
this.onChange();
}
else {
throw 'Error: index out of range';
}
};
/**
* retrieve the index of the currently selected vaue
* @return {Number} index
*/
Slider.prototype.getIndex = function() {
return this.index;
};
/**
* retrieve the currently selected value
* @return {*} value
*/
Slider.prototype.get = function() {
return this.values[this.index];
};
Slider.prototype._onMouseDown = function(event) {
// only react on left mouse button down
var leftButtonDown = event.which ? (event.which === 1) : (event.button === 1);
if (!leftButtonDown) return;
this.startClientX = event.clientX;
this.startSlideX = parseFloat(this.frame.slide.style.left);
this.frame.style.cursor = 'move';
// add event listeners to handle moving the contents
// we store the function onmousemove and onmouseup in the graph, so we can
// remove the eventlisteners lateron in the function mouseUp()
var me = this;
this.onmousemove = function (event) {me._onMouseMove(event);};
this.onmouseup = function (event) {me._onMouseUp(event);};
util.addEventListener(document, 'mousemove', this.onmousemove);
util.addEventListener(document, 'mouseup', this.onmouseup);
util.preventDefault(event);
};
Slider.prototype.leftToIndex = function (left) {
var width = parseFloat(this.frame.bar.style.width) -
this.frame.slide.clientWidth - 10;
var x = left - 3;
var index = Math.round(x / width * (this.values.length-1));
if (index < 0) index = 0;
if (index > this.values.length-1) index = this.values.length-1;
return index;
};
Slider.prototype.indexToLeft = function (index) {
var width = parseFloat(this.frame.bar.style.width) -
this.frame.slide.clientWidth - 10;
var x = index / (this.values.length-1) * width;
var left = x + 3;
return left;
};
Slider.prototype._onMouseMove = function (event) {
var diff = event.clientX - this.startClientX;
var x = this.startSlideX + diff;
var index = this.leftToIndex(x);
this.setIndex(index);
util.preventDefault();
};
Slider.prototype._onMouseUp = function (event) {
this.frame.style.cursor = 'auto';
// remove event listeners
util.removeEventListener(document, 'mousemove', this.onmousemove);
util.removeEventListener(document, 'mouseup', this.onmouseup);
util.preventDefault();
};
module.exports = Slider;

Loading…
Cancel
Save