avocado-old/packages/mixins/lfo/modulated-property.js
2019-04-12 18:58:38 -05:00

150 lines
3.1 KiB
JavaScript

import {compose} from '@avocado/core';
import {EventEmitterMixin as EventEmitter} from '../event-emitter';
import {PropertyMixin as Property} from '../property';
import {TransitionMixin as Transition} from '../transition';
const Modulator = {
Flat() {
return (location) => {
return .5;
}
},
Linear() {
return (location) => {
return location;
}
},
Random({variance = .4} = {}) {
return (location) => {
return Math.abs((Math.random() * (variance + variance) - variance)) % 1;
}
},
Sine() {
return (location) => .5 * (1 + Math.sin(location * Math.PI * 2))
},
};
const decorate = compose(
Transition,
Property('frequency', {
default: 0,
}),
Property('location', {
default: 0,
}),
Property('magnitude', {
default: 0,
track: true,
}),
EventEmitter,
);
class ModulatedProperty {
constructor(
object, key,
{frequency, location, magnitude, median, modulators}
) {
this.object = object;
this.key = key;
if (!modulators) {
modulators = [Modulator.Linear];
}
if (!Array.isArray(modulators)) {
modulators = [modulators];
}
this.median = median;
this.on('magnitudeChanged', this.onMagnitudeChanged, this);
this.setFrequency(frequency);
this.setLocation(location || 0);
this.setMagnitude(magnitude);
if (this.median) {
this.min = this.median - magnitude;
}
const modulatorFunction = (modulator) => {
if ('string' === typeof modulator) {
return Modulator[modulator] ? Modulator[modulator] : Modulator.Linear;
}
else if ('function' === typeof modulator) {
return modulator;
}
else {
return Modulator.Linear;
}
};
this.modulators = modulators.map((modulator) => {
if ('object' !== typeof modulator) {
return modulatorFunction(modulator)(modulator);
}
if (modulator.f) {
return modulatorFunction(modulator.f)(modulator);
}
else {
[key] = Object.keys(modulator)
return modulatorFunction(key)(modulator[key]);
}
});
this.transitions = [];
}
destroy() {
this.off('magnitudeChanged', this.onMagnitudeChanged);
}
onMagnitudeChanged() {
this.magnitude2 = this.magnitude() * 2;
}
tick(elapsed) {
this.transitions.forEach((transition) => {
transition.tick(elapsed);
});
const frequency = this.frequency();
let location = this.location();
location += elapsed;
if (location > frequency) {
location -= frequency;
}
this.setLocation(location);
const min = this.median ? this.min : this.object[this.key];
const value = this.modulators.reduce(
(value, m) => value + m(location / frequency),
0
) / this.modulators.length;
this.object[this.key] = min + value * this.magnitude2;
}
transition(...args) {
const transition = Transition.prototype.transition.apply(this, args);
this.transitions.push(transition);
transition.promise.then(() => {
this.transitions.splice(this.transitions.indexOf(transition), 1);
});
return transition;
}
}
export default decorate(ModulatedProperty);