2011-12-06 07:50:25 +04:00
|
|
|
|
|
|
|
(function( $ ){
|
|
|
|
|
|
|
|
$.Viewport = function(containerSize, contentSize, config) {
|
2012-01-05 04:45:47 +04:00
|
|
|
//TODO: this.config is something that should go away but currently the
|
|
|
|
// Drawer references the viewport.config
|
2011-12-06 07:50:25 +04:00
|
|
|
this.config = config;
|
2012-01-05 04:45:47 +04:00
|
|
|
this.zoomPoint = null;
|
2012-01-05 03:14:20 +04:00
|
|
|
this.containerSize = containerSize;
|
|
|
|
this.contentSize = contentSize;
|
|
|
|
this.contentAspect = contentSize.x / contentSize.y;
|
|
|
|
this.contentHeight = contentSize.y / contentSize.x;
|
|
|
|
this.centerSpringX = new $.Spring({
|
|
|
|
initial: 0,
|
|
|
|
springStiffness: config.springStiffness,
|
|
|
|
animationTime: config.animationTime
|
|
|
|
});
|
|
|
|
this.centerSpringY = new $.Spring({
|
|
|
|
initial: 0,
|
|
|
|
springStiffness: config.springStiffness,
|
|
|
|
animationTime: config.animationTime
|
|
|
|
});
|
|
|
|
this.zoomSpring = new $.Spring({
|
|
|
|
initial: 1,
|
|
|
|
springStiffness: config.springStiffness,
|
|
|
|
animationTime: config.animationTime
|
|
|
|
});
|
|
|
|
this.minZoomImageRatio = config.minZoomImageRatio;
|
|
|
|
this.maxZoomPixelRatio = config.maxZoomPixelRatio;
|
|
|
|
this.visibilityRatio = config.visibilityRatio;
|
|
|
|
this.wrapHorizontal = config.wrapHorizontal;
|
|
|
|
this.wrapVertical = config.wrapVertical;
|
2012-01-05 04:45:47 +04:00
|
|
|
this.homeBounds = new $.Rect( 0, 0, 1, this.contentHeight );
|
|
|
|
this.goHome( true );
|
2011-12-06 07:50:25 +04:00
|
|
|
this.update();
|
|
|
|
};
|
|
|
|
|
|
|
|
$.Viewport.prototype = {
|
2012-01-05 03:14:20 +04:00
|
|
|
getHomeZoom: function() {
|
|
|
|
var aspectFactor = this.contentAspect / this.getAspectRatio();
|
2011-12-06 07:50:25 +04:00
|
|
|
return (aspectFactor >= 1) ? 1 : aspectFactor;
|
|
|
|
},
|
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
getMinZoom: function() {
|
|
|
|
var homeZoom = this.getHomeZoom()
|
|
|
|
zoom = this.minZoomImageRatio * homeZoom;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
|
|
|
return Math.min(zoom, homeZoom);
|
|
|
|
},
|
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
getMaxZoom: function() {
|
2012-01-18 03:30:41 +04:00
|
|
|
var zoom =
|
|
|
|
this.contentSize.x *
|
|
|
|
this.maxZoomPixelRatio /
|
|
|
|
this.containerSize.x;
|
2012-01-05 03:14:20 +04:00
|
|
|
return Math.max(zoom, this.getHomeZoom());
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
2012-01-05 03:14:20 +04:00
|
|
|
|
2011-12-06 07:50:25 +04:00
|
|
|
getAspectRatio: function() {
|
2012-01-05 03:14:20 +04:00
|
|
|
return this.containerSize.x / this.containerSize.y;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
2012-01-05 03:14:20 +04:00
|
|
|
|
2011-12-06 07:50:25 +04:00
|
|
|
getContainerSize: function() {
|
2012-01-05 03:14:20 +04:00
|
|
|
return new $.Point(this.containerSize.x, this.containerSize.y);
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
2012-01-05 04:45:47 +04:00
|
|
|
getBounds: function( current ) {
|
2012-01-05 03:14:20 +04:00
|
|
|
var center = this.getCenter(current),
|
|
|
|
width = 1.0 / this.getZoom(current),
|
|
|
|
height = width / this.getAspectRatio();
|
|
|
|
|
|
|
|
return new $.Rect(
|
|
|
|
center.x - width / 2.0,
|
|
|
|
center.y - height / 2.0,
|
|
|
|
width,
|
|
|
|
height
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
2012-01-05 04:45:47 +04:00
|
|
|
getCenter: function( current ) {
|
2011-12-17 02:56:38 +04:00
|
|
|
var centerCurrent = new $.Point(
|
2012-01-05 03:14:20 +04:00
|
|
|
this.centerSpringX.current.value,
|
|
|
|
this.centerSpringY.current.value
|
|
|
|
),
|
|
|
|
centerTarget = new $.Point(
|
|
|
|
this.centerSpringX.target.value,
|
|
|
|
this.centerSpringY.target.value
|
|
|
|
),
|
|
|
|
oldZoomPixel,
|
|
|
|
zoom,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
bounds,
|
|
|
|
newZoomPixel,
|
|
|
|
deltaZoomPixels,
|
|
|
|
deltaZoomPoints;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
|
|
|
if (current) {
|
|
|
|
return centerCurrent;
|
|
|
|
} else if (!this.zoomPoint) {
|
|
|
|
return centerTarget;
|
|
|
|
}
|
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
oldZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
zoom = this.getZoom();
|
|
|
|
width = 1.0 / zoom;
|
|
|
|
height = width / this.getAspectRatio();
|
|
|
|
bounds = new $.Rect(
|
|
|
|
centerCurrent.x - width / 2.0,
|
|
|
|
centerCurrent.y - height / 2.0,
|
|
|
|
width,
|
|
|
|
height
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
newZoomPixel = this.zoomPoint.minus(
|
|
|
|
bounds.getTopLeft()
|
|
|
|
).times(
|
|
|
|
this.containerSize.x / bounds.width
|
|
|
|
);
|
|
|
|
deltaZoomPixels = newZoomPixel.minus( oldZoomPixel );
|
|
|
|
deltaZoomPoints = deltaZoomPixels.divide( this.containerSize.x * zoom );
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
return centerTarget.plus( deltaZoomPoints );
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
2012-01-05 04:45:47 +04:00
|
|
|
getZoom: function( current ) {
|
|
|
|
if ( current ) {
|
2012-01-05 03:14:20 +04:00
|
|
|
return this.zoomSpring.current.value;
|
2011-12-06 07:50:25 +04:00
|
|
|
} else {
|
2012-01-05 03:14:20 +04:00
|
|
|
return this.zoomSpring.target.value;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
|
2012-01-05 04:45:47 +04:00
|
|
|
applyConstraints: function( immediately ) {
|
|
|
|
var actualZoom = this.getZoom(),
|
|
|
|
constrainedZoom = Math.max(
|
|
|
|
Math.min( actualZoom, this.getMaxZoom() ),
|
|
|
|
this.getMinZoom()
|
|
|
|
),
|
|
|
|
bounds,
|
|
|
|
horizontalThreshold,
|
|
|
|
verticalThreshold,
|
|
|
|
left,
|
|
|
|
right,
|
|
|
|
top,
|
|
|
|
bottom,
|
|
|
|
dx = 0,
|
|
|
|
dy = 0;
|
|
|
|
|
|
|
|
if ( actualZoom != constrainedZoom ) {
|
|
|
|
this.zoomTo( constrainedZoom, this.zoomPoint, immediately );
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2012-01-05 04:45:47 +04:00
|
|
|
bounds = this.getBounds();
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-05 04:45:47 +04:00
|
|
|
horizontalThreshold = this.visibilityRatio * bounds.width;
|
|
|
|
verticalThreshold = this.visibilityRatio * bounds.height;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-05 04:45:47 +04:00
|
|
|
left = bounds.x + bounds.width;
|
|
|
|
right = 1 - bounds.x;
|
|
|
|
top = bounds.y + bounds.height;
|
|
|
|
bottom = this.contentHeight - bounds.y;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
if ( this.wrapHorizontal ) {
|
2012-01-05 04:45:47 +04:00
|
|
|
//do nothing
|
|
|
|
} else if ( left < horizontalThreshold ) {
|
|
|
|
dx = horizontalThreshold - left;
|
|
|
|
} else if ( right < horizontalThreshold ) {
|
|
|
|
dx = right - horizontalThreshold;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
if ( this.wrapVertical ) {
|
2012-01-05 04:45:47 +04:00
|
|
|
//do nothing
|
|
|
|
} else if ( top < verticalThreshold ) {
|
|
|
|
dy = verticalThreshold - top;
|
|
|
|
} else if ( bottom < verticalThreshold ) {
|
|
|
|
dy = bottom - verticalThreshold;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2012-01-05 04:45:47 +04:00
|
|
|
if ( dx || dy ) {
|
2011-12-06 07:50:25 +04:00
|
|
|
bounds.x += dx;
|
|
|
|
bounds.y += dy;
|
2012-01-05 04:45:47 +04:00
|
|
|
this.fitBounds( bounds, immediately );
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-01-05 04:45:47 +04:00
|
|
|
ensureVisible: function( immediately ) {
|
|
|
|
this.applyConstraints( immediately );
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
2012-01-05 04:45:47 +04:00
|
|
|
fitBounds: function( bounds, immediately ) {
|
2012-01-05 03:14:20 +04:00
|
|
|
var aspect = this.getAspectRatio(),
|
|
|
|
center = bounds.getCenter(),
|
|
|
|
newBounds = new $.Rect(
|
|
|
|
bounds.x,
|
|
|
|
bounds.y,
|
|
|
|
bounds.width,
|
|
|
|
bounds.height
|
|
|
|
),
|
|
|
|
oldBounds,
|
|
|
|
oldZoom,
|
|
|
|
newZoom,
|
2012-01-05 04:45:47 +04:00
|
|
|
referencePoint;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
|
|
|
if (newBounds.getAspectRatio() >= aspect) {
|
|
|
|
newBounds.height = bounds.width / aspect;
|
2012-01-05 04:45:47 +04:00
|
|
|
newBounds.y = center.y - newBounds.height / 2;
|
2011-12-06 07:50:25 +04:00
|
|
|
} else {
|
|
|
|
newBounds.width = bounds.height * aspect;
|
2012-01-05 04:45:47 +04:00
|
|
|
newBounds.x = center.x - newBounds.width / 2;
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
this.panTo(this.getCenter(true), true);
|
|
|
|
this.zoomTo(this.getZoom(true), null, true);
|
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
oldBounds = this.getBounds();
|
|
|
|
oldZoom = this.getZoom();
|
|
|
|
newZoom = 1.0 / newBounds.width;
|
2011-12-06 07:50:25 +04:00
|
|
|
if (newZoom == oldZoom || newBounds.width == oldBounds.width) {
|
2012-01-05 03:14:20 +04:00
|
|
|
this.panTo( center, immediately );
|
2011-12-06 07:50:25 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-01-05 04:45:47 +04:00
|
|
|
referencePoint = oldBounds.getTopLeft().times(
|
2012-01-05 03:14:20 +04:00
|
|
|
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
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
|
|
|
|
|
2012-01-05 04:45:47 +04:00
|
|
|
this.zoomTo( newZoom, referencePoint, immediately );
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
goHome: function(immediately) {
|
|
|
|
var center = this.getCenter();
|
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
if ( this.wrapHorizontal ) {
|
2011-12-06 07:50:25 +04:00
|
|
|
center.x = (1 + (center.x % 1)) % 1;
|
2012-01-05 03:14:20 +04:00
|
|
|
this.centerSpringX.resetTo(center.x);
|
|
|
|
this.centerSpringX.update();
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
if ( this.wrapVertical ) {
|
|
|
|
center.y = (this.contentHeight + (center.y % this.contentHeight)) % this.contentHeight;
|
|
|
|
this.centerSpringY.resetTo(center.y);
|
|
|
|
this.centerSpringY.update();
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
this.fitBounds(this.homeBounds, immediately);
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
panBy: function(delta, immediately) {
|
2012-01-05 03:14:20 +04:00
|
|
|
var center = new $.Point(
|
|
|
|
this.centerSpringX.target.value,
|
|
|
|
this.centerSpringY.target.value
|
|
|
|
);
|
|
|
|
this.panTo( center.plus( delta ), immediately );
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
panTo: function(center, immediately) {
|
|
|
|
if (immediately) {
|
2012-01-05 03:14:20 +04:00
|
|
|
this.centerSpringX.resetTo(center.x);
|
|
|
|
this.centerSpringY.resetTo(center.y);
|
2011-12-06 07:50:25 +04:00
|
|
|
} else {
|
2012-01-05 03:14:20 +04:00
|
|
|
this.centerSpringX.springTo(center.x);
|
|
|
|
this.centerSpringY.springTo(center.y);
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
zoomBy: function(factor, refPoint, immediately) {
|
2012-01-05 03:14:20 +04:00
|
|
|
this.zoomTo(this.zoomSpring.target.value * factor, refPoint, immediately);
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
zoomTo: function(zoom, refPoint, immediately) {
|
|
|
|
|
|
|
|
if (immediately) {
|
2012-01-05 03:14:20 +04:00
|
|
|
this.zoomSpring.resetTo(zoom);
|
2011-12-06 07:50:25 +04:00
|
|
|
} else {
|
2012-01-05 03:14:20 +04:00
|
|
|
this.zoomSpring.springTo(zoom);
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
this.zoomPoint = refPoint instanceof $.Point ? refPoint : null;
|
|
|
|
},
|
|
|
|
|
|
|
|
resize: function(newContainerSize, maintain) {
|
2012-01-05 03:14:20 +04:00
|
|
|
var oldBounds = this.getBounds(),
|
|
|
|
newBounds = oldBounds,
|
|
|
|
widthDeltaFactor = newContainerSize.x / this.containerSize.x;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
this.containerSize = new $.Point(newContainerSize.x, newContainerSize.y);
|
2011-12-06 07:50:25 +04:00
|
|
|
|
|
|
|
if (maintain) {
|
2012-01-18 03:30:41 +04:00
|
|
|
newBounds.width = oldBounds.width * widthDeltaFactor;
|
2011-12-06 07:50:25 +04:00
|
|
|
newBounds.height = newBounds.width / this.getAspectRatio();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.fitBounds(newBounds, true);
|
|
|
|
},
|
|
|
|
|
|
|
|
update: function() {
|
2012-01-05 03:14:20 +04:00
|
|
|
var oldCenterX = this.centerSpringX.current.value,
|
|
|
|
oldCenterY = this.centerSpringY.current.value,
|
|
|
|
oldZoom = this.zoomSpring.current.value,
|
|
|
|
oldZoomPixel,
|
|
|
|
newZoomPixel,
|
|
|
|
deltaZoomPixels,
|
|
|
|
deltaZoomPoints;
|
2011-12-06 07:50:25 +04:00
|
|
|
|
|
|
|
if (this.zoomPoint) {
|
2012-01-05 03:14:20 +04:00
|
|
|
oldZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
this.zoomSpring.update();
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
if (this.zoomPoint && this.zoomSpring.current.value != oldZoom) {
|
|
|
|
newZoomPixel = this.pixelFromPoint( this.zoomPoint, true );
|
|
|
|
deltaZoomPixels = newZoomPixel.minus( oldZoomPixel);
|
|
|
|
deltaZoomPoints = this.deltaPointsFromPixels( deltaZoomPixels, true );
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
this.centerSpringX.shiftBy( deltaZoomPoints.x );
|
|
|
|
this.centerSpringY.shiftBy( deltaZoomPoints.y );
|
2011-12-06 07:50:25 +04:00
|
|
|
} else {
|
|
|
|
this.zoomPoint = null;
|
|
|
|
}
|
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
this.centerSpringX.update();
|
|
|
|
this.centerSpringY.update();
|
2011-12-06 07:50:25 +04:00
|
|
|
|
2012-01-05 03:14:20 +04:00
|
|
|
return this.centerSpringX.current.value != oldCenterX ||
|
|
|
|
this.centerSpringY.current.value != oldCenterY ||
|
|
|
|
this.zoomSpring.current.value != oldZoom;
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
deltaPixelsFromPoints: function(deltaPoints, current) {
|
2012-01-05 03:14:20 +04:00
|
|
|
return deltaPoints.times(
|
|
|
|
this.containerSize.x * this.getZoom( current )
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
deltaPointsFromPixels: function(deltaPixels, current) {
|
2012-01-05 03:14:20 +04:00
|
|
|
return deltaPixels.divide(
|
|
|
|
this.containerSize.x * this.getZoom( current )
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
pixelFromPoint: function(point, current) {
|
2012-01-05 03:14:20 +04:00
|
|
|
var bounds = this.getBounds( current );
|
|
|
|
return point.minus(
|
|
|
|
bounds.getTopLeft()
|
|
|
|
).times(
|
|
|
|
this.containerSize.x / bounds.width
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
pointFromPixel: function(pixel, current) {
|
2012-01-05 03:14:20 +04:00
|
|
|
var bounds = this.getBounds( current );
|
|
|
|
return pixel.divide(
|
|
|
|
this.containerSize.x / bounds.width
|
|
|
|
).plus(
|
|
|
|
bounds.getTopLeft()
|
|
|
|
);
|
2011-12-06 07:50:25 +04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
}( OpenSeadragon ));
|