UNPKG

variaboard

Version:

VariaBoard is a control interface to modify parameters in JavaScript.

652 lines (545 loc) 21.8 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.VariaBoard = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Create a button */ var Button = function () { function Button(variaboard, config) { _classCallCheck(this, Button); this.variaboard = variaboard; this.id = config.id; this.title = config.title; this.description = config.description; this.callback = config.callback !== undefined ? config.callback : function () {}; this.createDOM(); this.listen(); } /** * Create necessary DOM elements */ _createClass(Button, [{ key: 'createDOM', value: function createDOM() { this.dom = {}; // control this.dom.control = document.createElement('div'); this.dom.control.classList.add(this.variaboard.namespace + '-control'); // button this.dom.button = document.createElement('button'); this.dom.button.classList.add(this.variaboard.namespace + '-control-button'); this.dom.button.textContent = this.title; this.dom.control.setAttribute('title', this.title + ': ' + this.description); this.dom.control.appendChild(this.dom.button); // add to control to panel this.variaboard.dom.controls.appendChild(this.dom.control); } /** * Setup event listeners */ }, { key: 'listen', value: function listen() { var _this = this; this.dom.button.addEventListener('click', function (e) { return _this.onButtonClick(e); }); } /** * On button click event * * @param {object} e - Event object */ }, { key: 'onButtonClick', value: function onButtonClick(e) { this.callback(); } }]); return Button; }(); module.exports = Button; },{}],2:[function(require,module,exports){ 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Create a control */ var Control = function () { function Control(variaboard, config) { _classCallCheck(this, Control); this.variaboard = variaboard; this.type = config.type; this.id = config.id; this.title = config.title; this.default = config.default; this.randomizable = config.randomizable !== undefined ? config.randomizable : true; this.mutable = config.mutable !== undefined ? config.mutable : true; this.locked = config.locked !== undefined ? config.locked : false; this.value = this.default; this.createDOM(); } _createClass(Control, [{ key: 'createDOM', value: function createDOM() { this.dom = {}; // control this.dom.control = document.createElement('div'); this.dom.control.classList.add(this.variaboard.namespace + '-control'); // title this.dom.title = document.createElement('h3'); this.dom.title.classList.add(this.variaboard.namespace + '-control-title'); this.dom.title.textContent = this.title; this.dom.control.appendChild(this.dom.title); // value this.dom.value = document.createElement('input'); this.dom.value.classList.add(this.variaboard.namespace + '-control-value'); this.dom.control.appendChild(this.dom.value); // add control to panel this.variaboard.dom.controls.appendChild(this.dom.control); } }, { key: 'get', value: function get() { return this.value; } }, { key: 'lock', value: function lock() { this.locked = true; } }, { key: 'unlock', value: function unlock() { this.locked = false; } }]); return Control; }(); module.exports = Control; },{}],3:[function(require,module,exports){ 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var Control = require('./control'); var Calc = require('../util/calc'); /** * Create a range control * * @param {object} variaboard - Reference to parent VariaBoard instance * @param {object} config - Configuration object * @param {string} config.id - Unique id/slug * @param {string} config.title - UI display title * @param {number} config.min - Minimum value * @param {number} config.max - Maximum value * @param {number} config.step - Step size * @param {number} config.default - Starting value * @param {boolean} config.randomizable - Can be randomized individually and by randomizing all * @param {boolean} config.mutable - Can be mutated individually and by mutating all * @param {boolean} config.locked - Temporarily toggle whether the control is affected by randomization and mutation * * @extends Control * * @requires {@link Control} * @requires {@link Calc} */ var Range = function (_Control) { _inherits(Range, _Control); function Range(variaboard, config) { _classCallCheck(this, Range); var _this = _possibleConstructorReturn(this, (Range.__proto__ || Object.getPrototypeOf(Range)).call(this, variaboard, config)); _this.type = 'range'; _this.min = config.min; _this.max = config.max; _this.step = config.step !== undefined ? Math.abs(config.step) : 1; _this.places = _this.step.toString().indexOf('.') > -1 ? _this.step.toString().split('.')[1].length : 0; _this.valueTarget = _this.value; _this.mouseIsDown = false; _this.settled = false; _this.listen(); _this.set(_this.value); return _this; } _createClass(Range, [{ key: 'createDOM', value: function createDOM() { _get(Range.prototype.__proto__ || Object.getPrototypeOf(Range.prototype), 'createDOM', this).call(this); // range this.dom.range = document.createElement('div'); this.dom.range.classList.add(this.variaboard.namespace + '-control-range'); // range inner this.dom.rangeInner = document.createElement('div'); this.dom.rangeInner.classList.add(this.variaboard.namespace + '-control-range-inner'); this.dom.range.appendChild(this.dom.rangeInner); this.dom.control.appendChild(this.dom.range); } }, { key: 'listen', value: function listen() { var _this2 = this; this.dom.value.addEventListener('change', function (e) { return _this2.onValueChange(e); }); this.dom.range.addEventListener('mousedown', function (e) { return _this2.onValueMousedown(e); }); } }, { key: 'onValueChange', value: function onValueChange(e) { this.set(this.dom.value.value); this.valueTarget = this.value; } }, { key: 'onValueMousedown', value: function onValueMousedown(e) { this.variaboard.mouse.down = true; this.variaboard.mouse.anchor.x = e.clientX; this.variaboard.mouse.anchor.y = e.clientY; this.mouseIsDown = true; this.setDragValue(); } }, { key: 'onWindowMouseup', value: function onWindowMouseup(e) { this.mouseIsDown = false; } }, { key: 'onWindowMousemove', value: function onWindowMousemove(e) { if (this.mouseIsDown) { this.setDragValue(); } } }, { key: 'randomize', value: function randomize() { this.settled = false; this.valueTarget = Calc.rand(this.min, this.max); } }, { key: 'mutate', value: function mutate() { var size = (this.max - this.min) / 15; this.settled = false; this.valueTarget = this.get() + Calc.rand(-size, size); } }, { key: 'setDragValue', value: function setDragValue() { var left = this.dom.range.offsetLeft; var width = this.dom.range.offsetWidth; var val = Calc.map(this.variaboard.mouse.x, left, left + width, this.min, this.max); this.set(val); this.valueTarget = this.value; } }, { key: 'easeSet', value: function easeSet() { if (Math.abs(this.value - this.valueTarget) > this.step / 2) { this.value += (this.valueTarget - this.value) * 0.2; this.set(this.value, true); } else { this.settled = true; this.value = this.valueTarget; this.set(this.value); } } }, { key: 'set', value: function set(val, bypassRounding) { // sanitize value val = parseFloat(val); val = isNaN(val) ? this.default : val; val = Calc.clamp(val, this.min, this.max); val = bypassRounding ? val : Calc.roundToNearestInterval(val, this.step); this.value = val; // set input value this.dom.value.value = this.value.toFixed(this.places); // set range value this.dom.rangeInner.style.transform = 'scaleX(' + Calc.map(this.value, this.min, this.max, 0, 1) + ')'; // set the title attribute for the control this.dom.control.setAttribute('title', this.title + ': ' + this.value.toFixed(this.places)); } }]); return Range; }(Control); module.exports = Range; },{"../util/calc":5,"./control":2}],4:[function(require,module,exports){ 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Button = require('./button'); var Range = require('./controls/range'); var Calc = require('./util/calc'); var VariaBoard = function () { /** * Create a VariaBoard control panel * * @param {object} config - Configuration object * @param {object|string} [config.container=document.body] - DOM element or CSS selector * @param {string} [config.title] - Title of the panel * * @requires {@link Button} * @requires {@link Range} * @requires {@link Calc} */ function VariaBoard(config) { _classCallCheck(this, VariaBoard); this.namespace = 'variaboard'; this.controls = {}; this.buttons = {}; this.mouse = { down: false, x: 0, y: 0, anchor: { x: 0, y: 0 } }; this.needsUpdate = false; this.raf = null; this.container = config.container !== undefined ? config.container : document.body; this.title = config.title !== undefined ? config.title : null; this.createDOM(); this.listen(); } /** * Create necessary DOM elements */ _createClass(VariaBoard, [{ key: 'createDOM', value: function createDOM() { this.dom = {}; // container this.dom.container = this.container; // panel this.dom.panel = document.createElement('div'); this.dom.panel.classList.add(this.namespace + '-panel'); // title if (this.title) { this.dom.title = document.createElement('h1'); this.dom.title.classList.add(this.namespace + '-title'); this.dom.title.textContent = this.title; this.dom.panel.appendChild(this.dom.title); } // controls this.dom.controls = document.createElement('div'); this.dom.controls.classList.add(this.namespace + '-controls'); this.dom.panel.appendChild(this.dom.controls); // add panel to container this.dom.container.appendChild(this.dom.panel); } /** * Setup event listeners */ }, { key: 'listen', value: function listen() { var _this = this; window.addEventListener('mouseup', function (e) { return _this.onWindowMouseup(e); }); window.addEventListener('mousemove', function (e) { return _this.onWindowMousemove(e); }); } /** * On window mouse up event * * @param {object} e - Event object */ }, { key: 'onWindowMouseup', value: function onWindowMouseup(e) { this.mouse.down = false; for (var key in this.controls) { var control = this.controls[key]; control.onWindowMouseup(e); } } /** * On window mouse move event * * @param {object} e - Event object */ }, { key: 'onWindowMousemove', value: function onWindowMousemove(e) { this.mouse.x = e.clientX; this.mouse.y = e.clientY; for (var key in this.controls) { var control = this.controls[key]; control.onWindowMousemove(e); } } /** * Update based on requestAnimationFrame() */ }, { key: 'update', value: function update() { var _this2 = this; this.needsUpdate = false; for (var key in this.controls) { if (this.controls[key].type === 'range') { this.controls[key].easeSet(); if (!this.controls[key].settled) { this.needsUpdate = true; } } } if (this.needsUpdate) { this.raf = requestAnimationFrame(function () { return _this2.update(); }); } } /** * Add a button * * @param {object} config - Configuration object * @param {object} config.id - ID slug * @param {object} config.title - Title text * @param {object} [config.callback=() => {}] - Callback function for button press * * @returns {object} Button object */ }, { key: 'addButton', value: function addButton(config) { this.buttons[config.id] = new Button(this, config); return this.buttons[config.id]; } /** * Add a range control via {@linkcode Range} * * @param {object} config - Configuration object * @param {string} config.id - Unique id/slug * @param {string} config.title - UI display title * @param {number} config.min - Minimum value * @param {number} config.max - Maximum value * @param {number} config.step - Step size * @param {number} config.default - Starting value * @param {boolean} config.randomizable - Can be randomized individually and by randomizing all * @param {boolean} config.mutable - Can be mutated individually and by mutating all * @param {boolean} config.locked - Temporarily toggle whether the control is affected by randomization and mutation * * @returns {object} Range object */ }, { key: 'addRange', value: function addRange(config) { this.controls[config.id] = new Range(this, config); return this.controls[config.id]; } }, { key: 'get', value: function get(id) { return this.controls[id].get(); } }, { key: 'randomize', value: function randomize() { for (var key in this.controls) { this.controls[key].randomize(); } cancelAnimationFrame(this.raf); this.update(); } }, { key: 'mutate', value: function mutate() { for (var key in this.controls) { this.controls[key].mutate(); } cancelAnimationFrame(this.raf); this.update(); } }]); return VariaBoard; }(); module.exports = VariaBoard; },{"./button":1,"./controls/range":3,"./util/calc":5}],5:[function(require,module,exports){ "use strict"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Calculation functions and helpers */ var Calc = function () { function Calc() { _classCallCheck(this, Calc); } _createClass(Calc, null, [{ key: "rand", /** * Get a random float within a range. If only one argument is passed, it is used as the max and the min becomes zero. * * @param {number} min - Minimum range value * @param {number} max - Maximum range value * * @example * // two arguments * Calc.rand(2, 18); * // -> random float between 2 and 18 * * // single argument * Calc.rand(42.5); * // -> random float between 0 and 42.5 * * @returns {number} Random float within the range */ value: function rand(min, max) { if (max === undefined) { max = min; min = 0; } return Math.random() * (max - min) + min; } /** * Clamp a value to a range * * @param {number} val - Input value * @param {number} min - Minimum range value * @param {number} max - Maximum range value * * @example * Calc.clamp(3, 10, 150); * // -> 10 * * Calc.clamp(400, 10, 150); * // -> 150 * * Calc.clamp(75, 10, 100); * // -> 75 * * @returns {number} Clamped value within range */ }, { key: "clamp", value: function clamp(val, min, max) { return Math.max(Math.min(val, max), min); } }, { key: "map", value: function map(val, inMin, inMax, outMin, outMax) { return (outMax - outMin) * ((val - inMin) / (inMax - inMin)) + outMin; } }, { key: "roundToNearestInterval", value: function roundToNearestInterval(value, interval) { return Math.round(value / interval) * interval; } }]); return Calc; }(); module.exports = Calc; },{}]},{},[4])(4) });