convert DrawerBase and drawer implementations to classes; add html drawer to the demo page

This commit is contained in:
Tom 2023-06-07 12:42:16 -07:00
parent 8fda8ceae7
commit 354590a17a
5 changed files with 507 additions and 508 deletions

View File

@ -44,40 +44,39 @@
* @param {Element} options.element - Parent element.
* @param {Number} [options.debugGridColor] - See debugGridColor in {@link OpenSeadragon.Options} for details.
*/
$.CanvasDrawer = function(options) {
$.DrawerBase.call(this, options);
class CanvasDrawer extends $.DrawerBase{
constructor(options){
super(options);
/**
* 2d drawing context for {@link OpenSeadragon.Drawer#canvas} if it's a <canvas> element, otherwise null.
* @member {Object} context
* @memberof OpenSeadragon.Drawer#
*/
this.context = this.canvas.getContext( '2d' );
/**
* 2d drawing context for {@link OpenSeadragon.Drawer#canvas} if it's a <canvas> element, otherwise null.
* @member {Object} context
* @memberof OpenSeadragon.Drawer#
*/
this.context = this.canvas.getContext( '2d' );
/**
* Sketch canvas used to temporarily draw tiles which cannot be drawn directly
* to the main canvas due to opacity. Lazily initialized.
*/
this.sketchCanvas = null;
this.sketchContext = null;
/**
* Sketch canvas used to temporarily draw tiles which cannot be drawn directly
* to the main canvas due to opacity. Lazily initialized.
*/
this.sketchCanvas = null;
this.sketchContext = null;
// We force our container to ltr because our drawing math doesn't work in rtl.
// This issue only affects our canvas renderer, but we do it always for consistency.
// Note that this means overlays you want to be rtl need to be explicitly set to rtl.
this.container.dir = 'ltr';
// We force our container to ltr because our drawing math doesn't work in rtl.
// This issue only affects our canvas renderer, but we do it always for consistency.
// Note that this means overlays you want to be rtl need to be explicitly set to rtl.
this.container.dir = 'ltr';
// Image smoothing for canvas rendering (only if canvas is used).
// Canvas default is "true", so this will only be changed if user specified "false".
this._imageSmoothingEnabled = true;
};
$.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadragon.Drawer.prototype */ {
// Image smoothing for canvas rendering (only if canvas is used).
// Canvas default is "true", so this will only be changed if user specified "false".
this._imageSmoothingEnabled = true;
}
/**
* Draws the TiledImages
*/
draw: function(tiledImages) {
draw(tiledImages) {
var _this = this;
this._prepareNewFrame(); // prepare to draw a new frame
tiledImages.forEach(function(tiledImage){
@ -89,26 +88,25 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
tiledImage._needsDraw = false;
}
});
},
}
/**
* @returns {Boolean} True - rotation is supported.
*/
canRotate: function() {
canRotate() {
return true;
},
}
/**
* Destroy the drawer (unload current loaded tiles)
*/
destroy: function() {
destroy() {
//force unloading of current canvas (1x1 will be gc later, trick not necessarily needed)
this.canvas.width = 1;
this.canvas.height = 1;
this.sketchCanvas = null;
this.sketchContext = null;
},
}
/**
* Turns image smoothing on or off for this viewer. Note: Ignored in some (especially older) browsers that do not support this property.
@ -118,17 +116,17 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
* drawn smoothly on the canvas; see imageSmoothingEnabled in
* {@link OpenSeadragon.Options} for more explanation.
*/
setImageSmoothingEnabled: function(imageSmoothingEnabled){
setImageSmoothingEnabled(imageSmoothingEnabled){
this._imageSmoothingEnabled = imageSmoothingEnabled;
this._updateImageSmoothingEnabled(this.context);
this.viewer.forceRedraw();
},
}
/**
* Draw a rectangle onto the canvas
* @param {OpenSeadragon.Rect} rect
*/
drawDebuggingRect: function(rect) {
drawDebuggingRect(rect) {
var context = this.context;
context.save();
context.lineWidth = 2 * $.pixelDensityRatio;
@ -143,8 +141,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
);
context.restore();
},
}
/**
* @private
@ -152,7 +149,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
* Clears the Drawer so it's ready to draw another frame.
*
*/
_prepareNewFrame: function() {
_prepareNewFrame() {
var viewportSize = this._calculateCanvasSize();
if( this.canvas.width !== viewportSize.x ||
this.canvas.height !== viewportSize.y ) {
@ -167,8 +164,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
}
}
this._clear();
},
}
/**
* @private
@ -176,7 +172,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
* @param {Boolean} useSketch Whether to clear sketch canvas or main canvas
* @param {OpenSeadragon.Rect} [bounds] The rectangle to clear
*/
_clear: function(useSketch, bounds){
_clear(useSketch, bounds){
var context = this._getContext(useSketch);
if (bounds) {
context.clearRect(bounds.x, bounds.y, bounds.width, bounds.height);
@ -184,106 +180,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
var canvas = context.canvas;
context.clearRect(0, 0, canvas.width, canvas.height);
}
},
/* Methods from TiledImage */
// /**
// * @private
// * @inner
// * Handles drawing a single TiledImage to the canvas
// *
// */
// _updateViewportWithTiledImage: function(tiledImage) {
// var _this = this;
// tiledImage._needsDraw = false;
// tiledImage._tilesLoading = 0;
// tiledImage.loadingCoverage = {};
// // Reset tile's internal drawn state
// while (tiledImage.lastDrawn.length > 0) {
// var tile = tiledImage.lastDrawn.pop();
// tile.beingDrawn = false;
// }
// var drawArea = tiledImage.getDrawArea();
// if(!drawArea){
// return;
// }
// function updateTile(info){
// var tile = info.tile;
// if(tile && tile.loaded){
// var needsDraw = _this._blendTile(
// tiledImage,
// tile,
// tile.x,
// tile.y,
// info.level,
// info.levelOpacity,
// info.currentTime
// );
// if(needsDraw){
// tiledImage._needsDraw = true;
// }
// }
// }
// var infoArray = tiledImage.getTileInfoForDrawing();
// infoArray.forEach(updateTile);
// this._drawTiles(tiledImage);
// },
// /**
// * @private
// * @inner
// * Updates the opacity of a tile according to the time it has been on screen
// * to perform a fade-in.
// * Updates coverage once a tile is fully opaque.
// * Returns whether the fade-in has completed.
// *
// * @param {OpenSeadragon.Tile} tile
// * @param {Number} x
// * @param {Number} y
// * @param {Number} level
// * @param {Number} levelOpacity
// * @param {Number} currentTime
// * @returns {Boolean}
// */
// _blendTile: function( tiledImage, tile, x, y, level, levelOpacity, currentTime ){
// var blendTimeMillis = 1000 * tiledImage.blendTime,
// deltaTime,
// opacity;
// if ( !tile.blendStart ) {
// tile.blendStart = currentTime;
// }
// deltaTime = currentTime - tile.blendStart;
// opacity = blendTimeMillis ? Math.min( 1, deltaTime / ( blendTimeMillis ) ) : 1;
// if ( tiledImage.alwaysBlend ) {
// opacity *= levelOpacity;
// }
// tile.opacity = opacity;
// tiledImage.lastDrawn.push( tile );
// if ( opacity === 1 ) {
// tiledImage._setCoverage( tiledImage.coverage, level, x, y, true );
// tiledImage._hasOpaqueTile = true;
// } else if ( deltaTime < blendTimeMillis ) {
// return true;
// }
// return false;
// },
}
/**
* @private
@ -291,7 +188,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
* Draws a TiledImage.
*
*/
_drawTiles: function( tiledImage ) {
_drawTiles( tiledImage ) {
var lastDrawn = tiledImage.lastDrawn;
if (tiledImage.opacity === 0 || (lastDrawn.length === 0 && !tiledImage.placeholderFillStyle)) {
return;
@ -532,7 +429,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
}
this._drawDebugInfo( tiledImage, lastDrawn );
},
}
/**
* @private
@ -540,7 +437,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
* Draws special debug information for a TiledImage if in debug mode.
* @param {OpenSeadragon.Tile[]} lastDrawn - An unordered list of Tiles drawn last frame.
*/
_drawDebugInfo: function( tiledImage, lastDrawn ) {
_drawDebugInfo( tiledImage, lastDrawn ) {
if( tiledImage.debugMode ) {
for ( var i = lastDrawn.length - 1; i >= 0; i-- ) {
var tile = lastDrawn[ i ];
@ -551,7 +448,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
}
}
}
},
}
/* Methods from Tile */
@ -563,7 +460,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
* @param {OpenSeadragon.Point[][]} polygons - an array of polygons. A polygon is an array of OpenSeadragon.Point
* @param {Boolean} useSketch - Whether to use the sketch canvas or not.
*/
_clipWithPolygons: function (polygons, useSketch) {
_clipWithPolygons (polygons, useSketch) {
var context = this._getContext(useSketch);
context.beginPath();
polygons.forEach(function (polygon) {
@ -572,7 +469,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
});
});
context.clip();
},
}
/**
* @private
@ -590,7 +487,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
* context.
* @param {OpenSeadragon.TileSource} source - The source specification of the tile.
*/
_drawTile: function( tile, drawingHandler, useSketch, scale, translate, shouldRoundPositionAndSize, source) {
_drawTile( tile, drawingHandler, useSketch, scale, translate, shouldRoundPositionAndSize, source) {
$.console.assert(tile, '[Drawer._drawTile] tile is required');
$.console.assert(drawingHandler, '[Drawer._drawTile] drawingHandler is required');
@ -598,7 +495,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
scale = scale || 1;
this._drawTileToCanvas(tile, context, drawingHandler, scale, translate, shouldRoundPositionAndSize, source);
},
}
/**
* @private
@ -617,7 +514,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
* context.
* @param {OpenSeadragon.TileSource} source - The source specification of the tile.
*/
_drawTileToCanvas: function( tile, context, drawingHandler, scale, translate, shouldRoundPositionAndSize, source) {
_drawTileToCanvas( tile, context, drawingHandler, scale, translate, shouldRoundPositionAndSize, source) {
var position = tile.position.times($.pixelDensityRatio),
size = tile.size.times($.pixelDensityRatio),
@ -708,68 +605,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
);
context.restore();
},
/**
* @private
* @inner
* Renders the tile in an html container.
* @function
* @param {OpenSeadragon.Tile} tile
* @param {Element} container
*/
_drawTileToHTML: function( tile, container ) {
if (!tile.cacheImageRecord) {
$.console.warn(
'[Drawer._drawTileToHTML] attempting to draw tile %s when it\'s not cached',
tile.toString());
return;
}
if ( !tile.loaded ) {
$.console.warn(
"Attempting to draw tile %s when it's not yet loaded.",
tile.toString()
);
return;
}
//EXPERIMENTAL - trying to figure out how to scale the container
// content during animation of the container size.
if ( !tile.element ) {
var image = tile.getImage();
if (!image) {
return;
}
tile.element = $.makeNeutralElement( "div" );
tile.imgElement = image.cloneNode();
tile.imgElement.style.msInterpolationMode = "nearest-neighbor";
tile.imgElement.style.width = "100%";
tile.imgElement.style.height = "100%";
tile.style = tile.element.style;
tile.style.position = "absolute";
}
if ( tile.element.parentNode !== container ) {
container.appendChild( tile.element );
}
if ( tile.imgElement.parentNode !== tile.element ) {
tile.element.appendChild( tile.imgElement );
}
tile.style.top = tile.position.y + "px";
tile.style.left = tile.position.x + "px";
tile.style.height = tile.size.y + "px";
tile.style.width = tile.size.x + "px";
if (tile.flipped) {
tile.style.transform = "scaleX(-1)";
}
$.setElementOpacity( tile.element, tile.opacity );
},
}
/**
* @private
@ -778,7 +614,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
* @param {Boolean} useSketch
* @returns
*/
_getContext: function( useSketch ) {
_getContext( useSketch ) {
var context = this.context;
if ( useSketch ) {
if (this.sketchCanvas === null) {
@ -808,7 +644,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
context = this.sketchContext;
}
return context;
},
}
/**
* @private
@ -817,9 +653,9 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
* @param {Boolean} useSketch
* @returns
*/
_saveContext: function( useSketch ) {
_saveContext( useSketch ) {
this._getContext( useSketch ).save();
},
}
/**
* @private
@ -828,26 +664,26 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
* @param {Boolean} useSketch
* @returns
*/
_restoreContext: function( useSketch ) {
_restoreContext( useSketch ) {
this._getContext( useSketch ).restore();
},
}
// private
_setClip: function(rect, useSketch) {
_setClip(rect, useSketch) {
var context = this._getContext( useSketch );
context.beginPath();
context.rect(rect.x, rect.y, rect.width, rect.height);
context.clip();
},
}
// private
_drawRectangle: function(rect, fillStyle, useSketch) {
_drawRectangle(rect, fillStyle, useSketch) {
var context = this._getContext( useSketch );
context.save();
context.fillStyle = fillStyle;
context.fillRect(rect.x, rect.y, rect.width, rect.height);
context.restore();
},
}
/**
* Blends the sketch canvas in the main canvas.
@ -865,7 +701,7 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
* canvas to blend in the main canvas. If specified, options.scale and
* options.translate get ignored.
*/
blendSketch: function(opacity, scale, translate, compositeOperation) {
blendSketch(opacity, scale, translate, compositeOperation) {
var options = opacity;
if (!$.isPlainObject(options)) {
options = {
@ -942,10 +778,10 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
);
}
this.context.restore();
},
}
// private
drawDebugInfo: function(tile, count, i, tiledImage) {
drawDebugInfo(tile, count, i, tiledImage) {
var colorIndex = this.viewer.world.getIndexOfItem(tiledImage) % this.debugGridColor.length;
var context = this.context;
@ -1045,13 +881,13 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
}
context.restore();
},
}
// private
_updateImageSmoothingEnabled: function(context){
_updateImageSmoothingEnabled(context){
context.msImageSmoothingEnabled = this._imageSmoothingEnabled;
context.imageSmoothingEnabled = this._imageSmoothingEnabled;
},
}
/**
* @private
@ -1060,10 +896,10 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
* @param {Boolean} sketch If set to true return the size of the sketch canvas
* @returns {OpenSeadragon.Point} The size of the canvas
*/
_getCanvasSize: function(sketch) {
_getCanvasSize(sketch) {
var canvas = this._getContext(sketch).canvas;
return new $.Point(canvas.width, canvas.height);
},
}
/**
* @private
@ -1072,12 +908,12 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
* @param {Boolean} sketch If set to true return the center point of the sketch canvas
* @returns {OpenSeadragon.Point} The center point of the canvas
*/
_getCanvasCenter: function() {
_getCanvasCenter() {
return new $.Point(this.canvas.width / 2, this.canvas.height / 2);
},
}
// private
_offsetForRotation: function(options) {
_offsetForRotation(options) {
var point = options.point ?
options.point.times($.pixelDensityRatio) :
this._getCanvasCenter();
@ -1093,10 +929,10 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
context.rotate(Math.PI / 180 * options.degrees);
}
context.translate(-point.x, -point.y);
},
}
// private
_flip: function(options) {
_flip(options) {
options = options || {};
var point = options.point ?
options.point.times($.pixelDensityRatio) :
@ -1106,16 +942,16 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
context.translate(point.x, 0);
context.scale(-1, 1);
context.translate(-point.x, 0);
},
}
// private
_restoreRotationChanges: function(useSketch) {
_restoreRotationChanges(useSketch) {
var context = this._getContext(useSketch);
context.restore();
},
}
// private
_calculateCanvasSize: function() {
_calculateCanvasSize() {
var pixelDensityRatio = $.pixelDensityRatio;
var viewportSize = this.viewport.getContainerSize();
return {
@ -1123,10 +959,10 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
x: Math.round(viewportSize.x * pixelDensityRatio),
y: Math.round(viewportSize.y * pixelDensityRatio)
};
},
}
// private
_calculateSketchCanvasSize: function() {
_calculateSketchCanvasSize() {
var canvasSize = this._calculateCanvasSize();
if (this.viewport.getRotation() === 0) {
return canvasSize;
@ -1140,11 +976,9 @@ $.extend( $.CanvasDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadr
x: sketchCanvasSize,
y: sketchCanvasSize
};
},
});
}
}
$.CanvasDrawer = CanvasDrawer;
/**

View File

@ -43,119 +43,113 @@
* @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport.
* @param {Element} options.element - Parent element.
*/
$.DrawerBase = function( options ) {
$.console.assert( options.viewer, "[Drawer] options.viewer is required" );
class DrawerBase{
constructor(options){
$.console.assert( options.viewer, "[Drawer] options.viewer is required" );
//backward compatibility for positional args while preferring more
//idiomatic javascript options object as the only argument
var args = arguments;
//backward compatibility for positional args while preferring more
//idiomatic javascript options object as the only argument
var args = arguments;
if( !$.isPlainObject( options ) ){
options = {
source: args[ 0 ], // Reference to Viewer tile source.
viewport: args[ 1 ], // Reference to Viewer viewport.
element: args[ 2 ] // Parent element.
};
if( !$.isPlainObject( options ) ){
options = {
source: args[ 0 ], // Reference to Viewer tile source.
viewport: args[ 1 ], // Reference to Viewer viewport.
element: args[ 2 ] // Parent element.
};
}
$.console.assert( options.viewport, "[Drawer] options.viewport is required" );
$.console.assert( options.element, "[Drawer] options.element is required" );
if ( options.source ) {
$.console.error( "[Drawer] options.source is no longer accepted; use TiledImage instead" );
}
this.viewer = options.viewer;
this.viewport = options.viewport;
this.debugGridColor = typeof options.debugGridColor === 'string' ? [options.debugGridColor] : options.debugGridColor || $.DEFAULT_SETTINGS.debugGridColor;
if (options.opacity) {
$.console.error( "[Drawer] options.opacity is no longer accepted; set the opacity on the TiledImage instead" );
}
this.useCanvas = $.supportsCanvas && ( this.viewer ? this.viewer.useCanvas : true );
/**
* The parent element of this Drawer instance, passed in when the Drawer was created.
* The parent of {@link OpenSeadragon.DrawerBase#canvas}.
* @member {Element} container
* @memberof OpenSeadragon.DrawerBase#
*/
this.container = $.getElement( options.element );
/**
* A &lt;canvas&gt; element if the browser supports them, otherwise a &lt;div&gt; element.
* Child element of {@link OpenSeadragon.DrawerBase#container}.
* @member {Element} canvas
* @memberof OpenSeadragon.DrawerBase#
*/
this.canvas = $.makeNeutralElement( this.useCanvas ? "canvas" : "div" );
/**
* @member {Element} element
* @memberof OpenSeadragon.DrawerBase#
* @deprecated Alias for {@link OpenSeadragon.DrawerBase#container}.
*/
this.element = this.container;
// TO DO: Does this need to be in DrawerBase, or only in Drawer implementations?
// We force our container to ltr because our drawing math doesn't work in rtl.
// This issue only affects our canvas renderer, but we do it always for consistency.
// Note that this means overlays you want to be rtl need to be explicitly set to rtl.
this.container.dir = 'ltr';
if (this.useCanvas) {
var viewportSize = this._calculateCanvasSize();
this.canvas.width = viewportSize.x;
this.canvas.height = viewportSize.y;
}
this.canvas.style.width = "100%";
this.canvas.style.height = "100%";
this.canvas.style.position = "absolute";
$.setElementOpacity( this.canvas, this.opacity, true );
// Allow pointer events to pass through the canvas element so implicit
// pointer capture works on touch devices
$.setElementPointerEventsNone( this.canvas );
$.setElementTouchActionNone( this.canvas );
// explicit left-align
this.container.style.textAlign = "left";
this.container.appendChild( this.canvas );
this._checkForAPIOverrides();
}
$.console.assert( options.viewport, "[Drawer] options.viewport is required" );
$.console.assert( options.element, "[Drawer] options.element is required" );
if ( options.source ) {
$.console.error( "[Drawer] options.source is no longer accepted; use TiledImage instead" );
get isOpenSeadragonDrawer(){
return true;
}
this.viewer = options.viewer;
this.viewport = options.viewport;
this.debugGridColor = typeof options.debugGridColor === 'string' ? [options.debugGridColor] : options.debugGridColor || $.DEFAULT_SETTINGS.debugGridColor;
if (options.opacity) {
$.console.error( "[Drawer] options.opacity is no longer accepted; set the opacity on the TiledImage instead" );
}
this.useCanvas = $.supportsCanvas && ( this.viewer ? this.viewer.useCanvas : true );
/**
* The parent element of this Drawer instance, passed in when the Drawer was created.
* The parent of {@link OpenSeadragon.DrawerBase#canvas}.
* @member {Element} container
* @memberof OpenSeadragon.DrawerBase#
*/
this.container = $.getElement( options.element );
/**
* A &lt;canvas&gt; element if the browser supports them, otherwise a &lt;div&gt; element.
* Child element of {@link OpenSeadragon.DrawerBase#container}.
* @member {Element} canvas
* @memberof OpenSeadragon.DrawerBase#
*/
this.canvas = $.makeNeutralElement( this.useCanvas ? "canvas" : "div" );
/**
* @member {Element} element
* @memberof OpenSeadragon.DrawerBase#
* @deprecated Alias for {@link OpenSeadragon.DrawerBase#container}.
*/
this.element = this.container;
// TO DO: Does this need to be in DrawerBase, or only in Drawer implementations?
// We force our container to ltr because our drawing math doesn't work in rtl.
// This issue only affects our canvas renderer, but we do it always for consistency.
// Note that this means overlays you want to be rtl need to be explicitly set to rtl.
this.container.dir = 'ltr';
if (this.useCanvas) {
var viewportSize = this._calculateCanvasSize();
this.canvas.width = viewportSize.x;
this.canvas.height = viewportSize.y;
}
this.canvas.style.width = "100%";
this.canvas.style.height = "100%";
this.canvas.style.position = "absolute";
$.setElementOpacity( this.canvas, this.opacity, true );
// Allow pointer events to pass through the canvas element so implicit
// pointer capture works on touch devices
$.setElementPointerEventsNone( this.canvas );
$.setElementTouchActionNone( this.canvas );
// explicit left-align
this.container.style.textAlign = "left";
this.container.appendChild( this.canvas );
this._checkForAPIOverrides();
};
/** @lends OpenSeadragon.DrawerBaseBase.prototype */
$.DrawerBase.prototype = {
// Drawer implementaions must define the next four methods. These are called
// by core OSD and/or public APIs, and forcing overrides (even for nullop methods) makes the
// behavior of the implementations explicitly clear in the code.
// Whether these have been overridden by child classes is checked in the
// constructor (via _checkForAPIOverrides).
/**
* @param tiledImage the TiledImage that is ready to be drawn
*/
draw: function(tiledImage) {
draw(tiledImage) {
$.console.error('Drawer.draw must be implemented by child class');
},
}
/**
* @returns {Boolean} True if rotation is supported.
*/
canRotate: function() {
canRotate() {
$.console.error('Drawer.canRotate must be implemented by child class');
},
}
/**
* Destroy the drawer (unload current loaded tiles)
*/
destroy: function() {
destroy() {
$.console.error('Drawer.destroy must be implemented by child class');
},
}
/**
* Turns image smoothing on or off for this viewer. Note: Ignored in some (especially older) browsers that do not support this property.
@ -165,23 +159,23 @@ $.DrawerBase.prototype = {
* drawn smoothly on the canvas; see imageSmoothingEnabled in
* {@link OpenSeadragon.Options} for more explanation.
*/
setImageSmoothingEnabled: function(imageSmoothingEnabled){
setImageSmoothingEnabled(imageSmoothingEnabled){
$.console.error('Drawer.setImageSmoothingEnabled must be implemented by child class');
},
}
/**
* Optional public API to draw a rectangle (e.g. for debugging purposes)
* Child classes can override this method if they wish to support this
* @param {OpenSeadragon.Rect} rect
*/
drawDebuggingRect: function(rect) {
drawDebuggingRect(rect) {
$.console.warn('[drawer].drawDebuggingRect is not implemented by this drawer');
},
}
// Deprecated functions
clear: function(){
clear(){
$.console.warn('[drawer].clear() is deprecated. The drawer is responsible for clearing itself as needed before drawing tiles.');
},
}
// Private functions
@ -192,7 +186,7 @@ $.DrawerBase.prototype = {
* draw, canRotate, destroy, and setImageSmoothinEnabled. Throws an exception if the original
* placeholder methods are still in place.
*/
_checkForAPIOverrides: function(){
_checkForAPIOverrides(){
if(this.draw === $.DrawerBase.prototype.draw){
throw("[drawer].draw must be implemented by child class");
}
@ -206,7 +200,7 @@ $.DrawerBase.prototype = {
if(this.setImageSmoothingEnabled === $.DrawerBase.prototype.setImageSmoothingEnabled){
throw("[drawer].setImageSmoothingEnabled must be implemented by child class");
}
},
}
// Utility functions internal API
@ -219,7 +213,7 @@ $.DrawerBase.prototype = {
* @param {OpenSeadragon.Rect} rectangle - The rectangle in viewport coordinate system.
* @returns {OpenSeadragon.Rect} Rectangle in drawer coordinate system.
*/
_viewportToDrawerRectangle: function(rectangle) {
_viewportToDrawerRectangle(rectangle) {
var topLeft = this.viewport.pixelFromPointNoRotate(rectangle.getTopLeft(), true);
var size = this.viewport.deltaPixelsFromPointsNoRotate(rectangle.getSize(), true);
@ -229,7 +223,7 @@ $.DrawerBase.prototype = {
size.x * $.pixelDensityRatio,
size.y * $.pixelDensityRatio
);
},
}
/**
* @private
@ -241,13 +235,13 @@ $.DrawerBase.prototype = {
* @param {OpenSeadragon.Point} point - the pixel point to convert
* @returns {OpenSeadragon.Point} Point in drawer coordinate system.
*/
_viewportCoordToDrawerCoord: function(point) {
_viewportCoordToDrawerCoord(point) {
var vpPoint = this.viewport.pixelFromPointNoRotate(point, true);
return new $.Point(
vpPoint.x * $.pixelDensityRatio,
vpPoint.y * $.pixelDensityRatio
);
},
}
/**
* @private
@ -256,7 +250,7 @@ $.DrawerBase.prototype = {
* and pixelDensityRatio
* @returns {Dictionary} {x, y} size of the canvas
*/
_calculateCanvasSize: function() {
_calculateCanvasSize() {
var pixelDensityRatio = $.pixelDensityRatio;
var viewportSize = this.viewport.getContainerSize();
return {
@ -264,15 +258,240 @@ $.DrawerBase.prototype = {
x: Math.round(viewportSize.x * pixelDensityRatio),
y: Math.round(viewportSize.y * pixelDensityRatio)
};
},
};
Object.defineProperty($.DrawerBase.prototype, "isOpenSeadragonDrawer", {
get: function get() {
return true;
}
});
}
$.DrawerBase = DrawerBase;
// $.DrawerBase = function( options ) {
// $.console.assert( options.viewer, "[Drawer] options.viewer is required" );
// //backward compatibility for positional args while preferring more
// //idiomatic javascript options object as the only argument
// var args = arguments;
// if( !$.isPlainObject( options ) ){
// options = {
// source: args[ 0 ], // Reference to Viewer tile source.
// viewport: args[ 1 ], // Reference to Viewer viewport.
// element: args[ 2 ] // Parent element.
// };
// }
// $.console.assert( options.viewport, "[Drawer] options.viewport is required" );
// $.console.assert( options.element, "[Drawer] options.element is required" );
// if ( options.source ) {
// $.console.error( "[Drawer] options.source is no longer accepted; use TiledImage instead" );
// }
// this.viewer = options.viewer;
// this.viewport = options.viewport;
// this.debugGridColor = typeof options.debugGridColor === 'string' ? [options.debugGridColor] : options.debugGridColor || $.DEFAULT_SETTINGS.debugGridColor;
// if (options.opacity) {
// $.console.error( "[Drawer] options.opacity is no longer accepted; set the opacity on the TiledImage instead" );
// }
// this.useCanvas = $.supportsCanvas && ( this.viewer ? this.viewer.useCanvas : true );
// /**
// * The parent element of this Drawer instance, passed in when the Drawer was created.
// * The parent of {@link OpenSeadragon.DrawerBase#canvas}.
// * @member {Element} container
// * @memberof OpenSeadragon.DrawerBase#
// */
// this.container = $.getElement( options.element );
// /**
// * A &lt;canvas&gt; element if the browser supports them, otherwise a &lt;div&gt; element.
// * Child element of {@link OpenSeadragon.DrawerBase#container}.
// * @member {Element} canvas
// * @memberof OpenSeadragon.DrawerBase#
// */
// this.canvas = $.makeNeutralElement( this.useCanvas ? "canvas" : "div" );
// /**
// * @member {Element} element
// * @memberof OpenSeadragon.DrawerBase#
// * @deprecated Alias for {@link OpenSeadragon.DrawerBase#container}.
// */
// this.element = this.container;
// // TO DO: Does this need to be in DrawerBase, or only in Drawer implementations?
// // We force our container to ltr because our drawing math doesn't work in rtl.
// // This issue only affects our canvas renderer, but we do it always for consistency.
// // Note that this means overlays you want to be rtl need to be explicitly set to rtl.
// this.container.dir = 'ltr';
// if (this.useCanvas) {
// var viewportSize = this._calculateCanvasSize();
// this.canvas.width = viewportSize.x;
// this.canvas.height = viewportSize.y;
// }
// this.canvas.style.width = "100%";
// this.canvas.style.height = "100%";
// this.canvas.style.position = "absolute";
// $.setElementOpacity( this.canvas, this.opacity, true );
// // Allow pointer events to pass through the canvas element so implicit
// // pointer capture works on touch devices
// $.setElementPointerEventsNone( this.canvas );
// $.setElementTouchActionNone( this.canvas );
// // explicit left-align
// this.container.style.textAlign = "left";
// this.container.appendChild( this.canvas );
// this._checkForAPIOverrides();
// };
// /** @lends OpenSeadragon.DrawerBaseBase.prototype */
// $.DrawerBase.prototype = {
// // Drawer implementaions must define the next four methods. These are called
// // by core OSD and/or public APIs, and forcing overrides (even for nullop methods) makes the
// // behavior of the implementations explicitly clear in the code.
// // Whether these have been overridden by child classes is checked in the
// // constructor (via _checkForAPIOverrides).
// /**
// * @param tiledImage the TiledImage that is ready to be drawn
// */
// draw: function(tiledImage) {
// $.console.error('Drawer.draw must be implemented by child class');
// },
// /**
// * @returns {Boolean} True if rotation is supported.
// */
// canRotate: function() {
// $.console.error('Drawer.canRotate must be implemented by child class');
// },
// /**
// * Destroy the drawer (unload current loaded tiles)
// */
// destroy: function() {
// $.console.error('Drawer.destroy must be implemented by child class');
// },
// /**
// * Turns image smoothing on or off for this viewer. Note: Ignored in some (especially older) browsers that do not support this property.
// *
// * @function
// * @param {Boolean} [imageSmoothingEnabled] - Whether or not the image is
// * drawn smoothly on the canvas; see imageSmoothingEnabled in
// * {@link OpenSeadragon.Options} for more explanation.
// */
// setImageSmoothingEnabled: function(imageSmoothingEnabled){
// $.console.error('Drawer.setImageSmoothingEnabled must be implemented by child class');
// },
// /**
// * Optional public API to draw a rectangle (e.g. for debugging purposes)
// * Child classes can override this method if they wish to support this
// * @param {OpenSeadragon.Rect} rect
// */
// drawDebuggingRect: function(rect) {
// $.console.warn('[drawer].drawDebuggingRect is not implemented by this drawer');
// },
// // Deprecated functions
// clear: function(){
// $.console.warn('[drawer].clear() is deprecated. The drawer is responsible for clearing itself as needed before drawing tiles.');
// },
// // Private functions
// /**
// * @private
// * @inner
// * Ensures that child classes have provided implementations for public API methods
// * draw, canRotate, destroy, and setImageSmoothinEnabled. Throws an exception if the original
// * placeholder methods are still in place.
// */
// _checkForAPIOverrides: function(){
// if(this.draw === $.DrawerBase.prototype.draw){
// throw("[drawer].draw must be implemented by child class");
// }
// if(this.canRotate === $.DrawerBase.prototype.canRotate){
// throw("[drawer].canRotate must be implemented by child class");
// }
// if(this.destroy === $.DrawerBase.prototype.destroy){
// throw("[drawer].destroy must be implemented by child class");
// }
// if(this.setImageSmoothingEnabled === $.DrawerBase.prototype.setImageSmoothingEnabled){
// throw("[drawer].setImageSmoothingEnabled must be implemented by child class");
// }
// },
// // Utility functions internal API
// /**
// * @private
// * @inner
// * Scale from OpenSeadragon viewer rectangle to drawer rectangle
// * (ignoring rotation)
// * @param {OpenSeadragon.Rect} rectangle - The rectangle in viewport coordinate system.
// * @returns {OpenSeadragon.Rect} Rectangle in drawer coordinate system.
// */
// _viewportToDrawerRectangle: function(rectangle) {
// var topLeft = this.viewport.pixelFromPointNoRotate(rectangle.getTopLeft(), true);
// var size = this.viewport.deltaPixelsFromPointsNoRotate(rectangle.getSize(), true);
// return new $.Rect(
// topLeft.x * $.pixelDensityRatio,
// topLeft.y * $.pixelDensityRatio,
// size.x * $.pixelDensityRatio,
// size.y * $.pixelDensityRatio
// );
// },
// /**
// * @private
// * @inner
// * This function converts the given point from to the drawer coordinate by
// * multiplying it with the pixel density.
// * This function does not take rotation into account, thus assuming provided
// * point is at 0 degree.
// * @param {OpenSeadragon.Point} point - the pixel point to convert
// * @returns {OpenSeadragon.Point} Point in drawer coordinate system.
// */
// _viewportCoordToDrawerCoord: function(point) {
// var vpPoint = this.viewport.pixelFromPointNoRotate(point, true);
// return new $.Point(
// vpPoint.x * $.pixelDensityRatio,
// vpPoint.y * $.pixelDensityRatio
// );
// },
// /**
// * @private
// * @inner
// * Calculate width and height of the canvas based on viewport dimensions
// * and pixelDensityRatio
// * @returns {Dictionary} {x, y} size of the canvas
// */
// _calculateCanvasSize: function() {
// var pixelDensityRatio = $.pixelDensityRatio;
// var viewportSize = this.viewport.getContainerSize();
// return {
// // canvas width and height are integers
// x: Math.round(viewportSize.x * pixelDensityRatio),
// y: Math.round(viewportSize.y * pixelDensityRatio)
// };
// },
// };
// Object.defineProperty($.DrawerBase.prototype, "isOpenSeadragonDrawer", {
// get: function get() {
// return true;
// }
// });
}( OpenSeadragon ));

View File

@ -1,8 +1,8 @@
/*
* OpenSeadragon - Drawer
* OpenSeadragon - HTMLDrawer
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors
* Copyright (C) 2010-2023 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -44,36 +44,54 @@
* @param {Element} options.element - Parent element.
* @param {Number} [options.debugGridColor] - See debugGridColor in {@link OpenSeadragon.Options} for details.
*/
$.HTMLDrawer = function(options) {
$.DrawerBase.call(this, options);
class HTMLDrawer extends $.DrawerBase{
constructor(options){
super(options);
/**
* 2d drawing context for {@link OpenSeadragon.Drawer#canvas} if it's a &lt;canvas&gt; element, otherwise null.
* @member {Object} context
* @memberof OpenSeadragon.Drawer#
*/
this.context = null;
/**
* 2d drawing context for {@link OpenSeadragon.Drawer#canvas} if it's a &lt;canvas&gt; element, otherwise null.
* @member {Object} context
* @memberof OpenSeadragon.Drawer#
*/
this.context = null;
// We force our container to ltr because our drawing math doesn't work in rtl.
// This issue only affects our canvas renderer, but we do it always for consistency.
// Note that this means overlays you want to be rtl need to be explicitly set to rtl.
this.container.dir = 'ltr';
// We force our container to ltr because our drawing math doesn't work in rtl.
// This issue only affects our canvas renderer, but we do it always for consistency.
// Note that this means overlays you want to be rtl need to be explicitly set to rtl.
this.container.dir = 'ltr';
};
/**
* Override default element to enforce div for HTMLDrawer
*/
this.canvas.parentNode.removeChild(this.canvas);
this.canvas = $.makeNeutralElement( "div" );
$.extend( $.HTMLDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadragon.Drawer.prototype */ {
this.canvas.style.width = "100%";
this.canvas.style.height = "100%";
this.canvas.style.position = "absolute";
$.setElementOpacity( this.canvas, this.opacity, true );
// Allow pointer events to pass through the canvas element so implicit
// pointer capture works on touch devices
$.setElementPointerEventsNone( this.canvas );
$.setElementTouchActionNone( this.canvas );
// explicit left-align
this.container.style.textAlign = "left";
this.container.appendChild( this.canvas );
}
/**
* Draws the TiledImages
*/
draw: function(tiledImages) {
draw(tiledImages) {
var _this = this;
this._prepareNewFrame(); // prepare to draw a new frame
tiledImages.forEach(function(tiledImage){
if (tiledImage.opacity !== 0 || tiledImage._preload) {
// _this._updateViewportWithTiledImage(tiledImage);
_this._drawTiles(tiledImage);
}
else {
@ -81,23 +99,22 @@ $.extend( $.HTMLDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadrag
}
});
},
}
/**
* @returns {Boolean} False - rotation is not supported.
*/
canRotate: function() {
canRotate() {
return false;
},
}
/**
* Destroy the drawer (unload current loaded tiles)
*/
destroy: function() {
destroy() {
//force unloading of current canvas (1x1 will be gc later, trick not necessarily needed)
this.canvas.width = 1;
this.canvas.height = 1;
},
this.canvas.innerHTML = "";
}
/**
* Turns image smoothing on or off for this viewer. Note: Ignored by HTML Drawer
@ -107,10 +124,10 @@ $.extend( $.HTMLDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadrag
* drawn smoothly on the canvas; see imageSmoothingEnabled in
* {@link OpenSeadragon.Options} for more explanation.
*/
setImageSmoothingEnabled: function(){
setImageSmoothingEnabled(){
// noop - HTML Drawer does not deal with this property
$.console.warn('HTMLDrawer.setImageSmoothingEnabled does not have an effect.');
},
}
/**
* @private
@ -118,108 +135,9 @@ $.extend( $.HTMLDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadrag
* Clears the Drawer so it's ready to draw another frame.
*
*/
_prepareNewFrame: function() {
_prepareNewFrame() {
this.canvas.innerHTML = "";
},
/* Methods from TiledImage */
/**
* @private
* @inner
* Handles drawing a single TiledImage to the canvas
*
*/
_updateViewportWithTiledImage: function(tiledImage) {
var _this = this;
tiledImage._needsDraw = false;
tiledImage._tilesLoading = 0;
tiledImage.loadingCoverage = {};
// Reset tile's internal drawn state
while (tiledImage.lastDrawn.length > 0) {
var tile = tiledImage.lastDrawn.pop();
tile.beingDrawn = false;
}
var drawArea = tiledImage.getDrawArea();
if(!drawArea){
return;
}
function updateTile(info){
var tile = info.tile;
if(tile && tile.loaded){
var needsDraw = _this._blendTile(
tiledImage,
tile,
tile.x,
tile.y,
info.level,
info.levelOpacity,
info.currentTime
);
if(needsDraw){
tiledImage._needsDraw = true;
}
}
}
var infoArray = tiledImage.getTileInfoForDrawing();
infoArray.forEach(updateTile);
this._drawTiles(tiledImage);
},
/**
* @private
* @inner
* Updates the opacity of a tile according to the time it has been on screen
* to perform a fade-in.
* Updates coverage once a tile is fully opaque.
* Returns whether the fade-in has completed.
*
* @param {OpenSeadragon.Tile} tile
* @param {Number} x
* @param {Number} y
* @param {Number} level
* @param {Number} levelOpacity
* @param {Number} currentTime
* @returns {Boolean}
*/
_blendTile: function( tiledImage, tile, x, y, level, levelOpacity, currentTime ){
var blendTimeMillis = 1000 * tiledImage.blendTime,
deltaTime,
opacity;
if ( !tile.blendStart ) {
tile.blendStart = currentTime;
}
deltaTime = currentTime - tile.blendStart;
opacity = blendTimeMillis ? Math.min( 1, deltaTime / ( blendTimeMillis ) ) : 1;
if ( tiledImage.alwaysBlend ) {
opacity *= levelOpacity;
}
tile.opacity = opacity;
tiledImage.lastDrawn.push( tile );
if ( opacity === 1 ) {
tiledImage._setCoverage( tiledImage.coverage, level, x, y, true );
tiledImage._hasOpaqueTile = true;
} else if ( deltaTime < blendTimeMillis ) {
return true;
}
return false;
},
}
/**
* @private
@ -227,7 +145,7 @@ $.extend( $.HTMLDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadrag
* Draws a TiledImage.
*
*/
_drawTiles: function( tiledImage ) {
_drawTiles( tiledImage ) {
var lastDrawn = tiledImage.lastDrawn;
if (tiledImage.opacity === 0 || (lastDrawn.length === 0 && !tiledImage.placeholderFillStyle)) {
return;
@ -258,9 +176,7 @@ $.extend( $.HTMLDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadrag
}
}
},
/* Methods from Tile */
}
/**
* @private
@ -270,22 +186,11 @@ $.extend( $.HTMLDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadrag
* @param {Function} drawingHandler - Method for firing the drawing event if using canvas.
* drawingHandler({context, tile, rendered})
*/
_drawTile: function( tile ) {
_drawTile( tile ) {
$.console.assert(tile, '[Drawer._drawTile] tile is required');
this._drawTileToHTML( tile, this.canvas );
},
let container = this.canvas;
/**
* @private
* @inner
* Renders the tile in an html container.
* @function
* @param {OpenSeadragon.Tile} tile
* @param {Element} container
*/
_drawTileToHTML: function( tile, container ) {
if (!tile.cacheImageRecord) {
$.console.warn(
'[Drawer._drawTileToHTML] attempting to draw tile %s when it\'s not cached',
@ -319,6 +224,7 @@ $.extend( $.HTMLDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadrag
tile.style = tile.element.style;
tile.style.position = "absolute";
}
if ( tile.element.parentNode !== container ) {
container.appendChild( tile.element );
}
@ -336,11 +242,11 @@ $.extend( $.HTMLDrawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadrag
}
$.setElementOpacity( tile.element, tile.opacity );
},
});
}
}
$.HTMLDrawer = HTMLDrawer;
}( OpenSeadragon ));

View File

@ -38,6 +38,7 @@
border: thin black solid;
padding:10px;
display:inline-block;
width:95%;
}
.description pre{
display:inline-block;
@ -164,9 +165,35 @@
</div>
<h2>HTMLDrawer: legacy pre-HTML5 drawer that uses &lt;img&gt; elements for tiles</h2>
<div class="mirrored">
<div>
<div class="description">
HTML-based rendering can be selected in two different ways:
</div>
<pre class="example-code">
// via the useCanvas option:
let viewer = OpenSeadragon({
...
useCanvas: false,
...
});
// or by passing the HTMLDrawer constructor
let viewer = OpenSeadragon({
...
customDrawer:OpenSeadragon.HTMLDrawer,
...
});
</pre>
</div>
<div id="htmldrawer" class="viewer-container"></div>
</div>
</div>
</body>
</html>

View File

@ -69,6 +69,19 @@ var viewer2 = window.viewer2 = OpenSeadragon({
ajaxWithCredentials: false
});
// Single viewer showing how to use plugin Drawer via configuration
// Also shows sequence mode
var viewer3 = window.viewer3 = OpenSeadragon({
id: "htmldrawer",
prefixUrl: "../../build/openseadragon/images/",
minZoomImageRatio:0.01,
customDrawer: OpenSeadragon.HTMLDrawer,
tileSources: [sources['leaves'], sources['rainbow'], sources['duomo']],
sequenceMode: true,
crossOriginPolicy: 'Anonymous',
ajaxWithCredentials: false
});