Home Manual Reference Source Repository

src/core/timeline-time-context.js

import scales from '../utils/scales';


/**
 * Defines and maintains global aspects of the visualization concerning the
 * relations between time and pixels.
 *
 * The `TimelineTimeContext` instance (unique across a visualization) keeps the
 * main reference on how many pixels should be used to represent one second
 * though its `timeToPixel` method. The attributes `zoom`, `offset` (i.e. from
 * origin) and `visibleWidth` allow for navigating in time and for maintaining
 * view consistency upon the DOM structure (`<svg>` and `<g>` tags) created by
 * the registered tracks.
 *
 * It also maintain an array of all references to `LayerTimeContext` instances
 * to propagate to `layers`, changes made on the time to pixel representation.
 *
 * [example usage](./examples/time-contexts.html)
 */
export default class TimelineTimeContext {
  /**
   * @param {Number} pixelsPerSecond - The number of pixels that should be
   *    used to display one second.
   * @param {Number} visibleWidth - The default with of the visible area
   *    displayed in `tracks` (in pixels).
   */
  constructor(pixelsPerSecond, visibleWidth) {
    this._children = [];

    this._timeToPixel = null;
    this._offset = 0;
    this._zoom = 1;
    this._computedPixelsPerSecond = pixelsPerSecond;
    // params
    this._visibleWidth = visibleWidth;
    this._maintainVisibleDuration = false;

    // create the timeToPixel scale
    const scale = scales.linear()
      .domain([0, 1])
      .range([0, pixelsPerSecond]);

    this._timeToPixel = scale;

    this._originalPixelsPerSecond = this._computedPixelsPerSecond;
  }

  /**
   * Returns the number of pixels per seconds ignoring the current zoom value.
   *
   * @type {Number}
   */
  get pixelsPerSecond() {
    return this._originalPixelsPerSecond;
  }

  /**
   * Updates all the caracteristics of this object according to the new
   * given value of pixels per seconds. Propagates the changes to the
   * `LayerTimeContext` children.
   *
   * @type {Number}
   */
  set pixelsPerSecond(value) {
    this._computedPixelsPerSecond = value * this.zoom;
    this._originalPixelsPerSecond = value;
    this._updateTimeToPixelRange();

    // force children scale update
    this._children.forEach(function(child) {
      if (child.stretchRatio !== 1) { return; }
      child.stretchRatio = child.stretchRatio;
    });
  }

  /**
   * Returns the number of pixels per seconds including the current zoom value.
   *
   * @type {Number}
   */
  get computedPixelsPerSecond() {
    return this._computedPixelsPerSecond;
  }

  /**
   * Returns the current offset applied to the registered `Track` instances
   * from origin (in seconds).
   *
   * @type {Number}
   */
  get offset() {
    return this._offset;
  }

  /**
   * Sets the offset to apply to the registered `Track` instances from origin
   * (in seconds).
   *
   * @type {Number}
   */
  set offset(value) {
    this._offset = value;
  }

  /**
   * Returns the current zoom level applied to the whole visualization.
   *
   * @type {Number}
   */
  get zoom() {
    return this._zoom;
  }

  /**
   * Sets the zoom ratio for the whole visualization.
   *
   * @type {Number}
   */
  set zoom(value) {
    // Compute change to propagate to children who have their own timeToPixel
    const ratioChange = value / this._zoom;
    this._zoom = value;
    this._computedPixelsPerSecond = this._originalPixelsPerSecond * value;
    this._updateTimeToPixelRange();

    this._children.forEach(function(child) {
      if (child.stretchRatio !== 1) { return; }
      child.stretchRatio = child.stretchRatio * ratioChange;
    });
  }

  /**
   * Returns the visible width of the `Track` instances.
   *
   * @type {Number}
   */
  get visibleWidth() {
    return this._visibleWidth;
  }

  /**
   * Sets the visible width of the `Track` instances.
   *
   * @type {Number}
   */
  set visibleWidth(value) {
    const widthRatio = value / this.visibleWidth;
    this._visibleWidth = value;

    if (this.maintainVisibleDuration) {
      this.pixelsPerSecond = this._computedPixelsPerSecond * widthRatio;
    }
  }

  /**
   * Returns the duration displayed by `Track` instances.
   *
   * @type {Number}
   */
  get visibleDuration() {
    return this.visibleWidth / this._computedPixelsPerSecond;
  }

  /**
   * Returns if the duration displayed by tracks should be maintained when
   * their width is updated.
   *
   * @type {Number}
   */
  get maintainVisibleDuration() {
    return this._maintainVisibleDuration;
  }

  /**
   * Defines if the duration displayed by tracks should be maintained when
   * their width is updated.
   *
   * @type {Boolean}
   */
  set maintainVisibleDuration(bool) {
    this._maintainVisibleDuration = bool;
  }

  /**
   * Returns the time to pixel trasfert function.
   *
   * @type {Function}
   */
  get timeToPixel() {
    return this._timeToPixel;
  }

  _updateTimeToPixelRange() {
    this.timeToPixel.range([0, this._computedPixelsPerSecond]);
  }
}