mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-21 20:56:09 +03:00
Adds flip/mirror slides feature.
This commit adds full support for a new OpenSeaDragon(OSD) feature - Mirror/Flip. In order to fully support this new feature, the following OSD objects had to be modified: - drawer.js: - navigator - openseadragon - strings - tiledImage - viewer Additionally, a new flip button was created (similar to the existing ones). Flip Logic Whenever the state is flip button is pressed, flip state is toogled, inverting all the controls and displays (the rotation direction is inverted as well). This means that all viewer coordinates (including user inputs) must me inverted too. Summary of modifications - drawer.js: modified _offsetForRotation to invert rotation angle on flipped state. Added a _flip method to scale/mirror canvas context. - navigator.js: adds full flip support and inverts nagivator inputs. - openseadragon.js: new buttons, flip state variable and showFlipControl variable. - strings.js: flip tool help tips. - tiledImage.js: flips the actual drawing canvas. - viewer.js: Added keyboardshortcuts to rotate 90degrees (r/R) and flip image (f/F). flip button state is stored here and flip order is set. The flipped state is stored on viewer object.
This commit is contained in:
parent
e5355abafe
commit
71fd747051
BIN
images/flip_grouphover.png
Normal file
BIN
images/flip_grouphover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
BIN
images/flip_hover.png
Normal file
BIN
images/flip_hover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
BIN
images/flip_pressed.png
Normal file
BIN
images/flip_pressed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
BIN
images/flip_rest.png
Normal file
BIN
images/flip_rest.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
@ -500,6 +500,10 @@ $.Drawer.prototype = {
|
||||
|
||||
if ( this.viewport.degrees !== 0 ) {
|
||||
this._offsetForRotation({degrees: this.viewport.degrees});
|
||||
} else{
|
||||
if(this.viewer.flipped) {
|
||||
this._flip({});
|
||||
}
|
||||
}
|
||||
if (tiledImage.getRotation(true) % 360 !== 0) {
|
||||
this._offsetForRotation({
|
||||
@ -620,10 +624,29 @@ $.Drawer.prototype = {
|
||||
context.save();
|
||||
|
||||
context.translate(point.x, point.y);
|
||||
context.rotate(Math.PI / 180 * options.degrees);
|
||||
if(this.viewer.flipped){
|
||||
context.rotate(Math.PI / 180 * -options.degrees);
|
||||
context.scale(-1, 1);
|
||||
}
|
||||
else{
|
||||
context.rotate(Math.PI / 180 * options.degrees);
|
||||
}
|
||||
context.translate(-point.x, -point.y);
|
||||
},
|
||||
|
||||
// private
|
||||
_flip: function(options) {
|
||||
var point = options.point ?
|
||||
options.point.times($.pixelDensityRatio) :
|
||||
this.getCanvasCenter();
|
||||
var context = this._getContext(options.useSketch);
|
||||
context.save();
|
||||
|
||||
context.translate(point.x, 0);
|
||||
context.scale(-1, 1);
|
||||
context.translate(-point.x, 0);
|
||||
},
|
||||
|
||||
// private
|
||||
_restoreRotationChanges: function(useSketch) {
|
||||
var context = this._getContext(useSketch);
|
||||
|
@ -208,12 +208,14 @@ $.Navigator = function( options ){
|
||||
var degrees = options.viewer.viewport ?
|
||||
options.viewer.viewport.getRotation() :
|
||||
options.viewer.degrees || 0;
|
||||
|
||||
rotate(degrees);
|
||||
options.viewer.addHandler("rotate", function (args) {
|
||||
rotate(args.degrees);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Remove the base class' (Viewer's) innerTracker and replace it with our own
|
||||
this.innerTracker.destroy();
|
||||
this.innerTracker = new $.MouseTracker({
|
||||
@ -271,6 +273,50 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
/* Flip navigator element
|
||||
*/
|
||||
toogleFlip: function() {
|
||||
this.flipped = !this.flipped;
|
||||
if (this.viewer.flipped){
|
||||
this.displayRegion.style.webkitTransform = "scale(-1,1)";
|
||||
this.displayRegion.style.mozTransform = "scale(-1,1)";
|
||||
this.displayRegion.style.msTransform = "scale(-1,1)";
|
||||
this.displayRegion.style.oTransform = "scale(-1,1)";
|
||||
this.displayRegion.style.transform = "scale(-1,1)";
|
||||
|
||||
this.canvas.style.webkitTransform = "scale(-1,1)";
|
||||
this.canvas.style.mozTransform = "scale(-1,1)";
|
||||
this.canvas.style.msTransform = "scale(-1,1)";
|
||||
this.canvas.style.oTransform = "scale(-1,1)";
|
||||
this.canvas.style.transform = "scale(-1,1)";
|
||||
|
||||
this.element.style.webkitTransform = "scale(-1,1)";
|
||||
this.element.style.mozTransform = "scale(-1,1)";
|
||||
this.element.style.msTransform = "scale(-1,1)";
|
||||
this.element.style.oTransform = "scale(-1,1)";
|
||||
this.element.style.transform = "scale(-1,1)";
|
||||
} else{
|
||||
this.displayRegion.style.webkitTransform = "scale(1,1)";
|
||||
this.displayRegion.style.mozTransform = "scale(1,1)";
|
||||
this.displayRegion.style.msTransform = "scale(1,1)";
|
||||
this.displayRegion.style.oTransform = "scale(1,1)";
|
||||
this.displayRegion.style.transform = "scale(1,1)";
|
||||
|
||||
this.canvas.style.webkitTransform = "scale(1,1)";
|
||||
this.canvas.style.mozTransform = "scale(1,1)";
|
||||
this.canvas.style.msTransform = "scale(1,1)";
|
||||
this.canvas.style.oTransform = "scale(1,1)";
|
||||
this.canvas.style.transform = "scale(1,1)";
|
||||
|
||||
this.element.style.webkitTransform = "scale(1,1)";
|
||||
this.element.style.mozTransform = "scale(1,1)";
|
||||
this.element.style.msTransform = "scale(1,1)";
|
||||
this.element.style.oTransform = "scale(1,1)";
|
||||
this.element.style.transform = "scale(1,1)";
|
||||
}
|
||||
this.viewport.viewer.forceRedraw();
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to update the navigator minimap's viewport rectangle when a change in the viewer's viewport occurs.
|
||||
@ -406,6 +452,9 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
||||
*/
|
||||
function onCanvasClick( event ) {
|
||||
if ( event.quick && this.viewer.viewport ) {
|
||||
if(this.viewer.flipped){
|
||||
event.position.x = this.viewport.getContainerSize().x - event.position.x;
|
||||
}
|
||||
this.viewer.viewport.panTo(this.viewport.pointFromPixel(event.position));
|
||||
this.viewer.viewport.applyConstraints();
|
||||
}
|
||||
@ -424,6 +473,11 @@ function onCanvasDrag( event ) {
|
||||
if( !this.panVertical ){
|
||||
event.delta.y = 0;
|
||||
}
|
||||
|
||||
if(this.viewer.flipped){
|
||||
event.delta.x = -event.delta.x;
|
||||
}
|
||||
|
||||
this.viewer.viewport.panBy(
|
||||
this.viewport.deltaPointsFromPixels(
|
||||
event.delta
|
||||
@ -488,11 +542,11 @@ function onCanvasScroll( event ) {
|
||||
* @param {Number} degrees
|
||||
*/
|
||||
function _setTransformRotate (element, degrees) {
|
||||
element.style.webkitTransform = "rotate(" + degrees + "deg)";
|
||||
element.style.mozTransform = "rotate(" + degrees + "deg)";
|
||||
element.style.msTransform = "rotate(" + degrees + "deg)";
|
||||
element.style.oTransform = "rotate(" + degrees + "deg)";
|
||||
element.style.transform = "rotate(" + degrees + "deg)";
|
||||
element.style.webkitTransform = "rotate(" + degrees + "deg)";
|
||||
element.style.mozTransform = "rotate(" + degrees + "deg)";
|
||||
element.style.msTransform = "rotate(" + degrees + "deg)";
|
||||
element.style.oTransform = "rotate(" + degrees + "deg)";
|
||||
element.style.transform = "rotate(" + degrees + "deg)";
|
||||
}
|
||||
|
||||
}( OpenSeadragon ));
|
||||
|
@ -478,6 +478,10 @@
|
||||
* Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding
|
||||
* this setting when set to false.
|
||||
*
|
||||
* @property {Boolean} [showFlipControl=false]
|
||||
* If true then the flip controls will be displayed as part of the
|
||||
* standard controls.
|
||||
*
|
||||
* @property {Boolean} [showSequenceControl=true]
|
||||
* If sequenceMode is true, then provide buttons for navigating forward and
|
||||
* backward through the images.
|
||||
@ -688,6 +692,12 @@
|
||||
* @property {String} rotateright.HOVER
|
||||
* @property {String} rotateright.DOWN
|
||||
*
|
||||
* @property {Object} flip - Images for the flip button.
|
||||
* @property {String} flip.REST
|
||||
* @property {String} flip.GROUP
|
||||
* @property {String} flip.HOVER
|
||||
* @property {String} flip.DOWN
|
||||
*
|
||||
* @property {Object} previous - Images for the previous button.
|
||||
* @property {String} previous.REST
|
||||
* @property {String} previous.GROUP
|
||||
@ -1120,6 +1130,7 @@ function OpenSeadragon( options ){
|
||||
showHomeControl: true, //HOME
|
||||
showFullPageControl: true, //FULL
|
||||
showRotationControl: false, //ROTATION
|
||||
showFlipControl: false, //FLIP
|
||||
controlsFadeDelay: 2000, //ZOOM/HOME/FULL/SEQUENCE
|
||||
controlsFadeLength: 1500, //ZOOM/HOME/FULL/SEQUENCE
|
||||
mouseNavEnabled: true, //GENERAL MOUSE INTERACTIVITY
|
||||
@ -1141,6 +1152,9 @@ function OpenSeadragon( options ){
|
||||
// INITIAL ROTATION
|
||||
degrees: 0,
|
||||
|
||||
// INITIAL FLIP STATE
|
||||
flipped: false,
|
||||
|
||||
// APPEARANCE
|
||||
opacity: 1,
|
||||
preload: false,
|
||||
@ -1209,6 +1223,12 @@ function OpenSeadragon( options ){
|
||||
HOVER: 'rotateright_hover.png',
|
||||
DOWN: 'rotateright_pressed.png'
|
||||
},
|
||||
flip: { // Flip icon by Yaroslav Samoylov from the Noun Project
|
||||
REST: 'flip_rest.png',
|
||||
GROUP: 'flip_grouphover.png',
|
||||
HOVER: 'flip_hover.png',
|
||||
DOWN: 'flip_pressed.png'
|
||||
},
|
||||
previous: {
|
||||
REST: 'previous_rest.png',
|
||||
GROUP: 'previous_grouphover.png',
|
||||
|
@ -57,7 +57,8 @@ var I18N = {
|
||||
NextPage: "Next page",
|
||||
PreviousPage: "Previous page",
|
||||
RotateLeft: "Rotate left",
|
||||
RotateRight: "Rotate right"
|
||||
RotateRight: "Rotate right",
|
||||
Flip: "Flip Horizontally"
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -844,7 +844,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
return;
|
||||
}
|
||||
if (immediately) {
|
||||
this._degreesSpring.resetTo(degrees);
|
||||
this._degreesSpring.resetTo(degrees);
|
||||
} else {
|
||||
this._degreesSpring.springTo(degrees);
|
||||
}
|
||||
@ -1886,6 +1886,10 @@ function drawTiles( tiledImage, lastDrawn ) {
|
||||
degrees: tiledImage.viewport.degrees,
|
||||
useSketch: useSketch
|
||||
});
|
||||
} else {
|
||||
if(tiledImage._drawer.viewer.flipped) {
|
||||
tiledImage._drawer._flip({});
|
||||
}
|
||||
}
|
||||
if (tiledImage.getRotation(true) % 360 !== 0) {
|
||||
tiledImage._drawer._offsetForRotation({
|
||||
@ -1969,6 +1973,10 @@ function drawTiles( tiledImage, lastDrawn ) {
|
||||
}
|
||||
if (tiledImage.viewport.degrees !== 0) {
|
||||
tiledImage._drawer._restoreRotationChanges(useSketch);
|
||||
} else{
|
||||
if(tiledImage._drawer.viewer.flipped) {
|
||||
tiledImage._drawer._flip({});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
104
src/viewer.js
104
src/viewer.js
@ -1252,6 +1252,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
* @param {Boolean} [options.preload=false] Default switch for loading hidden images (true loads, false blocks)
|
||||
* @param {Number} [options.degrees=0] Initial rotation of the tiled image around
|
||||
* its top left corner in degrees.
|
||||
* @param {Boolean} [options.flipped=false] Initial flip/mirror state
|
||||
* @param {String} [options.compositeOperation] How the image is composited onto other images.
|
||||
* @param {String} [options.crossOriginPolicy] The crossOriginPolicy for this specific image,
|
||||
* overriding viewer.crossOriginPolicy.
|
||||
@ -1414,6 +1415,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
opacity: queueItem.options.opacity,
|
||||
preload: queueItem.options.preload,
|
||||
degrees: queueItem.options.degrees,
|
||||
flipped: queueItem.options.flipped,
|
||||
compositeOperation: queueItem.options.compositeOperation,
|
||||
springStiffness: _this.springStiffness,
|
||||
animationTime: _this.animationTime,
|
||||
@ -1674,6 +1676,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
onFullScreenHandler = $.delegate( this, onFullScreen ),
|
||||
onRotateLeftHandler = $.delegate( this, onRotateLeft ),
|
||||
onRotateRightHandler = $.delegate( this, onRotateRight ),
|
||||
onFlipHandler = $.delegate( this, onFlip),
|
||||
onFocusHandler = $.delegate( this, onFocus ),
|
||||
onBlurHandler = $.delegate( this, onBlur ),
|
||||
navImages = this.navImages,
|
||||
@ -1685,7 +1688,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
|
||||
if( this.zoomInButton || this.zoomOutButton ||
|
||||
this.homeButton || this.fullPageButton ||
|
||||
this.rotateLeftButton || this.rotateRightButton ) {
|
||||
this.rotateLeftButton || this.rotateRightButton ||
|
||||
this.flipButton ) {
|
||||
//if we are binding to custom buttons then layout and
|
||||
//grouping is the responsibility of the page author
|
||||
useGroup = false;
|
||||
@ -1789,7 +1793,22 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
onFocus: onFocusHandler,
|
||||
onBlur: onBlurHandler
|
||||
}));
|
||||
}
|
||||
|
||||
if ( this.showFlipControl ) {
|
||||
buttons.push( this.flipButton = new $.Button({
|
||||
element: this.flipButton ? $.getElement( this.flipButton ) : null,
|
||||
clickTimeThreshold: this.clickTimeThreshold,
|
||||
clickDistThreshold: this.clickDistThreshold,
|
||||
tooltip: $.getString( "Tooltips.Flip" ),
|
||||
srcRest: resolveUrl( this.prefixUrl, navImages.flip.REST ),
|
||||
srcGroup: resolveUrl( this.prefixUrl, navImages.flip.GROUP ),
|
||||
srcHover: resolveUrl( this.prefixUrl, navImages.flip.HOVER ),
|
||||
srcDown: resolveUrl( this.prefixUrl, navImages.flip.DOWN ),
|
||||
onRelease: onFlipHandler,
|
||||
onFocus: onFocusHandler,
|
||||
onBlur: onBlurHandler
|
||||
}));
|
||||
}
|
||||
|
||||
if ( useGroup ) {
|
||||
@ -2602,8 +2621,26 @@ function onCanvasKeyPress( event ) {
|
||||
this.viewport.applyConstraints();
|
||||
}
|
||||
return false;
|
||||
case 114: //r
|
||||
case 82: //R
|
||||
if(this.flipped){
|
||||
this.viewport.setRotation(this.viewport.degrees - 90);
|
||||
} else{
|
||||
this.viewport.setRotation(this.viewport.degrees + 90);
|
||||
}
|
||||
this.viewport.applyConstraints();
|
||||
return false;
|
||||
case 70: //F
|
||||
case 102: //f
|
||||
this.flipped = !this.flipped;
|
||||
if(this.navigator){
|
||||
this.navigator.toogleFlip();
|
||||
}
|
||||
this._forceRedraw = !this._forceRedraw;
|
||||
this.forceRedraw();
|
||||
return false;
|
||||
default:
|
||||
//console.log( 'navigator keycode %s', event.keyCode );
|
||||
// console.log( 'navigator keycode %s', event.keyCode );
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
@ -2620,6 +2657,9 @@ function onCanvasClick( event ) {
|
||||
if ( !haveKeyboardFocus ) {
|
||||
this.canvas.focus();
|
||||
}
|
||||
if(this.flipped){
|
||||
event.position.x = this.viewport.getContainerSize().x - event.position.x;
|
||||
}
|
||||
|
||||
var canvasClickEventArgs = {
|
||||
tracker: event.eventSource,
|
||||
@ -2739,6 +2779,9 @@ function onCanvasDrag( event ) {
|
||||
if( !this.panVertical ){
|
||||
event.delta.y = 0;
|
||||
}
|
||||
if(this.flipped){
|
||||
event.delta.x = -event.delta.x;
|
||||
}
|
||||
|
||||
if( this.constrainDuringPan ){
|
||||
var delta = this.viewport.deltaPointsFromPixels( event.delta.negate() );
|
||||
@ -3064,6 +3107,10 @@ function onCanvasScroll( event ) {
|
||||
if (deltaScrollTime > this.minScrollDeltaTime) {
|
||||
this._lastScrollTime = thisScrollTime;
|
||||
|
||||
if(this.flipped){
|
||||
event.position.x = this.viewport.getContainerSize().x - event.position.x;
|
||||
}
|
||||
|
||||
if ( !event.preventDefaultAction && this.viewport ) {
|
||||
gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
|
||||
if ( gestureSettings.scrollToZoom ) {
|
||||
@ -3259,7 +3306,7 @@ function updateOnce( viewer ) {
|
||||
drawWorld( viewer );
|
||||
viewer._drawOverlays();
|
||||
if( viewer.navigator ){
|
||||
viewer.navigator.update( viewer.viewport );
|
||||
viewer.navigator.update( viewer.viewport );
|
||||
}
|
||||
|
||||
THIS[ viewer.hash ].forceRedraw = false;
|
||||
@ -3429,11 +3476,20 @@ function onFullScreen() {
|
||||
function onRotateLeft() {
|
||||
if ( this.viewport ) {
|
||||
var currRotation = this.viewport.getRotation();
|
||||
if (currRotation === 0) {
|
||||
currRotation = 270;
|
||||
}
|
||||
else {
|
||||
currRotation -= 90;
|
||||
if ( this.flipped ){
|
||||
if (currRotation === 270) {
|
||||
currRotation = 0;
|
||||
}
|
||||
else {
|
||||
currRotation += 90;
|
||||
}
|
||||
} else {
|
||||
if (currRotation === 0) {
|
||||
currRotation = 270;
|
||||
}
|
||||
else {
|
||||
currRotation -= 90;
|
||||
}
|
||||
}
|
||||
this.viewport.setRotation(currRotation);
|
||||
}
|
||||
@ -3445,16 +3501,38 @@ function onRotateLeft() {
|
||||
function onRotateRight() {
|
||||
if ( this.viewport ) {
|
||||
var currRotation = this.viewport.getRotation();
|
||||
if (currRotation === 270) {
|
||||
currRotation = 0;
|
||||
}
|
||||
else {
|
||||
currRotation += 90;
|
||||
if ( this.flipped ){
|
||||
if (currRotation === 0) {
|
||||
currRotation = 270;
|
||||
}
|
||||
else {
|
||||
currRotation -= 90;
|
||||
}
|
||||
} else {
|
||||
if (currRotation === 270) {
|
||||
currRotation = 0;
|
||||
}
|
||||
else {
|
||||
currRotation += 90;
|
||||
}
|
||||
}
|
||||
this.viewport.setRotation(currRotation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: The current rotation feature is limited to 90 degree turns.
|
||||
*/
|
||||
function onFlip() {
|
||||
this.flipped = !this.flipped;
|
||||
if(this.navigator){
|
||||
this.navigator.toogleFlip();
|
||||
}
|
||||
this._forceRedraw = !this._forceRedraw;
|
||||
this.forceRedraw();
|
||||
}
|
||||
|
||||
|
||||
|
||||
function onPrevious(){
|
||||
var previous = this._sequenceIndex - 1;
|
||||
|
@ -886,7 +886,6 @@ $.Viewport.prototype = {
|
||||
this.viewer.world.getHomeBounds(),
|
||||
this.viewer.world.getContentFactor());
|
||||
this.viewer.forceRedraw();
|
||||
|
||||
/**
|
||||
* Raised when rotation has been changed.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user