Home Manual Reference Source Repository

src/states/brush-zoom-state.js

import ns from '../core/namespace';
import BaseState from './base-state';


/**
 * Protools like zoom with zone selection. Press space bar to reset zoom.
 *
 * [example usage](./examples/states-zoom.html)
 *
 * @todo - could also handle `g` and `h` keys to zoom-in, zoom-out.
 */
export default class BrushZoomState extends BaseState {
  constructor(timeline) {
    super(timeline);
  }

  handleEvent(e) {
    switch(e.type) {
      case 'mousedown':
        this.onMouseDown(e);
        break;
      case 'mousemove':
        this.onMouseMove(e);
        break;
      case 'mouseup':
        this.onMouseUp(e);
        break;
      case 'keydown':
        this.onKeyDown(e);
        break;
    }
  }

  onMouseDown(e) {
    this.brushes = [];
    this.startX = e.x;
    // create brush in each containers
    this.tracks.forEach((track) => {
      const interactions = track.$interactions;

      const brush = document.createElementNS(ns, 'rect');
      brush.setAttributeNS(null, 'height', track.height);
      brush.setAttributeNS(null, 'y', 0);
      brush.style.fill = '#787878';
      brush.style.opacity = 0.2;

      interactions.appendChild(brush);

      this.brushes.push(brush);
    });
  }

  onMouseMove(e) {
    // update brush
    const width = Math.abs(e.x - this.startX);
    const x = Math.min(e.x, this.startX);

    this.brushes.forEach((brush) => {
      brush.setAttributeNS(null, 'width', width);
      brush.setAttributeNS(null, 'x', x);
    });
  }

  onMouseUp(e) {
    // remove brush
    this.brushes.forEach((brush) => {
      brush.parentNode.removeChild(brush);
    });

    // update timeContext
    const startX = this.startX;
    const endX = e.x;
    // return if no drag
    if (Math.abs(startX - endX) < 1) { return; }

    const leftX = Math.max(0, Math.min(startX, endX));
    const rightX = Math.max(startX, endX);

    let minTime = this.timeline.timeToPixel.invert(leftX);
    let maxTime = this.timeline.timeToPixel.invert(rightX);

    const deltaDuration = maxTime - minTime;
    const zoom = this.timeline.visibleDuration / deltaDuration;

    this.timeline.offset -= minTime;
    this.timeline.zoom *= zoom;

    this.tracks.update();
  }

  onKeyDown(e) {
    // reset on space bar
    if (e.originalEvent.keyCode === 32) {
      this.timeline.offset = 0;
      this.timeline.zoom = 1;
      this.tracks.update();
    }
  }
}