-
Notifications
You must be signed in to change notification settings - Fork 1
/
anim.js
170 lines (146 loc) · 4.37 KB
/
anim.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
"use strict"
var ease = require('./easing.js').ease;
var resolution = 10; //for faster fading, default 25
/**
* Animations with fix start and end. But can be aborted.
* @constructor
*
* @param dmx dmx.js instance
*/
function Anim(dmx) {
this.dmx = dmx;
this.fx_stack = [];
this.interval = null;
this.aborted = false;
this.finished = false;
this.currentStepFinalValues = {};
this.animationTimePercent = 0;
}
/**
* Abort this single animation
*/
Anim.prototype.abort = function () {
this.stop();
}
/**
* add animations step
*
* @param to channels to update e.g. {1: 255, 2:200} starting at 0!
* @param duration of step e.g. 2000 for 2 sec
* @param options object with e.g. easing key (e.g. linear (default) or inOutCubic or outBounce from easings.js)
* @returns {Anim}
*/
Anim.prototype.add = function(to, duration, options) {
var duration = duration || resolution
var options = options || {}
options['easing'] = options['easing'] || 'linear'
this.fx_stack.push({'to': to, 'duration': duration, 'options': options})
return this
}
/**
* Add relative chanels for multiple devices
* e.g. .addMultipleDevs({1: 255, 2:200}, 2000, [1,9]) -> channels 1,2,9,10 updated
*
* @param to channels to update e.g. {1: 255, 2:200}
* @param duration of step in ms e.g. 2000 for 2 sec
* @param startingchannels array with initial channels for starting e.g. [1,9]
* second value -1 is added to channels in to and also executed
* @param options object with e.g. easing key (e.g. linear (default) or inOutCubic or outBounce from easings.js)
* @returns {Anim}
*/
Anim.prototype.addMultipleDevs = function (to, duration, startingchannels, options) {
var tonew = {}; //new to field value
for (var i in startingchannels) {
for (var k in to) {
var newchannel = parseInt(k) + startingchannels[i] - 1; //new channel to manipulate
tonew[newchannel] = to[k]; //assign value to new channel
}
}
this.add(tonew, duration, options)
return this
}
/**
* Add delay between animation steps
* @param duration
* @return {Anim}
*/
Anim.prototype.delay = function(duration) {
return this.add({}, duration)
}
Anim.prototype.stop = function () {
this.aborted = true;
if(this.interval) {
clearInterval(this.interval)
}
this.fx_stack = []
}
/**
* @return final channel value for current animation step
*/
Anim.prototype.getCurrentStepFinalValues = function() {
return this.currentStepFinalValues;
};
/**
* @return animation percent - between 0 and 1
*/
Anim.prototype.getAnimationPercent = function() {
return this.animationTimePercent;
};
/** starts animation
*
* @param universe
* @param onFinish callbac called nearly on end (if want garuanteed on end, call .delay(msec) before )
* with argument final-value (not called if aborted)
* @param onUpdate callback called on every value update, argument are the new values
*/
Anim.prototype.run = function(universe, onFinish, onUpdate) {
var config = {};
var t = 0;
var d = 0;
var a;
var lastUpdate = new Date().getTime();
var fx_stack = this.fx_stack;
var self = this;
var ani_setup = function() {
a = fx_stack.shift();
t = 0;
d = a.duration;
config = {};
for(var k in a.to) {
config[k] = {
'start': self.dmx.get(universe, k),
'end': a.to[k]
}
}
self.currentStepFinalValues = a.to;
}
var ani_step = function() {
self.animationTimePercent = t / d;
var new_vals = {};
for(var k in config) {
new_vals[k] = Math.round(config[k].start + ease[a.options['easing']](t, 0, 1, d) * (config[k].end - config[k].start));
if(new_vals[k] < 0) new_vals[k] = 0;
if(new_vals[k] > 255) new_vals[k] = 255;
}
t = t + resolution;
self.dmx.update(universe, new_vals, !onUpdate); // update values if from animation scripts - and no display update configured
if(onUpdate) onUpdate(new_vals);
// comment line above and uncomment lines below for better performance on display-slider update in browser
// if(onUpdate && new Date().getTime() - lastUpdate > 100){ //update display slider only every 100 ms
// onUpdate(new_vals);
// lastUpdate = new Date().getTime();
// };
if(t > d) {
if(fx_stack.length > 0) {
ani_setup();
} else {
clearInterval(iid);
self.finished = true;
if(onFinish) onFinish(a.to);
}
}
};
ani_setup();
var iid = this.interval = setInterval(ani_step, resolution);
};
module.exports = Anim;