openseadragon/src/viewport.js

285 lines
9.3 KiB
JavaScript
Raw Normal View History

(function( $ ){
$.Viewport = function(containerSize, contentSize, config) {
this.zoomPoint = null;
this.config = config;
this._containerSize = containerSize;
this._contentSize = contentSize;
this._contentAspect = contentSize.x / contentSize.y;
this._contentHeight = contentSize.y / contentSize.x;
this._centerSpringX = new $.Spring(0, this.config);
this._centerSpringY = new $.Spring(0, this.config);
this._zoomSpring = new $.Spring(1, this.config);
this._homeBounds = new $.Rect(0, 0, 1, this._contentHeight);
this.goHome(true);
this.update();
};
$.Viewport.prototype = {
_getHomeZoom: function() {
var aspectFactor = this._contentAspect / this.getAspectRatio();
return (aspectFactor >= 1) ? 1 : aspectFactor;
},
_getMinZoom: function() {
var homeZoom = this._getHomeZoom();
var zoom = this.config.minZoomImageRatio * homeZoom;
return Math.min(zoom, homeZoom);
},
_getMaxZoom: function() {
var zoom = this._contentSize.x * this.config.maxZoomPixelRatio / this._containerSize.x;
return Math.max(zoom, this._getHomeZoom());
},
getAspectRatio: function() {
return this._containerSize.x / this._containerSize.y;
},
getContainerSize: function() {
return new $.Point(this._containerSize.x, this._containerSize.y);
},
getBounds: function(current) {
var center = this.getCenter(current);
var width = 1.0 / this.getZoom(current);
var height = width / this.getAspectRatio();
return new $.Rect(center.x - width / 2.0, center.y - height / 2.0,
width, height);
},
getCenter: function(current) {
var centerCurrent = new $.Point(
this._centerSpringX.getCurrent(),
this._centerSpringY.getCurrent()
);
var centerTarget = new $.Point(
this._centerSpringX.getTarget(),
this._centerSpringY.getTarget()
);
if (current) {
return centerCurrent;
} else if (!this.zoomPoint) {
return centerTarget;
}
var oldZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
var zoom = this.getZoom();
var width = 1.0 / zoom;
var height = width / this.getAspectRatio();
var bounds = new $.Rect(centerCurrent.x - width / 2.0,
centerCurrent.y - height / 2.0, width, height);
var newZoomPixel = this.zoomPoint.minus(bounds.getTopLeft()).times(this._containerSize.x / bounds.width);
var deltaZoomPixels = newZoomPixel.minus(oldZoomPixel);
var deltaZoomPoints = deltaZoomPixels.divide(this._containerSize.x * zoom);
return centerTarget.plus(deltaZoomPoints);
},
getZoom: function(current) {
if (current) {
return this._zoomSpring.getCurrent();
} else {
return this._zoomSpring.getTarget();
}
},
applyConstraints: function(immediately) {
var actualZoom = this.getZoom();
var constrainedZoom = Math.max(Math.min(actualZoom, this._getMaxZoom()), this._getMinZoom());
if (actualZoom != constrainedZoom) {
this.zoomTo(constrainedZoom, this.zoomPoint, immediately);
}
var bounds = this.getBounds();
var visibilityRatio = this.config.visibilityRatio;
var horThres = visibilityRatio * bounds.width;
var verThres = visibilityRatio * bounds.height;
var left = bounds.x + bounds.width;
var right = 1 - bounds.x;
var top = bounds.y + bounds.height;
var bottom = this._contentHeight - bounds.y;
var dx = 0;
if (this.config.wrapHorizontal) {
} else if (left < horThres) {
dx = horThres - left;
} else if (right < horThres) {
dx = right - horThres;
}
var dy = 0;
if (this.config.wrapVertical) {
} else if (top < verThres) {
dy = verThres - top;
} else if (bottom < verThres) {
dy = bottom - verThres;
}
if (dx || dy) {
bounds.x += dx;
bounds.y += dy;
this.fitBounds(bounds, immediately);
}
},
ensureVisible: function(immediately) {
this.applyConstraints(immediately);
},
fitBounds: function(bounds, immediately) {
var aspect = this.getAspectRatio();
var center = bounds.getCenter();
var newBounds = new $.Rect(bounds.x, bounds.y, bounds.width, bounds.height);
if (newBounds.getAspectRatio() >= aspect) {
newBounds.height = bounds.width / aspect;
newBounds.y = center.y - newBounds.height / 2;
} else {
newBounds.width = bounds.height * aspect;
newBounds.x = center.x - newBounds.width / 2;
}
this.panTo(this.getCenter(true), true);
this.zoomTo(this.getZoom(true), null, true);
var oldBounds = this.getBounds();
var oldZoom = this.getZoom();
var newZoom = 1.0 / newBounds.width;
if (newZoom == oldZoom || newBounds.width == oldBounds.width) {
this.panTo(center, immediately);
return;
}
var refPoint = oldBounds.getTopLeft().times(this._containerSize.x / oldBounds.width).minus(
newBounds.getTopLeft().times(this._containerSize.x / newBounds.width)).divide(
this._containerSize.x / oldBounds.width - this._containerSize.x / newBounds.width);
this.zoomTo(newZoom, refPoint, immediately);
},
goHome: function(immediately) {
var center = this.getCenter();
if (this.config.wrapHorizontal) {
center.x = (1 + (center.x % 1)) % 1;
this._centerSpringX.resetTo(center.x);
this._centerSpringX.update();
}
if (this.config.wrapVertical) {
center.y = (this._contentHeight + (center.y % this._contentHeight)) % this._contentHeight;
this._centerSpringY.resetTo(center.y);
this._centerSpringY.update();
}
this.fitBounds(this._homeBounds, immediately);
},
panBy: function(delta, immediately) {
var center = new $.Point(this._centerSpringX.getTarget(),
this._centerSpringY.getTarget());
this.panTo(center.plus(delta), immediately);
},
panTo: function(center, immediately) {
if (immediately) {
this._centerSpringX.resetTo(center.x);
this._centerSpringY.resetTo(center.y);
} else {
this._centerSpringX.springTo(center.x);
this._centerSpringY.springTo(center.y);
}
},
zoomBy: function(factor, refPoint, immediately) {
this.zoomTo(this._zoomSpring.getTarget() * factor, refPoint, immediately);
},
zoomTo: function(zoom, refPoint, immediately) {
if (immediately) {
this._zoomSpring.resetTo(zoom);
} else {
this._zoomSpring.springTo(zoom);
}
this.zoomPoint = refPoint instanceof $.Point ? refPoint : null;
},
resize: function(newContainerSize, maintain) {
var oldBounds = this.getBounds();
var newBounds = oldBounds;
var widthDeltaFactor = newContainerSize.x / this._containerSize.x;
this._containerSize = new $.Point(newContainerSize.x, newContainerSize.y);
if (maintain) {
newBounds.width = oldBounds.width * widthDeltaFactor;
newBounds.height = newBounds.width / this.getAspectRatio();
}
this.fitBounds(newBounds, true);
},
update: function() {
var oldCenterX = this._centerSpringX.getCurrent();
var oldCenterY = this._centerSpringY.getCurrent();
var oldZoom = this._zoomSpring.getCurrent();
if (this.zoomPoint) {
var oldZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
}
this._zoomSpring.update();
if (this.zoomPoint && this._zoomSpring.getCurrent() != oldZoom) {
var newZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
var deltaZoomPixels = newZoomPixel.minus(oldZoomPixel);
var deltaZoomPoints = this.deltaPointsFromPixels(deltaZoomPixels, true);
this._centerSpringX.shiftBy(deltaZoomPoints.x);
this._centerSpringY.shiftBy(deltaZoomPoints.y);
} else {
this.zoomPoint = null;
}
this._centerSpringX.update();
this._centerSpringY.update();
return this._centerSpringX.getCurrent() != oldCenterX ||
this._centerSpringY.getCurrent() != oldCenterY ||
this._zoomSpring.getCurrent() != oldZoom;
},
deltaPixelsFromPoints: function(deltaPoints, current) {
return deltaPoints.times(this._containerSize.x * this.getZoom(current));
},
deltaPointsFromPixels: function(deltaPixels, current) {
return deltaPixels.divide(this._containerSize.x * this.getZoom(current));
},
pixelFromPoint: function(point, current) {
var bounds = this.getBounds(current);
return point.minus(bounds.getTopLeft()).times(this._containerSize.x / bounds.width);
},
pointFromPixel: function(pixel, current) {
var bounds = this.getBounds(current);
return pixel.divide(this._containerSize.x / bounds.width).plus(bounds.getTopLeft());
}
};
}( OpenSeadragon ));