Skip to content
Snippets Groups Projects
Select Git revision
  • 5f6f83c4a918bceab6568e9ae048f15c9b008355
  • demo_ci_gitlab_pages default
  • demo_gitlab_ci
  • 5-images-in-annotations
  • 5-final-images
  • 5-chpk-images-in-annot
  • tetras-main protected
  • 5-rebase-images-in-annot
  • 5-wip-images-in-annot
  • tmp
  • 1-edit-annotations-on-videos
  • 5-old-images-in-annotations
  • old_demo_ci_gitlab_pages
  • images_annotations
  • wip
  • devsetup
  • wip-annot-video-ui
  • wip-annotations-on-videos
  • master
  • v0.4.0_react16
  • wip-debugging-annotations
21 results

AnnotationCreation.js

Blame
  • Forked from IIIF / Mirador / Mirador annotations
    Source project has a limited visibility.
    OpenSeadragonViewer.js 5.85 KiB
    import React, { Component } from 'react';
    import PropTypes from 'prop-types';
    import OpenSeadragon from 'openseadragon';
    import debounce from 'lodash/debounce';
    import ns from '../config/css-ns';
    import ZoomControls from '../containers/ZoomControls';
    
    /**
     * Represents a OpenSeadragonViewer in the mirador workspace. Responsible for mounting
     * and rendering OSD.
     */
    class OpenSeadragonViewer extends Component {
      /**
       * @param {Object} props
       */
      constructor(props) {
        super(props);
    
        this.viewer = null;
        this.ref = React.createRef();
        this.onViewportChange = this.onViewportChange.bind(this);
      }
    
      /**
       * React lifecycle event
       */
      componentDidMount() {
        const { tileSources, viewer } = this.props;
        if (!this.ref.current) {
          return;
        }
        this.viewer = new OpenSeadragon({
          id: this.ref.current.id,
          preserveViewport: true,
          blendTime: 0.1,
          alwaysBlend: false,
          showNavigationControl: false,
          preserveImageSizeOnResize: true,
        });
        this.viewer.addHandler('viewport-change', debounce(this.onViewportChange, 300));
    
        if (viewer) {
          this.viewer.viewport.panTo(viewer, false);
          this.viewer.viewport.zoomTo(viewer.zoom, viewer, false);
        }
    
        tileSources.forEach((tileSource, i) => this.addTileSource(tileSource, i));
      }
    
      /**
       * When the tileSources change, make sure to close the OSD viewer.
       * When the viewport state changes, pan or zoom the OSD viewer as appropriate
       */
      componentDidUpdate(prevProps) {
        const { tileSources, viewer } = this.props;
        if (!this.tileSourcesMatch(prevProps.tileSources)) {
          this.viewer.close();
          Promise.all(
            tileSources.map((tileSource, i) => this.addTileSource(tileSource, i)),
          ).then(() => {
            if (tileSources[0]) {
              this.fitBounds(...this.boundsFromTileSources(), true);
            }
          });
        } else if (viewer) {
          const { viewport } = this.viewer;
    
          if (viewer.x !== viewport.centerSpringX.target.value
            || viewer.y !== viewport.centerSpringY.target.value) {
            this.viewer.viewport.panTo(viewer, false);
          }
    
          if (viewer.zoom !== viewport.zoomSpring.target.value) {
            this.viewer.viewport.zoomTo(viewer.zoom, viewer, false);
          }
        }
      }
    
      /**
       */
      componentWillUnmount() {
        this.viewer.removeAllHandlers();
      }
    
      /**
       * Forward OSD state to redux
       */
      onViewportChange(event) {
        const { updateViewport, windowId } = this.props;
    
        const { viewport } = event.eventSource;
    
        updateViewport(windowId, {
          x: viewport.centerSpringX.target.value,
          y: viewport.centerSpringY.target.value,
          zoom: viewport.zoomSpring.target.value,
        });
      }
    
      /**
       * boundsFromTileSources - calculates the overall width/height
       * based on 0 -> n tileSources
       */
      boundsFromTileSources() {
        const { tileSources } = this.props;
        const heights = [];
        const dimensions = [];
        tileSources.forEach((tileSource) => {
          heights.push(tileSource.height);
          dimensions.push({
            width: tileSource.width,
            height: tileSource.height,
          });
        });
        const minHeight = Math.min(...heights);
        let scaledWidth = 0;
        dimensions.forEach((dim) => {
          const aspectRatio = dim.width / dim.height;
          scaledWidth += Math.floor(minHeight * aspectRatio);
        });
        return [
          0,
          0,
          scaledWidth,
          minHeight,
        ];
      }
    
      /**
       * boundingRectFromTileSource - Creates a bounding rectangle
       * in the Viewports space, using the current tileSource and the tileSource
       * total area. Limitation, can only handle tileSources with a length of 1 or 2
       */
      boundingRectFromTileSource(tileSource, i) {
        const { tileSources } = this.props;
        const wholeBounds = this.boundsFromTileSources();
        const { width } = tileSources[i];
        const { height } = tileSources[i];
        const aspectRatio = width / height;
        const scaledWidth = Math.floor(wholeBounds[3] * aspectRatio);
        let x = 0;
        if (i === 1) {
          x = wholeBounds[2] - scaledWidth;
        }
        return [
          x,
          0,
          scaledWidth,
          wholeBounds[3],
        ];
      }
    
      /**
       */
      addTileSource(tileSource, i = 0) {
        return new Promise((resolve, reject) => {
          if (!this.viewer) {
            return;
          }
          this.viewer.addTiledImage({
            tileSource,
            fitBounds: new OpenSeadragon.Rect(
              ...this.boundingRectFromTileSource(tileSource, i),
            ),
            success: event => resolve(event),
            error: event => reject(event),
          });
        });
      }
    
      /**
       */
      fitBounds(x, y, w, h) {
        this.viewer.viewport.fitBounds(
          new OpenSeadragon.Rect(x, y, w, h),
          true,
        );
      }
    
      /**
       * tileSourcesMatch - compares previous tileSources to current to determine
       * whether a refresh of the OSD viewer is needed.
       * @param  {Array} prevTileSources
       * @return {Boolean}
       */
      tileSourcesMatch(prevTileSources) {
        const { tileSources } = this.props;
        return tileSources.some((tileSource, index) => {
          if (!prevTileSources[index]) {
            return false;
          }
          if (tileSource['@id'] === prevTileSources[index]['@id']) {
            return true;
          }
          return false;
        });
      }
    
      /**
       * Renders things
       */
      render() {
        const { windowId, children } = this.props;
        return (
          <>
            <div
              className={ns('osd-container')}
              id={`${windowId}-osd`}
              ref={this.ref}
            >
              { children }
            </div>
            <ZoomControls windowId={windowId} />
          </>
        );
      }
    }
    
    OpenSeadragonViewer.defaultProps = {
      children: null,
      tileSources: [],
      viewer: null,
    };
    
    OpenSeadragonViewer.propTypes = {
      children: PropTypes.element,
      tileSources: PropTypes.arrayOf(PropTypes.object),
      viewer: PropTypes.object, // eslint-disable-line react/forbid-prop-types
      updateViewport: PropTypes.func.isRequired,
      windowId: PropTypes.string.isRequired,
    };
    
    export default OpenSeadragonViewer;