Skip to content
Permalink
Newer
Older
100644 246 lines (207 sloc) 6.66 KB
Jan 15, 2015
1
( function( window, factory ) {
2
'use strict';
3
// universal module definition
4
5
if ( typeof define == 'function' && define.amd ) {
6
// AMD
7
define( [
8
'get-style-property/get-style-property',
9
'fizzy-ui-utils/utils'
Jan 15, 2015
10
], function( getStyleProperty, utils ) {
11
return factory( window, getStyleProperty, utils );
12
});
13
} else if ( typeof exports == 'object' ) {
14
// CommonJS
15
module.exports = factory(
16
window,
17
require('desandro-get-style-property'),
18
require('fizzy-ui-utils')
Jan 15, 2015
19
);
20
} else {
21
// browser global
22
window.Flickity = window.Flickity || {};
23
window.Flickity.animatePrototype = factory(
24
window,
25
window.getStyleProperty,
26
window.fizzyUIUtils
Jan 15, 2015
27
);
28
}
30
}( window, function factory( window, getStyleProperty, utils ) {
31
32
'use strict';
33
34
// -------------------------- requestAnimationFrame -------------------------- //
35
36
// https://gist.github.com/1866474
37
38
var lastTime = 0;
39
var prefixes = 'webkit moz ms o'.split(' ');
40
// get unprefixed rAF and cAF, if present
41
var requestAnimationFrame = window.requestAnimationFrame;
42
var cancelAnimationFrame = window.cancelAnimationFrame;
43
// loop through vendor prefixes and get prefixed rAF and cAF
44
var prefix;
45
for( var i = 0; i < prefixes.length; i++ ) {
46
if ( requestAnimationFrame && cancelAnimationFrame ) {
47
break;
48
}
49
prefix = prefixes[i];
50
requestAnimationFrame = requestAnimationFrame || window[ prefix + 'RequestAnimationFrame' ];
51
cancelAnimationFrame = cancelAnimationFrame || window[ prefix + 'CancelAnimationFrame' ] ||
52
window[ prefix + 'CancelRequestAnimationFrame' ];
53
}
54
55
// fallback to setTimeout and clearTimeout if either request/cancel is not supported
56
if ( !requestAnimationFrame || !cancelAnimationFrame ) {
57
requestAnimationFrame = function( callback ) {
58
var currTime = new Date().getTime();
59
var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
60
var id = window.setTimeout( function() {
61
callback( currTime + timeToCall );
62
}, timeToCall );
63
lastTime = currTime + timeToCall;
64
return id;
65
};
66
67
cancelAnimationFrame = function( id ) {
68
window.clearTimeout( id );
69
};
70
}
71
72
// -------------------------- animate -------------------------- //
73
74
var proto = {};
75
76
proto.startAnimation = function() {
77
if ( this.isAnimating ) {
78
return;
79
}
80
81
this.isAnimating = true;
82
this.restingFrames = 0;
83
this.animate();
84
};
85
86
proto.animate = function() {
87
this.applySelectedAttraction();
88
89
var previousX = this.x;
90
91
this.integratePhysics();
92
this.positionSlider();
93
this.settle( previousX );
94
// animate next frame
95
if ( this.isAnimating ) {
96
var _this = this;
97
requestAnimationFrame( function animateFrame() {
98
_this.animate();
99
});
100
}
101
102
/** /
103
// log animation frame rate
104
var now = new Date();
105
if ( this.then ) {
106
console.log( ~~( 1000 / (now-this.then)) + 'fps' )
107
}
108
this.then = now;
109
/**/
110
};
111
112
113
var transformProperty = getStyleProperty('transform');
114
var is3d = !!getStyleProperty('perspective');
115
116
proto.positionSlider = function() {
117
var x = this.x;
118
// wrap position around
119
if ( this.options.wrapAround && this.cells.length > 1 ) {
120
x = utils.modulo( x, this.slideableWidth );
121
x = x - this.slideableWidth;
122
this.shiftWrapCells( x );
123
}
124
125
x = x + this.cursorPosition;
126
127
// reverse if right-to-left and using transform
128
x = this.options.rightToLeft && transformProperty ? -x : x;
129
130
var value = this.getPositionValue( x );
131
132
if ( transformProperty ) {
133
// use 3D tranforms for hardware acceleration on iOS
134
// but use 2D when settled, for better font-rendering
135
this.slider.style[ transformProperty ] = is3d && this.isAnimating ?
Dec 31, 2014
136
'translate3d(' + value + ',0,0)' : 'translateX(' + value + ')';
138
this.slider.style[ this.originSide ] = value;
139
}
140
};
141
142
proto.positionSliderAtSelected = function() {
Feb 1, 2015
143
if ( !this.cells.length ) {
144
return;
145
}
146
var selectedCell = this.cells[ this.selectedIndex ];
147
this.x = -selectedCell.target;
148
this.positionSlider();
149
};
150
151
proto.getPositionValue = function( position ) {
152
if ( this.options.percentPosition ) {
153
// percent position, round to 2 digits, like 12.34%
154
return ( Math.round( ( position / this.size.innerWidth ) * 10000 ) * 0.01 )+ '%';
155
} else {
156
// pixel positioning
157
return Math.round( position ) + 'px';
158
}
159
};
160
161
proto.settle = function( previousX ) {
162
// keep track of frames where x hasn't moved
163
if ( !this.isPointerDown && Math.round( this.x * 100 ) == Math.round( previousX * 100 ) ) {
164
this.restingFrames++;
165
}
166
// stop animating if resting for 3 or more frames
167
if ( this.restingFrames > 2 ) {
168
this.isAnimating = false;
169
delete this.isFreeScrolling;
170
// render position with translateX when settled
171
if ( is3d ) {
172
this.positionSlider();
173
}
174
this.dispatchEvent('settle');
175
}
176
};
177
178
proto.shiftWrapCells = function( x ) {
179
// shift before cells
180
var beforeGap = this.cursorPosition + x;
181
this._shiftCells( this.beforeShiftCells, beforeGap, -1 );
182
// shift after cells
183
var afterGap = this.size.innerWidth - ( x + this.slideableWidth + this.cursorPosition );
184
this._shiftCells( this.afterShiftCells, afterGap, 1 );
185
};
186
187
proto._shiftCells = function( cells, gap, shift ) {
188
for ( var i=0, len = cells.length; i < len; i++ ) {
189
var cell = cells[i];
190
var cellShift = gap > 0 ? shift : 0;
191
cell.wrapShift( cellShift );
192
gap -= cell.size.outerWidth;
193
}
194
};
195
196
proto._unshiftCells = function( cells ) {
197
if ( !cells || !cells.length ) {
198
return;
199
}
200
for ( var i=0, len = cells.length; i < len; i++ ) {
201
cells[i].wrapShift( 0 );
202
}
203
};
204
205
// -------------------------- physics -------------------------- //
206
207
proto.integratePhysics = function() {
208
this.velocity += this.accel;
209
this.x += this.velocity;
210
this.velocity *= this.getFrictionFactor();
211
// reset acceleration
212
this.accel = 0;
213
};
214
215
proto.applyForce = function( force ) {
216
this.accel += force;
217
};
218
219
proto.getFrictionFactor = function() {
220
return 1 - this.options[ this.isFreeScrolling ? 'freeScrollFriction' : 'friction' ];
221
};
222
223
224
proto.getRestingPosition = function() {
225
// my thanks to Steven Wittens, who simplified this math greatly
226
return this.x + this.velocity / ( 1 - this.getFrictionFactor() );
227
};
228
229
230
proto.applySelectedAttraction = function() {
231
// do not attract if pointer down or no cells
232
var len = this.cells.length;
233
if ( this.isPointerDown || this.isFreeScrolling || !len ) {
234
return;
235
}
236
var cell = this.cells[ this.selectedIndex ];
237
var wrap = this.options.wrapAround && len > 1 ?
238
this.slideableWidth * Math.floor( this.selectedIndex / len ) : 0;
239
var distance = ( cell.target + wrap ) * -1 - this.x;
240
var force = distance * this.options.selectedAttraction;
241
this.applyForce( force );
242
};
243
Jan 15, 2015
244
return proto;
Jan 15, 2015
246
}));