148 lines
3.0 KiB
JavaScript
148 lines
3.0 KiB
JavaScript
import {compose, EventEmitter, Property} from '@avocado/core';
|
|
|
|
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);
|