Skip to content
Snippets Groups Projects
Select Git revision
  • 5a166d556c1e4db4e958fa87cd4f31acbef5dd60
  • annotation-on-video default protected
  • demo_ci
  • 3-upstream-01022023
  • master
  • gh3538-captions
  • 16-adapt-for-images-annot
  • 15-api-for-annotations-on-video
  • 15-annotations-on-videos
  • video_for_annotations
  • wip-1-annotations-on-videos
  • 9-videoviewer-tests
  • 9_wip_videotests
  • 6-fix-tests-and-ci
  • _fix_ci
  • wip-webpack-from-git
16 results

CanvasAnnotationDisplay.js

Blame
  • user avatar
    Chris Beer authored
    Also, short-circuit annotation drawing if the globalAlpha is 0.
    5a166d55
    History
    CanvasAnnotationDisplay.js 4.12 KiB
    /**
     * CanvasAnnotationDisplay - class used to display a SVG and fragment based
     * annotations.
     */
    export default class CanvasAnnotationDisplay {
      /** */
      constructor({
        resource, palette, zoomRatio, offset, selected, hovered,
      }) {
        this.resource = resource;
        this.palette = palette;
        this.zoomRatio = zoomRatio;
        this.offset = offset;
        this.selected = selected;
        this.hovered = hovered;
      }
    
      /** */
      toContext(context) {
        this.context = context;
        if (this.resource.svgSelector) {
          this.svgContext();
        } else {
          this.fragmentContext();
        }
      }
    
      /** */
      get svgString() {
        return this.resource.svgSelector.value;
      }
    
      /** */
      svgContext() {
        let currentPalette;
        if (this.hovered) {
          currentPalette = this.palette.hovered;
        } else if (this.selected) {
          currentPalette = this.palette.selected;
        } else {
          currentPalette = this.palette.default;
        }
    
        if (currentPalette.globalAlpha === 0) return;
    
        [...this.svgPaths].forEach((element) => {
          /**
           *  Note: Path2D is not supported in IE11.
           *  TODO: Support multi canvas offset
           *  One example: https://developer.mozilla.org/en-US/docs/Web/API/Path2D/addPath
           */
          this.context.save();
          this.context.translate(this.offset.x, this.offset.y);
          const p = new Path2D(element.attributes.d.nodeValue);
    
          // Setup styling from SVG -> Canvas
          this.context.strokeStyle = this.color;
          if (element.attributes['stroke-dasharray']) {
            this.context.setLineDash(element.attributes['stroke-dasharray'].nodeValue.split(','));
          }
          const svgToCanvasMap = {
            fill: 'fillStyle',
            stroke: 'strokeStyle',
            'stroke-dashoffset': 'lineDashOffset',
            'stroke-linecap': 'lineCap',
            'stroke-linejoin': 'lineJoin',
            'stroke-miterlimit': 'miterlimit',
            'stroke-width': 'lineWidth',
          };
          Object.keys(svgToCanvasMap).forEach((key) => {
            if (element.attributes[key]) {
              this.context[svgToCanvasMap[key]] = element.attributes[key].nodeValue;
            }
          });
    
          // Resize the stroke based off of the zoomRatio (currentZoom / maxZoom)
          this.context.lineWidth /= this.zoomRatio;
    
          // Reset the color if it is selected or hovered on
          if (this.selected || this.hovered) {
            this.context.strokeStyle = currentPalette.strokeStyle || currentPalette.fillStyle;
          }
    
          if (element.attributes['stroke-opacity']) {
            this.context.globalAlpha = currentPalette.globalAlpha * element.attributes['stroke-opacity'].nodeValue;
          } else {
            this.context.globalAlpha = currentPalette.globalAlpha;
          }
    
          this.context.stroke(p);
    
          // Wait to set the fill, so we can adjust the globalAlpha value if we need to
          if (element.attributes.fill && element.attributes.fill.nodeValue !== 'none') {
            if (element.attributes['fill-opacity']) {
              this.context.globalAlpha = currentPalette.globalAlpha * element.attributes['fill-opacity'].nodeValue;
            } else {
              this.context.globalAlpha = currentPalette.globalAlpha;
            }
            this.context.fill(p);
          }
          this.context.restore();
        });
      }
    
      /** */
      fragmentContext() {
        const fragment = this.resource.fragmentSelector;
        fragment[0] += this.offset.x;
        fragment[1] += this.offset.y;
    
        let currentPalette;
        if (this.selected) {
          currentPalette = this.palette.selected;
        } else if (this.hovered) {
          currentPalette = this.palette.hovered;
        } else {
          currentPalette = this.palette.default;
        }
    
        this.context.save();
        Object.keys(currentPalette).forEach((key) => {
          this.context[key] = currentPalette[key];
        });
    
        if (currentPalette.globalAlpha === 0) return;
    
        if (currentPalette.fillStyle) {
          this.context.fillRect(...fragment);
        } else {
          this.context.lineWidth = 1 / this.zoomRatio;
          this.context.strokeRect(...fragment);
        }
    
        this.context.restore();
      }
    
      /** */
      get svgPaths() {
        const parser = new DOMParser();
        const xmlDoc = parser.parseFromString(this.svgString, 'text/xml');
        return xmlDoc.getElementsByTagName('path');
      }
    }