]> git.proxmox.com Git - extjs.git/blame - extjs/packages/core/src/fx/runner/CssTransition.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / core / src / fx / runner / CssTransition.js
CommitLineData
6527f429
DM
1/**\r
2 * @private\r
3 */\r
4Ext.define('Ext.fx.runner.CssTransition', {\r
5 extend: 'Ext.fx.runner.Css',\r
6 requires: ['Ext.AnimationQueue'],\r
7 alternateClassName: 'Ext.Animator',\r
8 singleton: true,\r
9\r
10 listenersAttached: false,\r
11\r
12 constructor: function() {\r
13 this.runningAnimationsData = {};\r
14\r
15 return this.callParent(arguments);\r
16 },\r
17\r
18 attachListeners: function() {\r
19 this.listenersAttached = true;\r
20\r
21 Ext.getWin().on('transitionend', 'onTransitionEnd', this);\r
22 },\r
23\r
24 onTransitionEnd: function(e) {\r
25 var target = e.target,\r
26 id = target.id;\r
27\r
28 if (id && this.runningAnimationsData.hasOwnProperty(id)) {\r
29 this.refreshRunningAnimationsData(Ext.get(target), [e.browserEvent.propertyName]);\r
30 }\r
31 },\r
32\r
33 onAnimationEnd: function(element, data, animation, isInterrupted, isReplaced) {\r
34 var id = element.getId(),\r
35 runningData = this.runningAnimationsData[id],\r
36 endRules = {},\r
37 endData = {},\r
38 runningNameMap, toPropertyNames, i, ln, name;\r
39\r
40 animation.un('stop', 'onAnimationStop', this);\r
41\r
42 if (runningData) {\r
43 runningNameMap = runningData.nameMap;\r
44 }\r
45\r
46 endRules[id] = endData;\r
47\r
48 if (data.onBeforeEnd) {\r
49 data.onBeforeEnd.call(data.scope || this, element, isInterrupted);\r
50 }\r
51\r
52 animation.fireEvent('animationbeforeend', animation, element, isInterrupted);\r
53 this.fireEvent('animationbeforeend', this, animation, element, isInterrupted);\r
54\r
55 if (isReplaced || (!isInterrupted && !data.preserveEndState)) {\r
56 toPropertyNames = data.toPropertyNames;\r
57\r
58 for (i = 0,ln = toPropertyNames.length; i < ln; i++) {\r
59 name = toPropertyNames[i];\r
60\r
61 if (runningNameMap && !runningNameMap.hasOwnProperty(name)) {\r
62 endData[name] = null;\r
63 }\r
64 }\r
65 }\r
66\r
67 if (data.after) {\r
68 Ext.merge(endData, data.after);\r
69 }\r
70\r
71 this.applyStyles(endRules);\r
72\r
73 if (data.onEnd) {\r
74 data.onEnd.call(data.scope || this, element, isInterrupted);\r
75 }\r
76\r
77 animation.fireEvent('animationend', animation, element, isInterrupted);\r
78 this.fireEvent('animationend', this, animation, element, isInterrupted);\r
79 Ext.AnimationQueue.stop(Ext.emptyFn, animation);\r
80 },\r
81\r
82 onAllAnimationsEnd: function(element) {\r
83 var id = element.getId(),\r
84 endRules = {};\r
85\r
86 delete this.runningAnimationsData[id];\r
87\r
88 endRules[id] = {\r
89 'transition-property': null,\r
90 'transition-duration': null,\r
91 'transition-timing-function': null,\r
92 'transition-delay': null\r
93 };\r
94\r
95 this.applyStyles(endRules);\r
96 this.fireEvent('animationallend', this, element);\r
97 },\r
98\r
99 hasRunningAnimations: function(element) {\r
100 var id = element.getId(),\r
101 runningAnimationsData = this.runningAnimationsData;\r
102\r
103 return runningAnimationsData.hasOwnProperty(id) && runningAnimationsData[id].sessions.length > 0;\r
104 },\r
105\r
106 refreshRunningAnimationsData: function(element, propertyNames, interrupt, replace) {\r
107 var id = element.getId(),\r
108 runningAnimationsData = this.runningAnimationsData,\r
109 runningData = runningAnimationsData[id];\r
110\r
111 if (!runningData) {\r
112 return;\r
113 }\r
114\r
115 var nameMap = runningData.nameMap,\r
116 nameList = runningData.nameList,\r
117 sessions = runningData.sessions,\r
118 ln, j, subLn, name,\r
119 i, session, map, list,\r
120 hasCompletedSession = false;\r
121\r
122 interrupt = Boolean(interrupt);\r
123 replace = Boolean(replace);\r
124\r
125 if (!sessions) {\r
126 return this;\r
127 }\r
128\r
129 ln = sessions.length;\r
130\r
131 if (ln === 0) {\r
132 return this;\r
133 }\r
134\r
135 if (replace) {\r
136 runningData.nameMap = {};\r
137 nameList.length = 0;\r
138\r
139 for (i = 0; i < ln; i++) {\r
140 session = sessions[i];\r
141 this.onAnimationEnd(element, session.data, session.animation, interrupt, replace);\r
142 }\r
143\r
144 sessions.length = 0;\r
145 }\r
146 else {\r
147 for (i = 0; i < ln; i++) {\r
148 session = sessions[i];\r
149 map = session.map;\r
150 list = session.list;\r
151\r
152 for (j = 0,subLn = propertyNames.length; j < subLn; j++) {\r
153 name = propertyNames[j];\r
154\r
155 if (map[name]) {\r
156 delete map[name];\r
157 Ext.Array.remove(list, name);\r
158 session.length--;\r
159 if (--nameMap[name] == 0) {\r
160 delete nameMap[name];\r
161 Ext.Array.remove(nameList, name);\r
162 }\r
163 }\r
164 }\r
165\r
166 if (session.length == 0) {\r
167 sessions.splice(i, 1);\r
168 i--;\r
169 ln--;\r
170\r
171 hasCompletedSession = true;\r
172 this.onAnimationEnd(element, session.data, session.animation, interrupt);\r
173 }\r
174 }\r
175 }\r
176\r
177 if (!replace && !interrupt && sessions.length == 0 && hasCompletedSession) {\r
178 this.onAllAnimationsEnd(element);\r
179 }\r
180 },\r
181\r
182 getRunningData: function(id) {\r
183 var runningAnimationsData = this.runningAnimationsData;\r
184\r
185 if (!runningAnimationsData.hasOwnProperty(id)) {\r
186 runningAnimationsData[id] = {\r
187 nameMap: {},\r
188 nameList: [],\r
189 sessions: []\r
190 };\r
191 }\r
192\r
193 return runningAnimationsData[id];\r
194 },\r
195\r
196 getTestElement: function() {\r
197 var testElement = this.testElement,\r
198 iframe, iframeDocument, iframeStyle;\r
199\r
200 if (!testElement) {\r
201 iframe = document.createElement('iframe');\r
202 //<debug>\r
203 // Set an attribute that tells the test runner to ignore this node when checking\r
204 // for dom cleanup\r
205 iframe.setAttribute('data-sticky', true);\r
206 //</debug>\r
207 iframe.setAttribute('tabIndex', -1);\r
208 iframeStyle = iframe.style;\r
209 iframeStyle.setProperty('visibility', 'hidden', 'important');\r
210 iframeStyle.setProperty('width', '0px', 'important');\r
211 iframeStyle.setProperty('height', '0px', 'important');\r
212 iframeStyle.setProperty('position', 'absolute', 'important');\r
213 iframeStyle.setProperty('border', '0px', 'important');\r
214 iframeStyle.setProperty('zIndex', '-1000', 'important');\r
215\r
216 document.body.appendChild(iframe);\r
217 iframeDocument = iframe.contentDocument;\r
218\r
219 iframeDocument.open();\r
220 iframeDocument.writeln('</body>');\r
221 iframeDocument.close();\r
222\r
223 this.testElement = testElement = iframeDocument.createElement('div');\r
224 testElement.style.setProperty('position', 'absolute', 'important');\r
225 iframeDocument.body.appendChild(testElement);\r
226 this.testElementComputedStyle = window.getComputedStyle(testElement);\r
227 }\r
228\r
229 return testElement;\r
230 },\r
231\r
232 getCssStyleValue: function(name, value) {\r
233 var testElement = this.getTestElement(),\r
234 computedStyle = this.testElementComputedStyle,\r
235 style = testElement.style;\r
236\r
237 style.setProperty(name, value);\r
238\r
239 if (Ext.browser.is.Firefox) {\r
240 // We force a repaint of the element in Firefox to make sure the computedStyle to be updated\r
241 testElement.offsetHeight;\r
242 }\r
243\r
244 value = computedStyle.getPropertyValue(name);\r
245 style.removeProperty(name);\r
246\r
247 return value;\r
248 },\r
249\r
250 run: function(animations) {\r
251 var me = this,\r
252 isLengthPropertyMap = me.lengthProperties,\r
253 fromData = {},\r
254 toData = {},\r
255 data = {},\r
256 element, elementId, from, to, before,\r
257 fromPropertyNames, toPropertyNames,\r
258 doApplyTo, message,\r
259 runningData, elementData,\r
260 i, j, ln, animation, propertiesLength, sessionNameMap,\r
261 computedStyle, formattedName, name, toFormattedValue,\r
262 computedValue, fromFormattedValue, isLengthProperty,\r
263 runningNameMap, runningNameList, runningSessions, runningSession;\r
264\r
265 if (!me.listenersAttached) {\r
266 me.attachListeners();\r
267 }\r
268\r
269 animations = Ext.Array.from(animations);\r
270\r
271 for (i = 0,ln = animations.length; i < ln; i++) {\r
272 animation = animations[i];\r
273 animation = Ext.factory(animation, Ext.fx.Animation);\r
274 element = animation.getElement();\r
275\r
276 // Empty function to prevent idleTasks from running while we animate.\r
277 Ext.AnimationQueue.start(Ext.emptyFn, animation);\r
278\r
279 computedStyle = window.getComputedStyle(element.dom);\r
280\r
281 elementId = element.getId();\r
282\r
283 data = Ext.merge({}, animation.getData());\r
284\r
285 if (animation.onBeforeStart) {\r
286 animation.onBeforeStart.call(animation.scope || me, element);\r
287 }\r
288 animation.fireEvent('animationstart', animation);\r
289 me.fireEvent('animationstart', me, animation);\r
290\r
291 data[elementId] = data;\r
292\r
293 before = data.before;\r
294 from = data.from;\r
295 to = data.to;\r
296\r
297 data.fromPropertyNames = fromPropertyNames = [];\r
298 data.toPropertyNames = toPropertyNames = [];\r
299\r
300 for (name in to) {\r
301 if (to.hasOwnProperty(name)) {\r
302 to[name] = toFormattedValue = me.formatValue(to[name], name);\r
303 formattedName = me.formatName(name);\r
304 isLengthProperty = isLengthPropertyMap.hasOwnProperty(name);\r
305\r
306 if (!isLengthProperty) {\r
307 toFormattedValue = me.getCssStyleValue(formattedName, toFormattedValue);\r
308 }\r
309\r
310 if (from.hasOwnProperty(name)) {\r
311 from[name] = fromFormattedValue = me.formatValue(from[name], name);\r
312\r
313 if (!isLengthProperty) {\r
314 fromFormattedValue = me.getCssStyleValue(formattedName, fromFormattedValue);\r
315 }\r
316\r
317 if (toFormattedValue !== fromFormattedValue) {\r
318 fromPropertyNames.push(formattedName);\r
319 toPropertyNames.push(formattedName);\r
320 }\r
321 }\r
322 else {\r
323 computedValue = computedStyle.getPropertyValue(formattedName);\r
324\r
325 if (toFormattedValue !== computedValue) {\r
326 toPropertyNames.push(formattedName);\r
327 }\r
328 }\r
329 }\r
330 }\r
331\r
332 propertiesLength = toPropertyNames.length;\r
333\r
334 if (propertiesLength === 0) {\r
335 me.onAnimationEnd(element, data, animation);\r
336 continue;\r
337 }\r
338\r
339 runningData = me.getRunningData(elementId);\r
340 runningSessions = runningData.sessions;\r
341\r
342 if (runningSessions.length > 0) {\r
343 me.refreshRunningAnimationsData(\r
344 element, Ext.Array.merge(fromPropertyNames, toPropertyNames), true, data.replacePrevious\r
345 );\r
346 }\r
347\r
348 runningNameMap = runningData.nameMap;\r
349 runningNameList = runningData.nameList;\r
350\r
351 sessionNameMap = {};\r
352 for (j = 0; j < propertiesLength; j++) {\r
353 name = toPropertyNames[j];\r
354 sessionNameMap[name] = true;\r
355\r
356 if (!runningNameMap.hasOwnProperty(name)) {\r
357 runningNameMap[name] = 1;\r
358 runningNameList.push(name);\r
359 }\r
360 else {\r
361 runningNameMap[name]++;\r
362 }\r
363 }\r
364\r
365 runningSession = {\r
366 element: element,\r
367 map: sessionNameMap,\r
368 list: toPropertyNames.slice(),\r
369 length: propertiesLength,\r
370 data: data,\r
371 animation: animation\r
372 };\r
373 runningSessions.push(runningSession);\r
374\r
375 animation.on('stop', 'onAnimationStop', me);\r
376\r
377 elementData = Ext.apply({}, before);\r
378 Ext.apply(elementData, from);\r
379\r
380 if (runningNameList.length > 0) {\r
381 fromPropertyNames = Ext.Array.difference(runningNameList, fromPropertyNames);\r
382 toPropertyNames = Ext.Array.merge(fromPropertyNames, toPropertyNames);\r
383 elementData['transition-property'] = fromPropertyNames;\r
384 }\r
385\r
386 fromData[elementId] = elementData;\r
387 toData[elementId] = Ext.apply({}, to);\r
388\r
389 toData[elementId]['transition-property'] = toPropertyNames;\r
390 toData[elementId]['transition-duration'] = data.duration;\r
391 toData[elementId]['transition-timing-function'] = data.easing;\r
392 toData[elementId]['transition-delay'] = data.delay;\r
393\r
394 animation.startTime = Date.now();\r
395 }\r
396\r
397 message = me.$className;\r
398\r
399 me.applyStyles(fromData);\r
400\r
401 doApplyTo = function(e) {\r
402 if (e.data === message && e.source === window) {\r
403 window.removeEventListener('message', doApplyTo, false);\r
404 me.applyStyles(toData);\r
405 }\r
406 };\r
407\r
408 if (window.requestAnimationFrame) {\r
409 window.requestAnimationFrame(function() {\r
410 window.addEventListener('message', doApplyTo, false);\r
411 window.postMessage(message, '*');\r
412 });\r
413 }else {\r
414 Ext.defer(function() {\r
415 window.addEventListener('message', doApplyTo, false);\r
416 window.postMessage(message, '*');\r
417 }, 1)\r
418 }\r
419 },\r
420\r
421 onAnimationStop: function(animation) {\r
422 var runningAnimationsData = this.runningAnimationsData,\r
423 id, runningData, sessions, i, ln, session;\r
424\r
425 for (id in runningAnimationsData) {\r
426 if (runningAnimationsData.hasOwnProperty(id)) {\r
427 runningData = runningAnimationsData[id];\r
428 sessions = runningData.sessions;\r
429\r
430 for (i = 0,ln = sessions.length; i < ln; i++) {\r
431 session = sessions[i];\r
432 if (session.animation === animation) {\r
433 this.refreshRunningAnimationsData(session.element, session.list.slice(), false);\r
434 }\r
435 }\r
436 }\r
437 }\r
438 }\r
439});\r