⌈⌋ ⎇ branch:  Bitrhythm


Artifact Content

Artifact aa8e64a018c3eebf1bc7c76fdd7039dbb54f9f00ea56d0da3da7c71547987abf:


'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';

/**
* Dial
*
*
* @description Dial with radial or linear interaction.
*
* @demo <span nexus-ui="dial"></span>
*
* @example
* var dial = new Nexus.Dial('#target')
*
* @example
* var dial = new Nexus.Dial('#target',{
*   'size': [75,75],
*   'interaction': 'radial', // "radial", "vertical", or "horizontal"
*   'mode': 'relative', // "absolute" or "relative"
*   'min': 0,
*   'max': 1,
*   'step': 0,
*   'value': 0
* })
*
* @output
* change
* Fires any time the interface's value changes. <br>
* The event data is the number value of the interface.
*
* @outputexample
* dial.on('change',function(v) {
*   console.log(v);
* })
*
* @tutorial
* Dial
* ygGMxq
*
*/

export default class Dial extends Interface {

  constructor() {

    let options = ['min','max','value'];

    let defaults = {
      'size': [75,75],
      'interaction': 'radial', // radial, vertical, horizontal
      'mode': 'relative', // absolute, relative
      'min': 0,
      'max': 1,
      'step': 0,
      'value': 0
    };

    super(arguments,options,defaults);

    this.interaction = this.settings.interaction;

    this._value = new Step(this.settings.min, this.settings.max, this.settings.step, this.settings.value);

    this.position = new Interaction.Handle(this.settings.mode,this.interaction,[0,this.width],[this.height,0]);

    this.init();

    this.value = this._value.value;

    this.position.value = this._value.normalized;

    this.previousAngle = false;

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

  }

  buildInterface() {

    this.background = svg.create('circle');
    this.screw = svg.create('circle');
    this.handle = svg.create('path');
    this.handle2 = svg.create('path');
    this.handleFill = svg.create('path');
    this.handle2Fill = svg.create('path');
    this.handleLine = svg.create('path');

    this.element.appendChild(this.background);
    this.element.appendChild(this.handle);
    this.element.appendChild(this.handle2);
    this.element.appendChild(this.handleFill);
    this.element.appendChild(this.handle2Fill);
    this.element.appendChild(this.handleLine);
    this.element.appendChild(this.screw);

  }


  sizeInterface() {

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

    let center = {
      x: this.width/2,
      y: this.height/2
    };

    let diameter = Math.min(this.width,this.height);

    this.background.setAttribute('cx', center.x);
    this.background.setAttribute('cy', center.y);
    this.background.setAttribute('r', diameter/2-diameter/40);

    this.screw.setAttribute('cx', center.x);
    this.screw.setAttribute('cy', center.y);
    this.screw.setAttribute('r', diameter/12);

    let value = this.value;

    let handlePoints = {
      start: Math.PI*1.5,
      end: math.clip( math.scale(value,0,0.5,Math.PI*1.5,Math.PI*0.5) , Math.PI*0.5, Math.PI*1.5 )
    };
    let handle2Points = {
      start: Math.PI*2.5,
      end: math.clip( math.scale(value,0.5,1,Math.PI*2.5,Math.PI*1.5) , Math.PI*1.5, Math.PI*2.5 )
    };

    let handlePath = svg.arc(center.x, center.y, diameter/2-diameter/40, handlePoints.start, handlePoints.end);
    let handle2Path = svg.arc(center.x, center.y, diameter/2-diameter/40, handle2Points.start, handle2Points.end);

    this.handle.setAttribute('d',handlePath);
    this.handle.setAttribute('stroke-width', diameter/20);
    this.handle.setAttribute('fill', 'none');

    this.handle2.setAttribute('d',handle2Path);
    this.handle2.setAttribute('stroke-width', diameter/20);
    this.handle2.setAttribute('fill', 'none');

    handlePath += ' L '+center.x+' '+center.y;

    this.handleFill.setAttribute('d',handlePath);
    this.handleFill.setAttribute('fill-opacity', '0.3');

    handle2Path += ' L '+center.x+' '+center.y;

    this.handle2Fill.setAttribute('d',handle2Path);
    this.handle2Fill.setAttribute('fill-opacity', '0.3');

    let arcEndingA;
    if (value < 0.5) {
      arcEndingA = handlePoints.end;
    } else {
      arcEndingA = handle2Points.end;
    }

    let arcEndingX = center.x + Math.cos(arcEndingA) * (diameter/2);
    let arcEndingY = center.y + Math.sin(arcEndingA) * (diameter/2) * -1;

    this.handleLine.setAttribute('d','M '+center.x+' '+center.y+' L '+arcEndingX+' '+arcEndingY);
    this.handleLine.setAttribute('stroke-width', diameter/20);

  }

  colorInterface() {
    this.background.setAttribute('fill', this.colors.fill);
    this.screw.setAttribute('fill', this.colors.accent);
    this.handle.setAttribute('stroke', this.colors.accent);
    this.handle2.setAttribute('stroke', this.colors.accent);
    this.handleFill.setAttribute('fill', this.colors.accent);
    this.handle2Fill.setAttribute('fill', this.colors.accent);
    this.handleLine.setAttribute('stroke', this.colors.accent);

  }

  render() {
    let value = this._value.normalized;

    let center = {
      x: this.width/2,
      y: this.height/2
    };

    let diameter = Math.min(this.width,this.height);

    let handlePoints = {
      start: Math.PI*1.5,
      end: math.clip( math.scale(value,0,0.5,Math.PI*1.5,Math.PI*0.5) , Math.PI*0.5, Math.PI*1.5 )
    };
    let handle2Points = {
      start: Math.PI *2.5,
      end: math.clip( math.scale(value,0.5,1,Math.PI*2.5,Math.PI*1.5) , Math.PI*1.5, Math.PI*2.5 )
    };

    let handlePath = svg.arc(center.x, center.y, diameter/2-diameter/40, handlePoints.start, handlePoints.end);
    let handle2Path = svg.arc(center.x, center.y, diameter/2-diameter/40, handle2Points.start, handle2Points.end);

    this.handle.setAttribute('d',handlePath);
    this.handle2.setAttribute('d',handle2Path);


    handlePath += ' L '+center.x+' '+center.y;

    this.handleFill.setAttribute('d',handlePath);

    handle2Path += ' L '+center.x+' '+center.y;

    this.handle2Fill.setAttribute('d',handle2Path);

    let arcEndingA;
    if (value <= 0.5) {
      arcEndingA = handlePoints.end;
    } else {
      arcEndingA = handle2Points.end;
    }

    let arcEndingX = center.x + Math.cos(arcEndingA) * (diameter/2);
    let arcEndingY = center.y + Math.sin(arcEndingA) * (diameter/2) * -1;

    this.handleLine.setAttribute('d','M '+center.x+' '+center.y+' L '+arcEndingX+' '+arcEndingY);

  }


  click() {
    if (this.mode==='relative') {
      this.previousAngle = false;
    }
    this.position.anchor = this.mouse;
    this.position.value = this._value.normalized;
    this.move();
   }

  move() {
    if (this.clicked) {

      this.position.update(this.mouse);

      let angle = this.position.value*Math.PI*2;

      if (angle < 0 ) { angle += (Math.PI*2); }

      if (this.mode === 'relative') {
        if (this.previousAngle !== false && Math.abs(this.previousAngle - angle) > 2) {
          if (this.previousAngle > 3) {
            angle = Math.PI*2;
          } else {
            angle = 0;
          }
        }
      } /* else {
        if (this.previousAngle !== false && Math.abs(this.previousAngle - angle) > 2) {
          if (this.previousAngle > 3) {
            angle = Math.PI*2;
          } else {
            angle = 0;
          }
        }
      } */
      this.previousAngle = angle;

      let realValue = angle / (Math.PI*2);

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

      if (this.mode === 'relative') {
        this.position.value = realValue;
      }

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

      this.render();

    }
  }

  release() {
  }

  /*
  Dial's value. When set, it will automatically be adjust to fit min/max/step settings of the interface.
  @type {number}
  @example dial.value = 10;

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

  set value(value) {
    this._value.update(value);
    this.emit('change',this.value);
    this.render();
  }
*/

    /**
    Dial's value. When set, it will automatically be adjust to fit min/max/step settings of the interface.
    @type {number}
    @example dial.value = 10;
    */
    get value() {
      return this._value.value;
    }
    set value(v) {
      this._value.update(v);
      this.position.value = this._value.normalized;
      this.emit('change',this._value.value);
      this.render();
    }

    /**
    Lower limit of the dial's output range
    @type {number}
    @example dial.min = 1000;
    */
    get min() {
      return this._value.min;
    }
    set min(v) {
      this._value.min = v;
    }

    /**
    Upper limit of the dial's output range
    @type {number}
    @example dial.max = 1000;
    */
    get max() {
      return this._value.max;
    }
    set max(v) {
      this._value.max = v;
    }

    /**
    The increment that the dial's value changes by.
    @type {number}
    @example dial.step = 5;
    */
    get step() {
      return this._value.step;
    }
    set step(v) {
      this._value.step = v;
    }

    /**
    Absolute mode (dial's value jumps to mouse click position) or relative mode (mouse drag changes value relative to its current position). Default: "relative".
    @type {string}
    @example dial.mode = "relative";
    */
    get mode() {
      return this.position.mode;
    }
    set mode(v) {
      this.position.mode = v;
    }


  /**
  Normalized value of the dial.
  @type {number}
  @example dial.normalized = 0.5;
  */
  get normalized() {
    return this._value.normalized;
  }

  set normalized(v) {
    this._value.updateNormal(v);
    this.emit('change',this.value);
  }

}