UNPKG

visjs-network

Version:

A dynamic, browser-based network visualization library.

167 lines (146 loc) 4.28 kB
/** * Associates a canvas to a given image, containing a number of renderings * of the image at various sizes. * * This technique is known as 'mipmapping'. * * NOTE: Images can also be of type 'data:svg+xml`. This code also works * for svg, but the mipmapping may not be necessary. * * @param {Image} image */ class CachedImage { /** * @ignore */ constructor() { // eslint-disable-line no-unused-vars this.NUM_ITERATIONS = 4 // Number of items in the coordinates array this.image = new Image() this.canvas = document.createElement('canvas') } /** * Called when the image has been successfully loaded. */ init() { if (this.initialized()) return this.src = this.image.src // For same interface with Image var w = this.image.width var h = this.image.height // Ease external access this.width = w this.height = h var h2 = Math.floor(h / 2) var h4 = Math.floor(h / 4) var h8 = Math.floor(h / 8) var h16 = Math.floor(h / 16) var w2 = Math.floor(w / 2) var w4 = Math.floor(w / 4) var w8 = Math.floor(w / 8) var w16 = Math.floor(w / 16) // Make canvas as small as possible this.canvas.width = 3 * w4 this.canvas.height = h2 // Coordinates and sizes of images contained in the canvas // Values per row: [top x, left y, width, height] this.coordinates = [ [0, 0, w2, h2], [w2, 0, w4, h4], [w2, h4, w8, h8], [5 * w8, h4, w16, h16] ] this._fillMipMap() } /** * @return {Boolean} true if init() has been called, false otherwise. */ initialized() { return this.coordinates !== undefined } /** * Redraw main image in various sizes to the context. * * The rationale behind this is to reduce artefacts due to interpolation * at differing zoom levels. * * Source: http://stackoverflow.com/q/18761404/1223531 * * This methods takes the resizing out of the drawing loop, in order to * reduce performance overhead. * * TODO: The code assumes that a 2D context can always be gotten. This is * not necessarily true! OTOH, if not true then usage of this class * is senseless. * * @private */ _fillMipMap() { var ctx = this.canvas.getContext('2d') // First zoom-level comes from the image var to = this.coordinates[0] ctx.drawImage(this.image, to[0], to[1], to[2], to[3]) // The rest are copy actions internal to the canvas/context for (let iterations = 1; iterations < this.NUM_ITERATIONS; iterations++) { let from = this.coordinates[iterations - 1] let to = this.coordinates[iterations] ctx.drawImage( this.canvas, from[0], from[1], from[2], from[3], to[0], to[1], to[2], to[3] ) } } /** * Draw the image, using the mipmap if necessary. * * MipMap is only used if param factor > 2; otherwise, original bitmap * is resized. This is also used to skip mipmap usage, e.g. by setting factor = 1 * * Credits to 'Alex de Mulder' for original implementation. * * @param {CanvasRenderingContext2D} ctx context on which to draw zoomed image * @param {Float} factor scale factor at which to draw * @param {number} left * @param {number} top * @param {number} width * @param {number} height */ drawImageAtPosition(ctx, factor, left, top, width, height) { if (!this.initialized()) return //can't draw image yet not intialized if (factor > 2) { // Determine which zoomed image to use factor *= 0.5 let iterations = 0 while (factor > 2 && iterations < this.NUM_ITERATIONS) { factor *= 0.5 iterations += 1 } if (iterations >= this.NUM_ITERATIONS) { iterations = this.NUM_ITERATIONS - 1 } //console.log("iterations: " + iterations); let from = this.coordinates[iterations] ctx.drawImage( this.canvas, from[0], from[1], from[2], from[3], left, top, width, height ) } else { // Draw image directly ctx.drawImage(this.image, left, top, width, height) } } } export default CachedImage