-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathaccelimation.js
181 lines (149 loc) · 5.58 KB
/
accelimation.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
171
172
173
174
175
176
177
178
179
180
181
// Copyright 2001, Aaron Boodman
// This code is public domain. Please use it for good, not evil.
//=====================================================================
// Accel[erated] [an]imation object
// change a property of an object over time in an accelerated fashion
//=====================================================================
// obj : reference to the object whose property you'd like to animate
// prop : property you would like to change eg: "left"
// to : final value of prop
// time : time the animation should take to run
// zip : optional. specify the zippiness of the acceleration. pick a
// number between 0.1 and 2 where 0.1 is full decelerated, 2 is
// full accelerated, and 1 is linear (no acceleration). default
// is 1.
// unit : optional. specify the units for use with prop. default is
// "px".
//=====================================================================
function Accelimation(obj, prop, to, time, zip, unit) {
if (typeof zip == "undefined") zip = 0;
if (typeof unit == "undefined") unit = "px";
if (zip > 2 || zip <= 0)
throw new Error("Illegal value for zip. Must be less than or equal " +
"to 2 and greater than 0.");
this.obj = obj;
this.prop = prop;
this.x1 = to;
this.dt = time;
this.zip = zip;
this.unit = unit;
this.x0 = parseInt(this.obj[this.prop]);
this.D = this.x1 - this.x0;
this.A = this.D / Math.abs(Math.pow(time, this.zip));
this.id = Accelimation.instances.length;
this.onend = null;
}
//=====================================================================
// public methods
//=====================================================================
// after you create an accelimation, you call this to start it-a runnin'
Accelimation.prototype.start = function() {
this.t0 = new Date().getTime();
this.t1 = this.t0 + this.dt;
var dx = this.x1 - this.x0;
Accelimation._add(this);
}
// and if you need to stop it early for some reason...
Accelimation.prototype.stop = function() {
Accelimation._remove(this);
}
//=====================================================================
// private methods
//=====================================================================
// paints one frame. gets called by Accelimation._paintAll.
Accelimation.prototype._paint = function(time) {
if (time < this.t1) {
var elapsed = time - this.t0;
this.obj[this.prop] =
Math.abs(Math.pow(elapsed, this.zip)) * this.A + this.x0 +
this.unit;
}
else this._end();
}
// ends the animation
Accelimation.prototype._end = function() {
Accelimation._remove(this);
this.obj[this.prop] = this.x1 + this.unit;
if (this.onend) {
this.onend();
}
}
//=====================================================================
// static methods (all private)
//=====================================================================
Accelimation._raf = function() {
(window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame).apply(null, arguments);
};
Accelimation._caf = function() {
(window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.msCancelAnimationFrame).apply(null, arguments);
};
// add a function to the list of ones to call periodically
Accelimation._add = function(o) {
var index = this.instances.length;
this.instances[index] = o;
// if this is the first one, start the engine
if (this.instances.length == 1) {
if (!this._scheduleOneShot())
this._scheduleRepeating();
}
}
Accelimation._scheduleOneShot = function() {
if (this._raf) {
this.timerId = this._raf(
function(timestamp) {
Accelimation._paintAll(timestamp);
});
return true;
} else {
return false;
}
}
Accelimation._scheduleRepeating = function() {
this.timerID = window.setInterval(
function() {
Accelimation._paintAll(new Date().getTime());
},
this.targetRes);
}
// remove a function from the list
Accelimation._remove = function(o) {
for (var i = 0; i < this.instances.length; i++) {
if (o == this.instances[i]) {
this.instances =
this.instances.slice(0,i).concat( this.instances.slice(i+1) );
break;
}
}
// if that was the last one, stop the engine
if (this.instances.length == 0) {
if (this._caf)
this._caf(this.timerID);
else
window.clearInterval(this.timerID);
this.timerID = null;
}
}
// "engine" - call each function in the list every so often
Accelimation._paintAll = function() {
var now = new Date().getTime();
for (var i = 0; i < this.instances.length; i++) {
// small chance that this accelimation could get dropped in the
// queue in the middle of a run. That means that it could have a
// t0 greater than "now", which means that elapsed would be negative.
this.instances[i]._paint(Math.max(now, this.instances[i].t0));
}
if (this.instances.length)
this._scheduleOneShot();
}
//=====================================================================
// static properties
//=====================================================================
Accelimation.instances = [];
Accelimation.targetRes = 10;
Accelimation.timerID = null;