Dygraph.WARNING = 3;
Dygraph.ERROR = 3;
+// Directions for panning and zooming. Use bit operations when combined
+// values are possible.
+Dygraph.HORIZONTAL = 1;
+Dygraph.VERTICAL = 2;
+
// Used for initializing annotation CSS rules only once.
Dygraph.addedAnnotationCSS = false;
// locally-stored copy of the attribute. valueWindow starts off the same as
// valueRange but is impacted by zoom or pan effects. valueRange is kept
// around to restore the original value back to valueRange.
- // TODO(konigsberg): There are no vertical pan effects yet, but valueWindow
- // would change accordingly.
this.valueRange_ = attrs.valueRange || null;
this.valueWindow_ = this.valueRange_;
var prevEndX = null;
var prevEndY = null;
var prevDragDirection = null;
+
+ // draggingDate and draggingValue represent the [date,value] point on the
+ // graph at which the mouse was pressed. As the mouse moves while panning,
+ // the viewport must pan so that the mouse position points to
+ // [draggingDate, draggingValue]
var draggingDate = null;
+ var draggingValue = null;
+
+ // The range in second/value units that the viewport encompasses during a
+ // panning operation.
var dateRange = null;
+ var valueRange = null;
// Utility function to convert page-wide coordinates to canvas coords
var px = 0;
var xDelta = Math.abs(dragStartX - dragEndX);
var yDelta = Math.abs(dragStartY - dragEndY);
- var dragDirection = (xDelta < yDelta) ? "V" : "H";
+
+ // drag direction threshold for y axis is twice as large as x axis
+ var dragDirection = (xDelta < yDelta / 2) ? Dygraph.VERTICAL : Dygraph.HORIZONTAL;
self.drawZoomRect_(dragDirection, dragStartX, dragEndX, dragStartY, dragEndY,
prevDragDirection, prevEndX, prevEndY);
dragEndY = getY(event);
// Want to have it so that:
- // 1. draggingDate appears at dragEndX
+ // 1. draggingDate appears at dragEndX, draggingValue appears at dragEndY.
// 2. daterange = (dateWindow_[1] - dateWindow_[0]) is unaltered.
+ // 3. draggingValue appears at dragEndY.
+ // 4. valueRange is unaltered.
- self.dateWindow_[0] = draggingDate - (dragEndX / self.width_) * dateRange;
- self.dateWindow_[1] = self.dateWindow_[0] + dateRange;
+ var minDate = draggingDate - (dragEndX / self.width_) * dateRange;
+ var maxDate = minDate + dateRange;
+ self.dateWindow_ = [minDate, maxDate];
+
+ var maxValue = draggingValue + (dragEndY / self.height_) * valueRange;
+ var minValue = maxValue - valueRange;
+ self.valueWindow_ = [ minValue, maxValue ];
self.drawGraph_(self.rawData_);
}
});
dragStartY = getY(event);
if (event.altKey || event.shiftKey) {
- // TODO(konigsberg): Support vertical panning.
- if (!self.dateWindow_) return; // have to be zoomed in to pan.
+ // have to be zoomed in to pan.
+ if (!self.dateWindow_ && !self.valueWindow_) return;
+
isPanning = true;
- dateRange = self.dateWindow_[1] - self.dateWindow_[0];
+ var xRange = self.xAxisRange();
+ dateRange = xRange[1] - xRange[0];
+ var yRange = self.yAxisRange();
+ valueRange = yRange[1] - yRange[0];
+
+ // TODO(konigsberg): Switch from all this math to toDataCoords?
+ // Seems to work for the dragging value.
draggingDate = (dragStartX / self.width_) * dateRange +
- self.dateWindow_[0];
+ xRange[0];
+ var r = self.toDataCoords(null, dragStartY);
+ draggingValue = r[1];
} else {
isZooming = true;
}
if (isPanning) {
isPanning = false;
draggingDate = null;
+ draggingValue = null;
dateRange = null;
+ valueRange = null;
}
});
if (isPanning) {
isPanning = false;
draggingDate = null;
+ draggingValue = null;
dateRange = null;
+ valueRange = null;
}
});
* avoid extra redrawing, but it's tricky to avoid interactions with the status
* dots.
*
- * @param {String} direction the direction of the zoom rectangle. "H" and "V"
- * for Horizontal and Vertical.
+ * @param {Number} direction the direction of the zoom rectangle. Acceptable
+ * values are Dygraph.HORIZONTAL and Dygraph.VERTICAL.
* @param {Number} startX The X position where the drag started, in canvas
* coordinates.
* @param {Number} endX The current X position of the drag, in canvas coords.
* @param {Number} startY The Y position where the drag started, in canvas
* coordinates.
* @param {Number} endY The current Y position of the drag, in canvas coords.
- * @param {String} prevDirection the value of direction on the previous call to
+ * @param {Number} prevDirection the value of direction on the previous call to
* this function. Used to avoid excess redrawing
* @param {Number} prevEndX The value of endX on the previous call to this
* function. Used to avoid excess redrawing
var ctx = this.canvas_.getContext("2d");
// Clean up from the previous rect if necessary
- if (prevDirection == "H") {
+ if (prevDirection == Dygraph.HORIZONTAL) {
ctx.clearRect(Math.min(startX, prevEndX), 0,
Math.abs(startX - prevEndX), this.height_);
- } else if (prevDirection == "V"){
+ } else if (prevDirection == Dygraph.VERTICAL){
ctx.clearRect(0, Math.min(startY, prevEndY),
this.width_, Math.abs(startY - prevEndY));
}
// Draw a light-grey rectangle to show the new viewing area
- if (direction == "H") {
+ if (direction == Dygraph.HORIZONTAL) {
if (endX && startX) {
ctx.fillStyle = "rgba(128,128,128,0.33)";
ctx.fillRect(Math.min(startX, endX), 0,
Math.abs(endX - startX), this.height_);
}
}
- if (direction == "V") {
+ if (direction == Dygraph.VERTICAL) {
if (endY && startY) {
ctx.fillStyle = "rgba(128,128,128,0.33)";
ctx.fillRect(0, Math.min(startY, endY),
Dygraph.prototype.doZoomY_ = function(lowY, highY) {
// Find the highest and lowest values in pixel range.
var r = this.toDataCoords(null, lowY);
- var minValue = r[1];
- r = this.toDataCoords(null, highY);
var maxValue = r[1];
+ r = this.toDataCoords(null, highY);
+ var minValue = r[1];
this.doZoomYValues_(minValue, maxValue);
};
* @private
*/
Dygraph.prototype.doZoomYValues_ = function(minValue, maxValue) {
- this.valueWindow_ = [maxValue, minValue];
+ this.valueWindow_ = [minValue, maxValue];
this.drawGraph_(this.rawData_);
if (this.attr_("zoomCallback")) {
var xRange = this.xAxisRange();
}
if (dirty) {
+ // Putting the drawing operation before the callback because it resets
+ // yAxisRange.
+ this.drawGraph_(this.rawData_);
if (this.attr_("zoomCallback")) {
var minDate = this.rawData_[0][0];
var maxDate = this.rawData_[this.rawData_.length - 1][0];
- var minValue = this.xAxisRange()[0];
- var maxValue = this.xAxisRange()[1];
+ var minValue = this.yAxisRange()[0];
+ var maxValue = this.yAxisRange()[1];
this.attr_("zoomCallback")(minDate, maxDate, minValue, maxValue);
}
- this.drawGraph_(this.rawData_);
}
};