dat.gui/time/scrubber.js

538 lines
11 KiB
JavaScript
Raw Normal View History

2011-02-02 18:12:55 +00:00
GUI.Controller.prototype.at = function(when, what, tween) {
2011-02-08 15:40:38 +00:00
// TODO: tween
2011-02-02 20:51:58 +00:00
this.scrubber.add(new GUI.ScrubberPoint(this.scrubber, when, what));
2011-02-02 18:12:55 +00:00
this.scrubber.render();
return this;
}
GUI.Scrubber = function(controller, timer) {
var _this = this;
this.points = [];
2011-02-02 18:12:55 +00:00
this.timer = timer;
2011-02-08 15:40:38 +00:00
this.timer.scrubbers.push(this);
2011-02-02 18:12:55 +00:00
this.controller = controller;
this.controller.scrubber = this;
2011-02-02 20:51:58 +00:00
this.playing = false;
2011-02-08 15:40:38 +00:00
var previouslyHandled;
this.getSaveObject = function() {
var pointArray = [];
for (var i in this.points) {
pointArray.push(this.points[i].getSaveObject());
}
var obj = {'points': pointArray};
return obj;
};
2011-02-02 20:51:58 +00:00
this.sort = function() {
this.points.sort(function(a,b) {
return a.time - b.time;
});
2011-02-08 15:40:38 +00:00
};
2011-02-02 20:51:58 +00:00
this.add = function(p) {
this.points.push(p);
2011-02-08 15:40:38 +00:00
this.getSaveObject();
2011-02-02 20:51:58 +00:00
this.sort();
2011-02-08 15:40:38 +00:00
};
2011-02-02 20:51:58 +00:00
var lastDown = 0;
2011-02-02 20:51:58 +00:00
this.controller.addChangeListener(function(newVal) {
2011-02-08 15:40:38 +00:00
if (!_this.playing) {
2011-02-08 15:40:38 +00:00
var v = newVal;
if (_this.controller.type == "boolean") {
v = !v; // Couldn't tell you why I have to do this.
}
if (_this.timer.activePoint == null) {
2011-02-08 15:40:38 +00:00
_this.timer.activePoint = new GUI.ScrubberPoint(_this, _this.timer.playhead, v);
_this.add(_this.timer.activePoint);
_this.render();
} else {
2011-02-08 15:40:38 +00:00
_this.timer.activePoint.value = v;
}
2011-02-02 20:51:58 +00:00
}
2011-02-02 20:51:58 +00:00
});
2011-02-02 18:12:55 +00:00
this.domElement = document.createElement('div');
this.domElement.setAttribute('class', 'guidat-time');
var canvas = document.createElement('canvas');
this.domElement.appendChild(canvas);
this.g = canvas.getContext('2d');
var width;
2011-02-02 20:51:58 +00:00
var position;
2011-02-02 18:12:55 +00:00
var height;
2011-02-03 00:01:12 +00:00
var mx, pmx;
2011-02-08 15:40:38 +00:00
this.__defineGetter__('width', function() {
2011-02-02 18:12:55 +00:00
return width;
});
2011-02-08 15:40:38 +00:00
this.__defineGetter__('height', function() {
2011-02-02 18:12:55 +00:00
return height;
});
controller.domElement.insertBefore(this.domElement, controller.propertyNameElement.nextSibling);
this.render = function() {
// TODO: if visible ...
2011-02-08 15:40:38 +00:00
2011-02-02 18:12:55 +00:00
_this.g.clearRect(0, 0, width, height);
2011-02-08 15:40:38 +00:00
// Draw 0
if (_this.timer.windowMin < 0) {
var x = GUI.map(0, _this.timer.windowMin, _this.timer.windowMin+_this.timer.windowWidth, 0, width);
_this.g.fillStyle = '#000';
_this.g.fillRect(0, 0, x, height-1);
}
// Draw ticks
if (_this.timer.useSnap) {
_this.g.lineWidth = 1;
for (var i = _this.timer.snap(_this.timer.windowMin); i < _this.timer.windowMin+_this.timer.windowWidth; i+= _this.timer.snapIncrement) {
if (i == 0) continue;
var x = Math.round(GUI.map(i, _this.timer.windowMin, _this.timer.windowMin+_this.timer.windowWidth, 0, width))+0.5;
if (i < 0) {
_this.g.strokeStyle = '#111111';
} else {
_this.g.strokeStyle = '#282828';
}
_this.g.beginPath();
_this.g.moveTo(x, 0);
_this.g.lineTo(x, height-1);
_this.g.stroke();
}
}
// Draw points
for (var i in _this.points) {
_this.points[i].render();
2011-02-02 18:12:55 +00:00
}
// Draw playhead
2011-02-08 15:40:38 +00:00
_this.g.strokeStyle = '#ff0024';
_this.g.lineWidth = 1;
2011-02-02 18:12:55 +00:00
var t = Math.round(GUI.map(_this.timer.playhead, _this.timer.windowMin, _this.timer.windowMin+_this.timer.windowWidth, 0, width))+0.5;
_this.g.beginPath();
_this.g.moveTo(t, 0);
_this.g.lineTo(t, height);
_this.g.stroke();
}
this.render();
var onResize = function() {
canvas.width = width = _this.domElement.offsetWidth;
canvas.height = height = _this.domElement.offsetHeight;
2011-02-02 20:51:58 +00:00
position = GUI.getOffset(canvas);
2011-02-02 18:12:55 +00:00
_this.render();
};
window.addEventListener('resize', function(e) {
onResize();
}, false);
var scrubPan = function() {
var t = _this.timer.playhead;
var tmin = _this.timer.windowMin + _this.timer.windowWidth/5;
var tmax = _this.timer.windowMin + _this.timer.windowWidth - _this.timer.windowWidth/5;
if (t < tmin) {
_this.timer.windowMin += GUI.map(t, _this.timer.windowMin, tmin, -_this.timer.windowWidth/50, 0);
}
if (t > tmax) {
_this.timer.windowMin += 0;
_this.timer.windowMin += GUI.map(t, tmax, _this.timer.windowMin+_this.timer.windowWidth, 0,_this.timer.windowWidth/50);
}
}
2011-02-08 15:40:38 +00:00
2011-02-02 20:51:58 +00:00
var scrub = function(e) {
var t = GUI.map(e.pageX, position.left, position.left+width, _this.timer.windowMin, _this.timer.windowMin+_this.timer.windowWidth);
2011-02-08 15:40:38 +00:00
_this.timer.playhead = _this.timer.snap(t);
scrubPan();
2011-02-03 00:01:12 +00:00
}
2011-02-08 15:40:38 +00:00
var pan = function(e) {
2011-02-03 00:01:12 +00:00
mx = e.pageX;
2011-02-08 15:40:38 +00:00
var t = GUI.map(mx - pmx, 0, width, 0, _this.timer.windowWidth);
_this.timer.windowMin -= t;
2011-02-03 00:01:12 +00:00
pmx = mx;
2011-02-08 15:40:38 +00:00
}
var dragActive = function(e) {
var t = GUI.map(e.pageX, position.left, position.left+width, _this.timer.windowMin, _this.timer.windowMin+_this.timer.windowWidth);
_this.timer.activePoint.time = _this.timer.snap(t);
_this.timer.playhead = _this.timer.snap(t);
pmx = mx;
2011-02-03 00:01:12 +00:00
_this.sort();
2011-02-02 20:51:58 +00:00
}
canvas.addEventListener('mousedown', function(e) {
2011-02-08 15:40:38 +00:00
if (false) {
e.preventDefault();
document.addEventListener('mousemove', pan, false);
return false;
}
2011-02-08 15:40:38 +00:00
var thisDown = GUI.millis();
// Double click creates a keyframe
if (thisDown - lastDown < 300) {
_this.timer.activePoint = new GUI.ScrubberPoint(_this, _this.timer.playhead, _this.controller.getValue());
_this.add(_this.timer.activePoint);
_this.render();
2011-02-08 15:40:38 +00:00
// A regular click COULD select a point ...
} else if (_this.timer.hoverPoint != null) {
2011-02-02 20:51:58 +00:00
_this.timer.activePoint = _this.timer.hoverPoint;
2011-02-08 15:40:38 +00:00
_this.timer.playhead = _this.timer.snap(_this.timer.activePoint.time);
2011-02-03 00:01:12 +00:00
pmx = mx = e.pageX;
2011-02-08 15:40:38 +00:00
document.addEventListener('mousemove', dragActive, false);
2011-02-03 00:01:12 +00:00
2011-02-08 15:40:38 +00:00
// Or we could just be trying to place the playhead/scrub.
2011-02-02 20:51:58 +00:00
} else {
_this.timer.activePoint = null;
2011-02-03 00:01:12 +00:00
_this.timer.hoverPoint = null;
2011-02-02 20:51:58 +00:00
scrub(e);
2011-02-08 15:40:38 +00:00
document.body.style.cursor = 'text';
_this.timer.pause();
2011-02-08 15:40:38 +00:00
pmx = mx = e.pageX;
2011-02-02 20:51:58 +00:00
document.addEventListener('mousemove', scrub, false);
_this.render();
}
lastDown = thisDown;
2011-02-02 20:51:58 +00:00
}, false);
2011-02-08 15:40:38 +00:00
canvas.addEventListener('mousewheel', function(e) {
e.preventDefault();
var dx = e.wheelDeltaX*4;
var dy = e.wheelDeltaY*4;
_this.timer.windowWidth -= dy;
_this.timer.windowMin += dy/2 + dx;
return false;
}, false);
2011-02-02 20:51:58 +00:00
canvas.addEventListener('mousemove', function(e) {
_this.timer.hoverPoint = null;
for (var i in _this.points) {
var cur = _this.points[i];
if (cur.isHovering(e.pageX-position.left)) {
_this.timer.hoverPoint = cur;
}
}
2011-02-03 00:45:12 +00:00
if (_this.timer.hoverPoint == null) {
2011-02-08 15:40:38 +00:00
document.body.style.cursor = 'pointer';
2011-02-03 00:45:12 +00:00
} else {
2011-02-08 15:40:38 +00:00
document.body.style.cursor = 'auto';
2011-02-03 00:45:12 +00:00
}
2011-02-02 20:51:58 +00:00
_this.render();
});
document.addEventListener('mouseup', function() {
2011-02-08 15:40:38 +00:00
document.body.style.cursor = 'auto';
document.removeEventListener('mousemove', dragActive, false);
2011-02-02 20:51:58 +00:00
document.removeEventListener('mousemove', scrub, false);
2011-02-08 15:40:38 +00:00
document.removeEventListener('mousemove', pan, false);
2011-02-02 20:51:58 +00:00
}, false);
2011-02-02 18:12:55 +00:00
onResize();
this.timer.addPlayListener(this.render);
2011-02-08 15:40:38 +00:00
var handlePoint = function(point) {
if (point != previouslyHandled) {
previouslyHandled = point;
_this.controller.setValue(point.value);
}
};
2011-02-02 18:12:55 +00:00
var onPlayChange = function(curTime, prevTime) {
2011-02-08 15:40:38 +00:00
if (_this.points.length == 0) return;
2011-02-08 15:40:38 +00:00
_this.playing = true;
2011-02-08 15:40:38 +00:00
if (_this.controller.type == 'function') {
for (var i = 0; i < _this.points.length; i++) {
var t = _this.points[i].time;
if ((curTime > prevTime && prevTime < t && t < curTime) ||
(curTime < prevTime && prevTime > t && t > curTime)) {
_this.controller.getValue().call(this);
}
}
2011-02-08 15:40:38 +00:00
} else {
var prev = undefined, next = undefined;
// Find "surrounding" points.
for (var i = 0; i < _this.points.length; i++) {
var t = _this.points[i].time;
2011-02-08 15:40:38 +00:00
if (t > curTime) {
if (i == 0) {
prev = null;
next = _this.points[i];
break;
} else {
2011-02-03 00:01:12 +00:00
2011-02-08 15:40:38 +00:00
prev = _this.points[i-1];
next = _this.points[i];
break;
}
2011-02-03 00:01:12 +00:00
}
2011-02-08 15:40:38 +00:00
}
2011-02-08 15:40:38 +00:00
if (next == undefined) {
prev = _this.points[_this.points.length-1];
next = null;
2011-02-02 18:12:55 +00:00
}
2011-02-08 15:40:38 +00:00
console.log(next, prev);
2011-02-08 15:40:38 +00:00
if (next != null & prev != null) {
2011-02-08 15:40:38 +00:00
if (_this.controller.type == 'number') {
var t = prev.tween(GUI.map(curTime, prev.time, next.time, 0, 1));
_this.controller.setValue(GUI.map(t, 0, 1, prev.value, next.value));
2011-02-08 15:40:38 +00:00
} else {
handlePoint(prev);
}
2011-02-08 15:40:38 +00:00
} else if (next != null) {
handlePoint(next);
} else if (prev != null) {
handlePoint(prev);
}
}
2011-02-02 18:12:55 +00:00
2011-02-02 20:51:58 +00:00
_this.playing = false;
2011-02-02 18:12:55 +00:00
};
this.timer.addPlayListener(onPlayChange);
this.timer.addWindowListener(this.render);
};
GUI.ScrubberPoint = function(scrubber, time, value) {
2011-02-03 00:01:12 +00:00
var _this = this;
2011-02-02 18:12:55 +00:00
var g = scrubber.g;
var timer = scrubber.timer;
var type = scrubber.controller.type;
2011-02-02 20:51:58 +00:00
var x, y;
this.hold = false;
2011-02-08 15:40:38 +00:00
var val;
this.__defineSetter__("value", function(v) {
val = v;
scrubber.render();
});
2011-02-02 20:51:58 +00:00
this.value = value;
2011-02-08 15:40:38 +00:00
this.__defineGetter__("value", function() {
return val;
});
2011-02-02 20:51:58 +00:00
var barSize = 4;
var rectSize = 7;
2011-02-08 15:40:38 +00:00
var c1 = '#ffd800';
var c2 = '#ff9000';
this.getSaveObject = function() {
var obj = { 'value': _this.value, 'time': time };
if (this.hold) {
obj.hold = true;
}
// TODO: save tweens
return obj;
};
2011-02-02 18:12:55 +00:00
this.tween = function(t) {
return t;
};
2011-02-02 18:12:55 +00:00
2011-02-03 00:01:12 +00:00
this.remove = function() {
scrubber.points.splice(scrubber.points.indexOf(this), 1);
scrubber.render();
};
2011-02-02 20:51:58 +00:00
this.isHovering = function(xx) {
return xx >= x-rectSize/2 && xx <= x+rectSize/2;
};
2011-02-02 18:12:55 +00:00
2011-02-08 15:40:38 +00:00
this.__defineGetter__('next', function() {
if (scrubber.points.length <= 1) {
return null;
2011-02-02 18:12:55 +00:00
}
var i = scrubber.points.indexOf(this);
if (i + 1 >= scrubber.points.length) {
return null;
}
2011-02-02 18:12:55 +00:00
return scrubber.points[i+1];
2011-02-02 20:51:58 +00:00
2011-02-02 18:12:55 +00:00
});
2011-02-08 15:40:38 +00:00
this.__defineGetter__('time', function() {
return time;
2011-02-02 18:12:55 +00:00
});
2011-02-08 15:40:38 +00:00
this.__defineSetter__('time', function(s) {
2011-02-03 00:01:12 +00:00
time = s;
});
2011-02-02 18:12:55 +00:00
this.render = function() {
2011-02-02 18:12:55 +00:00
2011-02-02 20:51:58 +00:00
x = GUI.map(time, timer.windowMin, timer.windowMin+timer.windowWidth, 0, 1);
if (x >= 0 && x <= 1) {
x = Math.round(GUI.map(x, 0, 1, 0, scrubber.width));
2011-02-03 00:01:12 +00:00
} else {
return;
2011-02-02 18:12:55 +00:00
}
2011-02-02 20:51:58 +00:00
y = scrubber.height/2;
2011-02-03 00:01:12 +00:00
if (scrubber.timer.activePoint == this) {
2011-02-08 15:40:38 +00:00
g.fillStyle = '#ffd800'; //
2011-02-03 00:01:12 +00:00
} else if (scrubber.timer.hoverPoint == this) {
2011-02-08 15:40:38 +00:00
g.fillStyle = '#999';
2011-02-03 00:01:12 +00:00
} else {
2011-02-08 15:40:38 +00:00
g.fillStyle = '#ccc';
2011-02-03 00:01:12 +00:00
}
switch (type) {
2011-02-08 15:40:38 +00:00
case 'boolean':
2011-02-03 03:53:46 +00:00
g.save();
g.translate(x, y-0.5);
if (this.value) {
g.strokeStyle = g.fillStyle;
g.lineWidth = barSize;
g.beginPath();
g.arc(0, 0, barSize, 0, Math.PI*2, false);
g.stroke();
} else {
g.rotate(Math.PI/4);
g.fillRect(-barSize/2, -barSize*3.5/2, barSize, barSize*3.5);
g.rotate(Math.PI/2);
g.fillRect(-barSize/2, -barSize*3.5/2, barSize, barSize*3.5);
}
g.restore();
break;
2011-02-08 15:40:38 +00:00
case 'number':
g.save();
var n = this.next;
if (n != null) {
var nx = GUI.constrain(GUI.map(n.time, timer.windowMin, timer.windowMin+timer.windowWidth, 0, 1));
2011-02-08 15:40:38 +00:00
nx = GUI.constrain(GUI.map(nx, 0, 1, 0, scrubber.width));
g.lineWidth = rectSize/2
2011-02-08 15:40:38 +00:00
g.strokeStyle='#222';
g.beginPath();
g.moveTo(nx, y);
g.lineTo(x, y);
g.stroke();
}
g.translate(x, y);
g.rotate(Math.PI/4);
2011-02-03 00:01:12 +00:00
// g.fillStyle = c1;
g.fillRect(-rectSize/2, -rectSize/2, rectSize, rectSize);
g.restore();
break;
default:
g.save();
g.translate(x-barSize/2, 0);
2011-02-02 20:51:58 +00:00
//g.fillStyle = c1;
g.fillRect(0, 0, barSize/2, scrubber.height-1);
2011-02-02 20:51:58 +00:00
//g.fillStyle = c2;
g.fillRect(barSize/2, 0, barSize/2, scrubber.height-1);
g.restore();
2011-02-02 18:12:55 +00:00
}
2011-02-02 18:12:55 +00:00
}
}