-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjcp-transitioninterval.js
143 lines (123 loc) · 4.56 KB
/
jcp-transitioninterval.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
/**
* Animation timer class
*
* Runs and manages an interval for processing CSS-augmented animation.
*
* This timer works in one of a few ways, depending on browser features available.
* - In fully-enabled mode, we use transitionEnd DOM events to trigger the start of the next
* animation frame. This ensures consistent timing and reduces transition jitter sometimes apparent
* in the half-enabled mode.
* - In half-enabled mode, transitions are still used to smooth between frames but no events are
* available to signify the end of transitions. In this case we just use an interval to try to
* sync the javascript CSS updates with the end of the CSS transitions - but this *can* get out
* of sync slightly which leads to tearing in the animation. Fortunately this mode is unlikely
* to be executed.
* - In fallback mode, we simply sample at a higher framerate to compensate for the lack of browser
* transition support.
*
* TransitionIntervals require three parameters:
* - a callback to execute for every tick of the timer. The context of the callback is the timer object,
* which contains a frameCount variable which may be of use in callbacks.
* - a sampling rate for the animation when running in CSS-enabled browsers
* - a fallback sampling rate when transition smoothing is not available
*
* After construction, call addElements() to append DOM elements under the control of the TransitionInterval's
* animation timer. These need to be registered in order to apply the correct CSS attributes necessary for
* transition timing on the elements.
*
* @requires jcparallax.js
*/
(function($) {
jcparallax.TransitionInterval = function(cb, framerate, fbFramerate, extraCssAnimCheckCb)
{
this.elements = jQuery([]);
this.setFramerates(framerate, fbFramerate, extraCssAnimCheckCb);
this.callback = cb;
this.frameCount = 0;
};
$.extend(jcparallax.TransitionInterval.prototype, {
_running : false,
timeout : null, // frame timeout reference (fallback mode)
update : null, // underlying registered timer update callback
start : function()
{
// ignore if already running
if (this._running) {
return;
}
var that = this;
if (jcparallax.support.transitionEndEvent) {
this.update = function() {
// if callback returns to flag no movement, check again at our frame interval
if (!that.callback.call(that)) {
clearTimeout( that.timeout );
that.timeout = setTimeout(function() {
that.timeout = null;
that.update();
}, that.framerate);
} else {
// this check is placed here to failsafe in the event that browser rendering stutters,
// resulting in the transition end event never being called and the animation failing
// to restart.
clearTimeout( that.timeout );
that.timeout = setTimeout(function() {
that.timeout = null;
that.update();
}, that.framerate * 2);
++that.frameCount;
}
};
// bind to the first element, we only need it to run once since all framerates are the same
this.elements.on(jcparallax.support.transitionEndEvent, that.update);
} else {
var supported = jcparallax.support.transitions && (!this.useFallbackCheckCb || (this.useFallbackCheckCb && this.useFallbackCheckCb()));
this.update = function() {
that.callback.call(that);
that.timeout = setTimeout(that.update, supported ? that.framerate : that.fbFramerate);
++that.frameCount;
};
this.timeout = setTimeout(this.update, supported ? this.framerate : this.fbFramerate);
}
this._running = true;
this.update(); // run to first frame
},
stop : function()
{
if (this._running) {
if (jcparallax.support.transitionEndEvent) {
this.elements.off(jcparallax.support.transitionEndEvent, this.update);
} else {
clearTimeout( this.timeout );
this.timeout = null;
}
this._running = false;
}
},
setFramerates : function(cssFramerate, fallbackFramerate, extraCssAnimCheckCb)
{
this.framerate = cssFramerate;
this.fbFramerate = fallbackFramerate;
this.useFallbackCheckCb = extraCssAnimCheckCb;
this._applyCss();
},
addElements : function(els)
{
if ($.isArray(els)) {
this.elements.pushStack(els);
} else {
this.elements = this.elements.add(els);
}
this._applyCss();
},
_applyCss : function()
{
var that = this,
cssFramerate = (this.framerate / 1000) + 's';
// set transition-duration for CSS sample tweening
$.each(jcparallax.cssDomPrefixes, function(i, prefix) {
that.elements.css(prefix + 'transition-duration', cssFramerate);
});
this.elements.css('transition-duration', cssFramerate);
}
});
})(jQuery);