⌈⌋ ⎇ branch:  Bitrhythm


Artifact Content

Artifact 782e1590bd5588509a1ca5d537f1feb108e80c713e6104f957c279f3c3a4fd69:


'use strict';

let svg = require('../util/svg');
let math = require('../util/math');
let Interface = require('../core/interface');
let Step = require('../models/step');
import * as Interaction from '../util/interaction';

/**
* Pan
*
* @description Stereo crossfader.
*
* @demo <span nexus-ui="pan"></span>
*
* @example
* var pan = new Nexus.Pan('#target')
*
* @output
* change
* Fires any time the interface's value changes. <br>
* The event data is an object containing the interface's <i>value</i> (-1 to 1), as well as <i>L</i> and <i>R</i> amplitude values (0-1) for left and right speakers, calculated by a square-root crossfade algorithm.
*
* @outputexample
* pan.on('change',function(v) {
*   console.log(v);
* })
*
*
*/

export default class Pan extends Interface {

  constructor() {

    let options = ['scale','value'];

    let defaults = {
      'size': [120,20],
      'orientation': 'horizontal',
      'mode': 'relative',
      'scale': [-1,1],
      'step': 0,
      'value': 0,
      'hasKnob': true
    };

    super(arguments,options,defaults);

    this.orientation = this.settings.orientation;

    this.mode = this.settings.mode;

    this.hasKnob = this.settings.hasKnob;

    // this.step should eventually be get/set
    // updating it will update the _value step model
    this.step = this.settings.step; // float

    this._value = new Step(this.settings.scale[0], this.settings.scale[1], this.settings.step, this.settings.value);

    this.init();

    this.position = new Interaction.Handle(this.mode,this.orientation,[0,this.width],[this.height,0]);
    this.position.value = this._value.normalized;

    this.value = this._value.value;

    this.emit('change',this.value);

  }

  buildInterface() {

    this.bar = svg.create('rect');
    this.knob = svg.create('circle');

    this.element.appendChild(this.bar);
    this.element.appendChild(this.knob);

  }

  sizeInterface() {

    if (this.position) {
      this.position.resize([0,this.width],[this.height,0]);
    }

    if (this.width < this.height) {
      this.orientation = 'vertical';
    } else {
      this.orientation = 'horizontal';
    }

    let x, y, w, h, barOffset, cornerRadius;
    this.knobData = {
      level: 0,
      r: 0
    };

    if (this.orientation === 'vertical') {
      this.thickness = this.width / 2;
    	x = this.width/2;
    	y = 0;
    	w = this.thickness;
    	h = this.height;
      this.knobData.r = this.thickness * 0.8;
    	this.knobData.level = h-this.knobData.r-this.normalized*(h-this.knobData.r*2);
      barOffset = 'translate('+this.thickness*(-1)/2+',0)';
      cornerRadius = w/2;
    } else {
      this.thickness = this.height / 2;
    	x = 0;
    	y = this.height/2;
    	w = this.width;
    	h = this.thickness;
      this.knobData.r = this.thickness * 0.8;
    	this.knobData.level = this.normalized*(w-this.knobData.r*2)+this.knobData.r;
      barOffset = 'translate(0,'+this.thickness*(-1)/2+')';
      cornerRadius = h/2;
    }

    this.bar.setAttribute('x',x);
    this.bar.setAttribute('y',y);
    this.bar.setAttribute('transform',barOffset);
    this.bar.setAttribute('rx',cornerRadius); // corner radius
    this.bar.setAttribute('ry',cornerRadius);
    this.bar.setAttribute('width',w);
    this.bar.setAttribute('height',h);

    if (this.orientation === 'vertical') {
      this.knob.setAttribute('cx',x);
      this.knob.setAttribute('cy',this.knobData.level);
    } else {
      this.knob.setAttribute('cx',this.knobData.level);
      this.knob.setAttribute('cy',y);
    }
    this.knob.setAttribute('r',this.knobData.r);

  }

  colorInterface() {

    this.bar.setAttribute('fill', this.colors.fill);
    this.knob.setAttribute('fill', this.colors.accent);

    if (!this.hasKnob) {
      this.knob.setAttribute('fill','transparent');
    }

  }

  render() {
    if (!this.clicked) {
      this.knobData.r = this.thickness*0.75;
    }
    this.knob.setAttribute('r',this.knobData.r);

    if (this.orientation === 'vertical') {
  	   this.knobData.level = this.knobData.r+this._value.normalized*(this.height-this.knobData.r*2);
       this.knob.setAttribute('cy',this.height - this.knobData.level);
    } else {
  	   this.knobData.level = this._value.normalized*(this.width-this.knobData.r*2)+this.knobData.r;
       this.knob.setAttribute('cx',this.knobData.level);
    }
  }


  click() {
    this.knobData.r = this.thickness*0.9;
    this.position.anchor = this.mouse;
    this.move();
  }

  move() {
    if (this.clicked) {
      this.position.update(this.mouse);

      this.value = this._value.updateNormal( this.position.value );

      this.emit('change',{
        value: this.value,
        L: Math.pow( math.scale(this.value,-1,1,1,0), 2),
        R: Math.pow( math.scale(this.value,-1,1,0,1), 2)
      });

    }
  }

  release() {
    this.render();
  }

  /**
  The position of crossfader, from -1 (left) to 1 (right). Setting this value updates the interface and triggers the output event.
  @type {number}
  */
  get value() {
    return this._value.value;
  }

  set value(value) {
    this._value.update(value);
    this.position.value = this._value.normalized;
    this.emit('change',{
      value: this.value,
      L: Math.pow( math.scale(this.value,-1,1,1,0), 2),
      R: Math.pow( math.scale(this.value,-1,1,0,1), 2)
    });
    this.render();
  }

  get normalized() {
    return this._value.normalized;
  }

}