X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph.js;h=85a46c0ec65561adf92f2e849283b23e60f9fe3f;hb=8b211dd655ffd4e8404afb58b98a7442dc06c1d2;hp=2ae8cd9f77f61cad8938e1e02827848b8f0efb2e;hpb=8b83c6cc6495cf8a22ed2af673736e774a29ca1d;p=dygraphs.git diff --git a/dygraph.js b/dygraph.js index 2ae8cd9..85a46c0 100644 --- a/dygraph.js +++ b/dygraph.js @@ -135,6 +135,11 @@ Dygraph.INFO = 2; 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; @@ -175,8 +180,6 @@ Dygraph.prototype.__init__ = function(div, file, attrs) { // 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_; @@ -756,8 +759,18 @@ Dygraph.prototype.createDragInterface_ = function() { 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; @@ -773,7 +786,9 @@ Dygraph.prototype.createDragInterface_ = function() { 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); @@ -786,11 +801,18 @@ Dygraph.prototype.createDragInterface_ = function() { 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_); } }); @@ -803,12 +825,21 @@ Dygraph.prototype.createDragInterface_ = function() { 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; } @@ -826,7 +857,9 @@ Dygraph.prototype.createDragInterface_ = function() { if (isPanning) { isPanning = false; draggingDate = null; + draggingValue = null; dateRange = null; + valueRange = null; } }); @@ -895,7 +928,9 @@ Dygraph.prototype.createDragInterface_ = function() { if (isPanning) { isPanning = false; draggingDate = null; + draggingValue = null; dateRange = null; + valueRange = null; } }); @@ -914,15 +949,15 @@ Dygraph.prototype.createDragInterface_ = function() { * 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 @@ -935,23 +970,23 @@ Dygraph.prototype.drawZoomRect_ = function(direction, startX, endX, startY, endY 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), @@ -1012,9 +1047,9 @@ Dygraph.prototype.doZoomXDates_ = function(minDate, maxDate) { 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); }; @@ -1029,7 +1064,7 @@ Dygraph.prototype.doZoomY_ = function(lowY, highY) { * @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(); @@ -1055,14 +1090,16 @@ Dygraph.prototype.doUnzoom_ = function() { } 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_); } };