,
3 | * sponsored by Evil Martians.
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | ;(function($) {
20 | "use strict";
21 |
22 | // Common methods and properties for jQuery Transition Events plugin.
23 | // Mostly for internal usage, but maybe helpful for some hack stuff:
24 | //
25 | // if ( $.Transitions.isSupported() ) {
26 | // // CSS Transitions is supported
27 | // }
28 | $.Transitions = {
29 |
30 | // Hash of property name to event name with vendor prefixes.
31 | // It is used to detect prefix.
32 | _names: {
33 | // Webkit must be on bottom, because Opera try to use webkit
34 | // prefix.
35 | 'transition': 'transitionend',
36 | 'OTransition': 'oTransitionEnd',
37 | 'WebkitTransition': 'webkitTransitionEnd',
38 | 'MozTransition': 'transitionend'
39 | },
40 |
41 | // Return array of milliseconds for CSS value of `transition-duration`.
42 | // It’s used in `$.fn.afterTransition`.
43 | _parseTimes: function (string) {
44 | var value, array = string.split(/,\s*/);
45 | for (var i = 0; i < array.length; i++) {
46 | value = array[i];
47 | array[i] = parseFloat(value);
48 | if ( value.match(/\ds/) ) {
49 | array[i] = array[i] * 1000;
50 | }
51 | }
52 | return array;
53 | },
54 |
55 | // Autodetect vendor prefix and return `transitionend` event name.
56 | //
57 | // If browser didn’t support CSS Transitions it will return `false`.
58 | getEvent: function () {
59 | var finded = false;
60 | for ( var prop in this._names ) {
61 | if ( typeof(document.body.style[prop]) != 'undefined' ) {
62 | finded = this._names[prop];
63 | break;
64 | }
65 | }
66 |
67 | this.getEvent = function () {
68 | return finded;
69 | };
70 |
71 | return finded;
72 | },
73 |
74 | // Alias to vendor prefixed `requestAnimationFrame`. Will be replace
75 | // by native function after first call.
76 | animFrame: function (callback) {
77 | var raf = window.requestAnimationFrame ||
78 | window.webkitRequestAnimationFrame ||
79 | window.mozRequestAnimationFrame ||
80 | window.msRequestAnimationFrame;
81 | if ( raf ) {
82 | this.animFrame = function (callback) {
83 | return raf.call(window, callback);
84 | };
85 | } else {
86 | this.animFrame = function (callback) {
87 | return setTimeout(callback, 10);
88 | };
89 | }
90 | return this.animFrame(callback);
91 | },
92 |
93 | // Return `true` if browser support CSS Transitions.
94 | isSupported: function () {
95 | return this.getEvent() !== false;
96 | }
97 |
98 | }
99 |
100 | // jQuery node methods.
101 | $.extend($.fn, {
102 |
103 | // Call `callback` after CSS Transition finish
104 | // `delay + (durationPart * duration)`. It will call `callback` only
105 | // once, in difference from `transitionEnd`.
106 | //
107 | // $('.show-video').click(function () {
108 | // $('.slider').addClass('video-position').afterTransition(
109 | // function () { autoPlayVideo(); });
110 | // });
111 | //
112 | // You can set `durationPart` to call `callback` in the middle of
113 | // transition:
114 | //
115 | // $('.fliper').addClass('rotate').afterTransition(0.5, function () {
116 | // $(this).find('.backface').show();
117 | // });
118 | //
119 | // Callback will get object with `propertyName` and `elapsedTime`
120 | // properties. If transition is set to difference properties, it will
121 | // be called on every property.
122 | //
123 | // This method doesn’t check, that transition is really finished (it can
124 | // be canceled in the middle).
125 | afterTransition: function (durationPart, callback) {
126 | if ( typeof(callback) == 'undefined' ) {
127 | callback = durationPart;
128 | durationPart = 1;
129 | }
130 |
131 | if ( !$.Transitions.isSupported() ) {
132 | for (var i = 0; i < this.length; i++) {
133 | callback.call(this[i], {
134 | type: 'aftertransition',
135 | elapsedTime: 0,
136 | propertyName: '',
137 | currentTarget: this[i]
138 | });
139 | }
140 | return this;
141 | }
142 |
143 | for (var i = 0; i < this.length; i++) {
144 | var el = $(this[i]);
145 | var props = el.css('transition-property').split(/,\s*/);
146 | var durations = el.css('transition-duration');
147 | var delays = el.css('transition-delay');
148 |
149 | durations = $.Transitions._parseTimes(durations);
150 | delays = $.Transitions._parseTimes(delays);
151 |
152 | var prop, duration, delay, after, elapsed;
153 | for (var j = 0; j < props.length; j++) {
154 | prop = props[j];
155 | duration = durations[ durations.length == 1 ? 0 : j ];
156 | delay = delays[ delays.length == 1 ? 0 : j ];
157 | after = delay + (duration * durationPart);
158 | elapsed = duration * durationPart / 1000;
159 |
160 | (function (el, prop, after, elapsed) {
161 | setTimeout(function () {
162 | $.Transitions.animFrame(function () {
163 | callback.call(el[0], {
164 | type: 'aftertransition',
165 | elapsedTime: elapsed,
166 | propertyName: prop,
167 | currentTarget: el[0]
168 | });
169 | });
170 | }, after);
171 | })(el, prop, after, elapsed);
172 | }
173 | }
174 | return this;
175 | },
176 |
177 | // Set `callback` to listen every CSS Transition finish.
178 | // It will call `callback` on every finished transition,
179 | // in difference from `afterTransition`.
180 | //
181 | // It just bind to `transitionend` event, but detect vendor prefix.
182 | //
183 | // Callback will get event object with `propertyName` and `elapsedTime`
184 | // properties. If transition is set to difference properties, it will
185 | // be called on every property.
186 | //
187 | // Note, that `callback` will get original event object, not from
188 | // jQuery.
189 | //
190 | // var slider = $('.slider').transitionEnd(function () {
191 | // if ( slider.hasClass('video-position') ) {
192 | // autoPlayVideo();
193 | // }
194 | // });
195 | //
196 | // $('.show-video').click(function () {
197 | // slider.addClass('video-position');
198 | // });
199 | //
200 | // If transition will be canceled before finish, event won’t be fired.
201 | transitionEnd: function (callback) {
202 | for (var i = 0; i < this.length; i++) {
203 | this[i].addEventListener($.Transitions.getEvent(), function (e) {
204 | callback.call(this, e);
205 | });
206 | }
207 | return this;
208 | }
209 |
210 | });
211 |
212 | }).call(this, jQuery);
213 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "transition-events",
3 | "version": "0.2.1",
4 | "devDependencies": {
5 | "coffee-script": "1.3.3",
6 | "fs-extra": "0.1.3",
7 | "glob": "3.1.12",
8 | "uglify-js": "1.3.3"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | jQuery Transition Events Test
6 |
7 |
8 |
67 |
68 |
69 | jQuery Transition Events Test
70 |
71 | Support: .
72 | Event name: .
73 |
74 |
78 |
106 |
107 |
108 |
--------------------------------------------------------------------------------