1
+ ( function ( global ) {
2
+
3
+ function extend ( obj ) {
4
+ for ( var i = 1 ; i < arguments . length ; i ++ ) {
5
+ var source = arguments [ i ] ;
6
+ for ( var name in source ) if ( source . hasOwnProperty ( name ) )
7
+ obj [ name ] = source [ name ] ;
8
+ }
9
+ return obj ;
10
+ }
11
+
12
+ var EventEmitter = {
13
+ on : function ( type , handler ) {
14
+ this . _ensureEvent ( type ) ;
15
+ this . _events [ type ] . push ( handler ) ;
16
+ } ,
17
+ off : function ( type , handler ) {
18
+ if ( this . _events && this . _events [ type ] ) {
19
+ this . _events [ type ] = this . _events [ type ] . filter ( function ( h ) {
20
+ return h !== handler ;
21
+ } ) ;
22
+ }
23
+ } ,
24
+ emit : function ( type ) {
25
+ var args = Array . prototype . slice . call ( arguments , 1 ) ;
26
+ this . _ensureEvent ( type ) ;
27
+ this . _events [ type ] . forEach ( function ( handler ) {
28
+ handler . apply ( this , args ) ;
29
+ } ) ;
30
+ } ,
31
+ _ensureEvent : function ( type ) {
32
+ if ( ! this . _events ) this . _events = { } ;
33
+ if ( ! this . _events [ type ] ) this . _events [ type ] = [ ] ;
34
+ }
35
+ } ;
36
+
37
+ function Vector2d ( x , y ) {
38
+ this . x = x ;
39
+ this . y = y ;
40
+ }
41
+
42
+ Object . defineProperty ( Vector2d . prototype , 'length' , {
43
+ get : function ( ) { return Math . sqrt ( this . x * this . x + this . y * this . y ) ; } ,
44
+ enumerable : true
45
+ } ) ;
46
+
47
+ Vector2d . prototype . add = function ( v ) {
48
+ if ( v instanceof this . constructor )
49
+ return new this . constructor ( this . x + v . x , this . y + v . y ) ;
50
+ else
51
+ return new this . constructor ( this . x + v , this . y + v ) ;
52
+ } ;
53
+
54
+ Vector2d . prototype . sub = function ( v ) {
55
+ if ( v instanceof this . constructor )
56
+ return this . add ( v . neg ( ) ) ;
57
+ else
58
+ return this . add ( - v ) ;
59
+ } ;
60
+
61
+ Vector2d . prototype . neg = function ( ) {
62
+ return new this . constructor ( - this . x , - this . y ) ;
63
+ } ;
64
+
65
+ Vector2d . prototype . scale = function ( s ) {
66
+ return new this . constructor ( this . x * s , this . y * s ) ;
67
+ } ;
68
+
69
+ Vector2d . prototype . dot = function ( v ) {
70
+ return this . x * v . x + this . y * v . y ;
71
+ } ;
72
+
73
+ Vector2d . prototype . cross = function ( v ) {
74
+ return this . x * v . y - this . y * v . x ;
75
+ } ;
76
+
77
+ Vector2d . prototype . towards = function ( other ) {
78
+ return other . sub ( this ) . normalize ( ) ;
79
+ } ;
80
+
81
+ Vector2d . prototype . normalize = function ( ) {
82
+ return this . scale ( 1 / this . length ) ;
83
+ } ;
84
+
85
+ Vector2d . random = function ( unit ) {
86
+ var r = Math . random ( ) * Math . PI * 2 ,
87
+ d = unit ? 1 : Math . random ( ) ;
88
+ return new this (
89
+ d * Math . cos ( r ) ,
90
+ d * Math . sin ( r )
91
+ ) ;
92
+ } ;
93
+
94
+ Vector2d . randomWithin = function ( rectangle ) {
95
+ return new this (
96
+ rectangle . corner . x + Math . random ( ) * rectangle . width ,
97
+ rectangle . corner . y + Math . random ( ) * rectangle . height
98
+ ) ;
99
+ } ;
100
+
101
+ Vector2d . prototype . rotate = function ( r ) {
102
+ return new this . constructor (
103
+ this . x * Math . cos ( r ) - this . y * Math . sin ( r ) ,
104
+ this . x * Math . sin ( r ) + this . y * Math . cos ( r )
105
+ ) ;
106
+ } ;
107
+
108
+ Vector2d . prototype . clone = function ( ) {
109
+ return new this . constructor ( this . x , this . y ) ;
110
+ } ;
111
+
112
+ function BezierCurve ( A , B , C , D ) {
113
+ this . _A = A ;
114
+ this . _B = B ;
115
+ this . _C = C ;
116
+ this . _D = D ;
117
+ } ;
118
+
119
+ extend ( BezierCurve . prototype , EventEmitter ) ;
120
+
121
+ [ 'A' , 'B' , 'C' , 'D' ] . forEach ( function ( pt ) {
122
+ Object . defineProperty ( BezierCurve . prototype , pt , {
123
+ get : function ( ) { return this [ '_' + pt ] ; } ,
124
+ set : function ( v ) {
125
+ this [ '_' + pt ] = v ;
126
+ this . emit ( 'change' , pt ) ;
127
+ }
128
+ } ) ;
129
+ } ) ;
130
+
131
+ BezierCurve . prototype . interpolate = function ( t ) {
132
+ var tt = t * t ,
133
+ ttt = tt * t ,
134
+ u = 1 - t ,
135
+ uu = u * u ,
136
+ uuu = uu * u ;
137
+
138
+ return this . A . scale ( uuu ) .
139
+ add ( this . B . scale ( 3 * uu * t ) ) .
140
+ add ( this . C . scale ( 3 * u * tt ) ) .
141
+ add ( this . D . scale ( ttt ) ) ;
142
+ } ;
143
+
144
+ // Derivative to determine the curve's slope.
145
+ BezierCurve . prototype . direction = function ( t ) {
146
+ var tt = t * t ,
147
+ g = ( t - 1 ) * ( t - 1 ) ,
148
+ h = - 3 * tt + 4 * t - 1 ,
149
+ i = 3 * tt - 2 * t ,
150
+ j = - tt ;
151
+
152
+ return this . A . scale ( g ) .
153
+ add ( this . B . scale ( h ) ) .
154
+ add ( this . C . scale ( i ) ) .
155
+ add ( this . D . scale ( j ) ) .
156
+ scale ( - 3 ) ;
157
+ } ;
158
+
159
+ function AnimationGenerator ( options ) {
160
+ this . options = extend ( { } , this . constructor . defaults , options || { } ) ;
161
+ }
162
+
163
+ AnimationGenerator . defaults = {
164
+ segments : 128 ,
165
+ orientAlongPath : false ,
166
+ maxError : 1 ,
167
+ name : 'CUSTOM_ANIMATION'
168
+ } ;
169
+
170
+ AnimationGenerator . prototype . generate = function ( bezier ) {
171
+ return this . generateAnimation ( this . generatePointList ( bezier ) ) ;
172
+ } ;
173
+
174
+ AnimationGenerator . prototype . generatePointList = function ( bezier ) {
175
+ var increment = 1 / this . options . segments ,
176
+ points = [ ] ,
177
+ i , p , t , d , pa , n , minD , minError = 0 , idx ;
178
+
179
+ for ( var i = 0 ; i <= this . options . segments ; i ++ ) {
180
+ var t = i * increment ,
181
+ p = bezier . interpolate ( t ) ;
182
+ p . t = t ;
183
+ p . dir = bezier . direction ( t ) ;
184
+ p . angle = - Math . atan2 ( p . dir . x , p . dir . y ) / Math . PI * 180 ;
185
+ points . push ( p ) ;
186
+ }
187
+
188
+ // Cull as many points as possible from the generated segments
189
+ // while the error is less than the maximum allowed.
190
+ do {
191
+ if ( points . length == 2 ) break ;
192
+
193
+ idx = null ;
194
+ minError = Infinity ;
195
+
196
+ for ( i = 1 ; i < points . length - 1 ; i ++ ) {
197
+ n = points [ i - 1 ] . towards ( points [ i + 1 ] ) ;
198
+ pa = points [ i - 1 ] . sub ( points [ i ] ) ;
199
+ d = pa . sub ( n . scale ( pa . dot ( n ) ) ) . length ;
200
+ if ( d < minError && d < this . options . maxError ) {
201
+ minError = d ;
202
+ idx = i ;
203
+ }
204
+ }
205
+
206
+ if ( idx != null ) points . splice ( idx , 1 ) ;
207
+
208
+ } while ( minError <= this . options . maxError )
209
+
210
+ return points ;
211
+ }
212
+
213
+ AnimationGenerator . prototype . generateAnimation = function ( pointList ) {
214
+ var i , rollingSum = 0 , total = 0 ,
215
+ animation = [ '@-webkit-keyframes ' + this . options . name + ' {' ] ;
216
+
217
+ pointList [ 0 ] . l = 0 ;
218
+ for ( i = 1 ; i < pointList . length ; i ++ ) {
219
+ total += pointList [ i ] . l = pointList [ i ] . sub ( pointList [ i - 1 ] ) . length ;
220
+ }
221
+
222
+ for ( i = 0 ; i < pointList . length ; i ++ ) {
223
+ rollingSum += pointList [ i ] . l ;
224
+ animation . push ( this . _generateKeyframe ( rollingSum / total , pointList [ i ] ) ) ;
225
+ }
226
+
227
+ animation . push ( '}' ) ;
228
+ return animation . join ( '\n' ) ;
229
+ } ;
230
+
231
+ AnimationGenerator . prototype . _generateKeyframe = function ( position , point ) {
232
+ var extras = '' ;
233
+ if ( this . options . orientAlongPath ) {
234
+ extras = 'rotate(' + point . angle . toFixed ( 1 ) + 'deg)' ;
235
+ }
236
+ return ' ' + ( position * 100 ) . toFixed ( 4 ) + '% { -webkit-transform: translate(' + ( point . x . toFixed ( 1 ) ) + 'px, ' + ( point . y . toFixed ( 1 ) ) + 'px) ' + extras + '; }' ;
237
+ } ;
238
+
239
+ // exports
240
+ global . Vector2d = Vector2d ;
241
+ global . BezierCurve = BezierCurve ;
242
+ global . AnimationGenerator = AnimationGenerator ;
243
+
244
+ } ) ( ( typeof module != 'undefined' && module . exports ) ? module . exports : window ) ;
0 commit comments