Add TiledImage.fitInBounds method.

This commit is contained in:
Antoine Vandecreme 2016-03-21 16:11:50 -04:00
parent 030fec590e
commit e4fca14c33
5 changed files with 220 additions and 5 deletions

View File

@ -28,6 +28,7 @@ module.exports = function(grunt) {
"src/mousetracker.js", "src/mousetracker.js",
"src/control.js", "src/control.js",
"src/controldock.js", "src/controldock.js",
"src/placement.js",
"src/viewer.js", "src/viewer.js",
"src/navigator.js", "src/navigator.js",
"src/strings.js", "src/strings.js",

125
src/placement.js Normal file
View File

@ -0,0 +1,125 @@
/*
* OpenSeadragon - Placement
*
* Copyright (C) 2010-2016 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function($) {
/**
* An enumeration of positions to anchor an element.
* @memberOf OpenSeadragon
* @static
* @property {OpenSeadragon.Placement} CENTER
* @property {OpenSeadragon.Placement} TOP_LEFT
* @property {OpenSeadragon.Placement} TOP
* @property {OpenSeadragon.Placement} TOP_RIGHT
* @property {OpenSeadragon.Placement} RIGHT
* @property {OpenSeadragon.Placement} BOTTOM_RIGHT
* @property {OpenSeadragon.Placement} BOTTOM
* @property {OpenSeadragon.Placement} BOTTOM_LEFT
* @property {OpenSeadragon.Placement} LEFT
*/
$.Placement = {
CENTER: {
isLeft: false,
isHorizontallyCentered: true,
isRight: false,
isTop: false,
isVerticallyCentered: true,
isBottom: false
},
TOP_LEFT: {
isLeft: true,
isHorizontallyCentered: false,
isRight: false,
isTop: true,
isVerticallyCentered: false,
isBottom: false
},
TOP: {
isLeft: false,
isHorizontallyCentered: true,
isRight: false,
isTop: true,
isVerticallyCentered: false,
isBottom: false
},
TOP_RIGHT: {
isLeft: false,
isHorizontallyCentered: false,
isRight: true,
isTop: true,
isVerticallyCentered: false,
isBottom: false
},
RIGHT: {
isLeft: false,
isHorizontallyCentered: false,
isRight: true,
isTop: false,
isVerticallyCentered: true,
isBottom: false
},
BOTTOM_RIGHT: {
isLeft: false,
isHorizontallyCentered: false,
isRight: true,
isTop: false,
isVerticallyCentered: false,
isBottom: true
},
BOTTOM: {
isLeft: false,
isHorizontallyCentered: true,
isRight: false,
isTop: false,
isVerticallyCentered: false,
isBottom: true
},
BOTTOM_LEFT: {
isLeft: true,
isHorizontallyCentered: false,
isRight: false,
isTop: false,
isVerticallyCentered: false,
isBottom: true
},
LEFT: {
isLeft: true,
isHorizontallyCentered: false,
isRight: false,
isTop: false,
isVerticallyCentered: true,
isBottom: false
}
};
}(OpenSeadragon));

View File

@ -543,6 +543,48 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
this._setScale(height / this.normHeight, immediately); this._setScale(height / this.normHeight, immediately);
}, },
/**
* Positions and scales the TiledImage to fit in the specified bounds.
* Note: this method fires OpenSeadragon.TiledImage.event:bounds-change
* twice
* @param {OpenSeadragon.Rect} bounds The bounds to fit the image into.
* @param {OpenSeadragon.Placement} [anchor=OpenSeadragon.Placement.CENTER]
* How to anchor the image in the bounds.
* @param {Boolean} [immediately=false] Whether to animate to the new size
* or snap immediately.
* @fires OpenSeadragon.TiledImage.event:bounds-change
*/
fitInBounds: function(bounds, anchor, immediately) {
anchor = anchor || $.Placement.CENTER;
if (bounds.getAspectRatio() > this.contentAspectX) {
// We will have margins on the X axis
var targetWidth = bounds.height * this.contentAspectX;
var marginLeft = 0;
if (anchor.isHorizontallyCentered) {
marginLeft = (bounds.width - targetWidth) / 2;
} else if (anchor.isRight) {
marginLeft = bounds.width - targetWidth;
}
this.setPosition(
new $.Point(bounds.x + marginLeft, bounds.y),
immediately);
this.setHeight(bounds.height, immediately);
} else {
// We will have margins on the Y axis
var targetHeight = bounds.width / this.contentAspectX;
var marginTop = 0;
if (anchor.isVerticallyCentered) {
marginTop = (bounds.height - targetHeight) / 2;
} else if (anchor.isBottom) {
marginTop = bounds.height - targetHeight;
}
this.setPosition(
new $.Point(bounds.x, bounds.y + marginTop),
immediately);
this.setWidth(bounds.width, immediately);
}
},
/** /**
* @returns {OpenSeadragon.Rect|null} The TiledImage's current clip rectangle, * @returns {OpenSeadragon.Rect|null} The TiledImage's current clip rectangle,
* in image pixels, or null if none. * in image pixels, or null if none.

View File

@ -22,6 +22,7 @@
<script src="/src/mousetracker.js"></script> <script src="/src/mousetracker.js"></script>
<script src="/src/control.js"></script> <script src="/src/control.js"></script>
<script src="/src/controldock.js"></script> <script src="/src/controldock.js"></script>
<script src="/src/placement.js"></script>
<script src="/src/viewer.js"></script> <script src="/src/viewer.js"></script>
<script src="/src/navigator.js"></script> <script src="/src/navigator.js"></script>
<script src="/src/strings.js"></script> <script src="/src/strings.js"></script>

View File

@ -4,18 +4,18 @@
var viewer; var viewer;
module('TiledImage', { module('TiledImage', {
setup: function () { setup: function() {
var example = $('<div id="example"></div>').appendTo("#qunit-fixture"); var example = $('<div id="example"></div>').appendTo("#qunit-fixture");
testLog.reset(); testLog.reset();
viewer = OpenSeadragon({ viewer = OpenSeadragon({
id: 'example', id: 'example',
prefixUrl: '/build/openseadragon/images/', prefixUrl: '/build/openseadragon/images/',
springStiffness: 100 // Faster animation = faster tests springStiffness: 100 // Faster animation = faster tests
}); });
}, },
teardown: function () { teardown: function() {
if (viewer && viewer.close) { if (viewer && viewer.close) {
viewer.close(); viewer.close();
} }
@ -87,7 +87,7 @@
// ---------- // ----------
asyncTest('animation', function() { asyncTest('animation', function() {
viewer.addHandler("open", function () { viewer.addHandler("open", function() {
var image = viewer.world.getItemAt(0); var image = viewer.world.getItemAt(0);
propEqual(image.getBounds(), new OpenSeadragon.Rect(0, 0, 1, 1), 'target bounds on open'); propEqual(image.getBounds(), new OpenSeadragon.Rect(0, 0, 1, 1), 'target bounds on open');
propEqual(image.getBounds(true), new OpenSeadragon.Rect(0, 0, 1, 1), 'current bounds on open'); propEqual(image.getBounds(true), new OpenSeadragon.Rect(0, 0, 1, 1), 'current bounds on open');
@ -257,4 +257,50 @@
}); });
}); });
asyncTest('fitInBounds', function() {
function assertRectEquals(actual, expected, message) {
ok(actual.equals(expected), message + ' should be ' +
expected.toString() + ', found ' + actual.toString());
}
viewer.addHandler('open', function openHandler() {
viewer.removeHandler('open', openHandler);
var squareImage = viewer.world.getItemAt(0);
squareImage.fitInBounds(
new OpenSeadragon.Rect(0, 0, 1, 2),
OpenSeadragon.Placement.CENTER,
true);
var actualBounds = squareImage.getBounds(true);
var expectedBounds = new OpenSeadragon.Rect(0, 0.5, 1, 1);
assertRectEquals(actualBounds, expectedBounds, 'Square image bounds');
var tallImage = viewer.world.getItemAt(1);
tallImage.fitInBounds(
new OpenSeadragon.Rect(0, 0, 1, 2),
OpenSeadragon.Placement.TOP_LEFT,
true);
actualBounds = tallImage.getBounds(true);
expectedBounds = new OpenSeadragon.Rect(0, 0, 0.5, 2);
assertRectEquals(actualBounds, expectedBounds, 'Tall image bounds');
var wideImage = viewer.world.getItemAt(2);
wideImage.fitInBounds(
new OpenSeadragon.Rect(0, 0, 1, 2),
OpenSeadragon.Placement.BOTTOM_RIGHT,
true);
actualBounds = wideImage.getBounds(true);
expectedBounds = new OpenSeadragon.Rect(0, 1.75, 1, 0.25);
assertRectEquals(actualBounds, expectedBounds, 'Wide image bounds');
start();
});
viewer.open([
'/test/data/testpattern.dzi',
'/test/data/tall.dzi',
'/test/data/wide.dzi'
]);
});
})(); })();