OSDN Git Service

c851f8edc205e88075e300a9dc110c7d78055972
[pettanr/clientJs.git] / 0.6.x / js / 02_dom / 10_XNodeAnime.js
1 \r
2 var X_NodeAnime_QUEUE           = [],\r
3         X_NodeAnime_uid             = 0,\r
4         X_NodeAnime_reserved        = false,\r
5         X_NodeAnime_updateTimerID   = 0,\r
6         X_NodeAnime_needsDetection  = false,\r
7         \r
8         X_NodeAnime_hasTransform    = !!X_Node_CSS_VENDER_PREFIX[ 'transform' ],\r
9         \r
10         X_NodeAnime_hasDXTransform  = 5.5 <= X_UA[ 'IE' ] && X_UA[ 'IE' ] < 9 && X_UA[ 'ActiveX' ], // IEHost が 11 の場合不可\r
11         \r
12         /* Opera mobile で  translateZ(0) が有効だと XY が 0 0 になる */\r
13         /* GPUレイヤーにいる間に要素のコンテンツを変更をすると transitionend が動かなくなるっぽい Mac safari と firefox */\r
14         X_NodeAnime_translateZ      = X_Node_CSS_VENDER_PREFIX[ 'perspective' ] &&\r
15                                                                         !X_UA[ 'OperaMobile' ] && !X_UA[ 'OperaTablet' ] &&\r
16                                                                         !( X_UA[ 'IE' ] === 11 ) && !( X_UA[ 'IEHost' ] === 11 ) ? ' translateZ(0)' : '',\r
17 \r
18 /*\r
19  * phase:\r
20  *  0: アニメーション無\r
21  *  1: 登録されたばかり\r
22  *  2: 準待機\r
23  *  3: 後続待機\r
24  *  4: 強制停止(GPU転送予約)\r
25  *  5: GPU解除待ち\r
26  *  6: 開始可能\r
27  *  7: アニメーション中\r
28  */\r
29 \r
30         X_NODE_ANIME_RESET = 1,\r
31         X_NODE_ANIME_STAY_GPU = 2,\r
32         \r
33         X_NodeAnime_DEFAULT = {\r
34                 x        : NaN,\r
35                 y        : NaN,\r
36                 toX      : NaN,\r
37                 toY      : NaN,\r
38                 fromX    : NaN,\r
39                 fromY    : NaN,\r
40                 rotate   : NaN, fromRotate   : NaN, toRotate   : NaN,\r
41                 skewX    : NaN, fromSkewX    : NaN, toSkewX    : NaN,\r
42                 skewY    : NaN, fromSkewY    : NaN, toSkewY    : NaN,\r
43                 scaleX   : 1,   fromScaleX   : 1,   toScaleX   : 1,\r
44                 scaleY   : 1,   fromScaleY   : 1,   toScaleY   : 1,\r
45                 alpha    : NaN,\r
46                 scrollX  : NaN, fromScrollX  : NaN, toScrollX  : NaN,\r
47                 scrollY  : NaN, fromScrollY  : NaN, toScrollY  : NaN //,\r
48                 //doScroll : false//,\r
49                 //duration : 0\r
50                 //phase, lazyRelease, easing, follower, releaseNow, inited, progress, fallback\r
51                 // fromTime, toTime\r
52         };\r
53 \r
54 /**\r
55  * GPU サポートの効いたアニメーションの設定\r
56  * <a href="https://outcloud.blogspot.jp/2015/12/pettanR-Node-Anime.html">ぺったんRフレームワークのアニメーションメソッド</a>\r
57  * @alias Node.prototype.animate\r
58  * @param {object} obj\r
59  * @return {Node} メソッドチェーン\r
60  * @example\r
61  * xnode.animate{\r
62  *       from       : {\r
63  *          x       : num,\r
64  *      y       : num,\r
65  *      opacity : 0.0, //~1.0\r
66  *      rotate  : deg,\r
67  *      skew    : deg,\r
68  *      skewX   : deg,\r
69  *      skewY   : deg,\r
70  *      scale   : num,\r
71  *      scaleX  : num,\r
72  *      scaleY  : num,\r
73  *      scrollX : num,\r
74  *      scrollY : num\r
75  *    },\r
76  *   to          : {}, // from と同じ\r
77  *   duration    : ms,\r
78  *   lazyRelease : ms,\r
79  *   easing      : 'quadratic', // function, 'circular', 'back', 'bounce', 'elastic'\r
80  *   fallback    : bitFlag // 16:DXTransform, 8:css-p, 4:zoom(s) > 2:fontSize(s) > 1:width&height(vh,s)\r
81  *   ** tree にいなくてもアニメーションを行いイベントを発生\r
82  *   ** SVG transfrom\r
83  *   fallbackWidth, fallbackHeight, transformOrigin( 0.5, 0.5 )\r
84  * }\r
85  */\r
86 function X_Node_animate( obj ){\r
87         var list        = X_NodeAnime_QUEUE,\r
88                 from        = obj[ 'from' ] || {},\r
89                 dest        = obj[ 'to' ]   || {},\r
90                 duration    = obj[ 'duration' ],\r
91                 lazyRelease = obj[ 'lazyRelease' ],\r
92                 easing      = obj[ 'easing' ],\r
93                 fallback    = obj[ 'fallback' ],\r
94                 a, sameRate;\r
95                 \r
96         obj = this[ '_anime' ];\r
97         \r
98         if( !( this[ '_flags' ] & X_NodeFlags_IN_TREE ) ){\r
99                 alert( '@animate 要素はツリーに追加されていません!' );\r
100                 // それでもアニメーションしてタイマー代わりにするとか、、、?\r
101                 return this;\r
102         };\r
103         \r
104         if( !obj ){\r
105                 this[ '_anime' ] = obj = X_Object_copy( X_NodeAnime_DEFAULT );\r
106                 a = this[ '_css' ] && parseFloat( this[ '_css' ].opacity );\r
107                 if( 0 <= a ) obj.alpha = a;\r
108         };\r
109                 \r
110 // form :\r
111         obj.fromX       = obj.x       = X_NodeAnime_getFinite( from[ 'x' ],       obj.x );\r
112         obj.fromY       = obj.y       = X_NodeAnime_getFinite( from[ 'y' ],       obj.y );\r
113         obj.fromRotate  = obj.rotate  = X_NodeAnime_getFinite( from[ 'rotate' ],  obj.rotate );\r
114         obj.fromSkewX   = obj.skewX   = X_NodeAnime_getFinite( from[ 'skewX' ],   from[ 'skew' ],  obj.skewX );\r
115         obj.fromSkewY   = obj.skewY   = X_NodeAnime_getFinite( from[ 'skewY' ],   from[ 'skew' ],  obj.skewY );\r
116         obj.fromScaleX  = obj.scaleX  = X_NodeAnime_getFinite( from[ 'scaleX' ],  from[ 'scale' ], obj.scaleX );\r
117         obj.fromScaleY  = obj.scaleY  = X_NodeAnime_getFinite( from[ 'scaleY' ],  from[ 'scale' ], obj.scaleY );\r
118         obj.fromAlpha   = obj.alpha   = X_NodeAnime_getFinite( from[ 'opacity' ], obj.alpha );\r
119         obj.fromScrollX = obj.scrollX = X_NodeAnime_getFinite( from[ 'scrollX' ], obj.scrollX );\r
120         obj.fromScrollY = obj.scrollY = X_NodeAnime_getFinite( from[ 'scrollY' ], obj.scrollY );\r
121  // to :\r
122         obj.toX                       = X_NodeAnime_getFinite( dest[ 'x' ],       obj.x );\r
123         obj.toY                       = X_NodeAnime_getFinite( dest[ 'y' ],       obj.y );\r
124         obj.toRotate                  = X_NodeAnime_getFinite( dest[ 'rotate' ],  obj.rotate );\r
125         obj.toSkewX                   = X_NodeAnime_getFinite( dest[ 'skewX' ],   dest[ 'skew'  ], obj.skewX );\r
126         obj.toSkewY                   = X_NodeAnime_getFinite( dest[ 'skewY' ],   dest[ 'skew'  ], obj.skewY );\r
127         obj.toScaleX                  = X_NodeAnime_getFinite( dest[ 'scaleX' ],  dest[ 'scale' ], obj.scaleX );\r
128         obj.toScaleY                  = X_NodeAnime_getFinite( dest[ 'scaleY' ],  dest[ 'scale' ], obj.scaleY );\r
129         obj.toAlpha                   = X_NodeAnime_getFinite( dest[ 'opacity' ], obj.alpha );\r
130         obj.toScrollX                 = X_NodeAnime_getFinite( dest[ 'scrollX' ], obj.scrollX );\r
131         obj.toScrollY                 = X_NodeAnime_getFinite( dest[ 'scrollY' ], obj.scrollY );\r
132 \r
133         if( X_Type_isFinite( obj.toX ) && X_Type_isNaN( obj.x ) ) obj.x = obj.fromX = 0;\r
134         if( X_Type_isFinite( obj.toY ) && X_Type_isNaN( obj.y ) ) obj.y = obj.fromY = 0;\r
135         \r
136         if( obj.toRotate && X_Type_isNaN( obj.rotate ) ) obj.rotate = obj.fromRotate = 0;\r
137         if( obj.toSkewX  && X_Type_isNaN( obj.skewX  ) ) obj.skewX  = obj.fromSkewX  = 0;\r
138         if( obj.toSkewY  && X_Type_isNaN( obj.skewY  ) ) obj.skewY  = obj.fromSkewY  = 0;\r
139 \r
140         obj.duration    = 0 <= duration    && X_Type_isFinite( duration    ) ? duration    : 0;\r
141         obj.lazyRelease = 0 <= lazyRelease && X_Type_isFinite( lazyRelease ) ? lazyRelease : 0;\r
142         obj.easing      = X_Type_isFunction( easing ) ? easing : X_NodeAnime_ease[ easing ] || X_NodeAnime_ease[ 'circular' ];\r
143         obj.inited      = false;\r
144         obj.transform   = ( X_Type_isFinite( obj.x ) || X_Type_isFinite( obj.y ) || obj.lazyRelease ) && X_NodeAnime_hasTransform;\r
145         obj.doScroll    = 0 <= obj.toScrollX || 0 <= obj.toScrollY;\r
146         obj.fallback    = 0;\r
147         obj.altX        = fallback & 8  ? 'right'  : 'left'; \r
148         obj.altY        = fallback & 16 ? 'bottom' : 'top';\r
149         \r
150         // scale\r
151         if( obj.toScaleX !== 1 || obj.fromScaleX !== 1 || obj.toScaleY !== 1 || obj.fromScaleY !== 1 ){\r
152                 sameRate = obj.fromScaleX === obj.fromScaleY && obj.toScaleX === obj.toScaleY;\r
153                 \r
154                 if( X_NodeAnime_hasTransform ){\r
155                         obj.transform = true;\r
156                 } else\r
157                 if( X_NodeAnime_hasDXTransform && ( fallback & 32 ) ){ // DX Transform\r
158                         obj.fallback = 32;\r
159                 } else\r
160                 if( ( fallback & 4 ) && sameRate ){ // zoom\r
161                         obj.fallback = 4;\r
162                 } else\r
163                 if( ( fallback & 2 ) && sameRate ){ // fontSize\r
164                         obj.fallback = 2;\r
165                 } else\r
166                 if( fallback & 1 ){ // width & height\r
167                         obj.fallback = 1;\r
168                 };\r
169         };\r
170 \r
171         // rotate, skew\r
172         if( X_Type_isFinite( obj.rotate ) || X_Type_isFinite( obj.skewX ) || X_Type_isFinite( obj.skewY ) ){\r
173                 if( X_NodeAnime_hasTransform ){\r
174                         obj.transform = true;\r
175                 } else\r
176                 if( X_NodeAnime_hasDXTransform && ( fallback & 32 ) ){ // DX Transform\r
177                         obj.fallback = 32;\r
178                 };\r
179         };\r
180 \r
181         if( !obj.duration && 6 <= obj.phase ){\r
182                 this[ 'stop' ](); // 現在値で停止\r
183         } else {\r
184                 if( !obj.phase ){\r
185                         list[ list.length ] = this;\r
186                         obj.phase = 1;\r
187                         obj.uid   = ++X_NodeAnime_uid;\r
188                         X_NodeAnime_needsDetection = true;\r
189                 } else\r
190                 if( obj.phase < 4 ){\r
191                         list.splice( list.indexOf( this ), 1 );\r
192                         list[ list.length ] = this;\r
193                         obj.uid   = ++X_NodeAnime_uid;\r
194                         X_NodeAnime_needsDetection = true;\r
195                 } else\r
196                 if( obj.duration ){\r
197                         // リストの先頭にいるため検査不要でアニメーション開始可能 4, 5, 6, 7\r
198                         obj.phase = 6;\r
199                 } else\r
200                 // GPU 転送予約、または transform や opacity の値のみ設定\r
201                 if( obj.phase !== 5 ){ // GPU解除待ち ではない -> 4. 6, 7\r
202                         obj.phase      = 4; // 強制停止(GPU転送予約)または値のみ更新\r
203                         obj.releaseNow = false; // TODO folower がいるため GPU 転送できないケースあり\r
204                         X_NodeAnime_needsDetection = true;\r
205                 } else {\r
206                         obj.phase = 1;\r
207                         X_NodeAnime_needsDetection = true;\r
208                 };\r
209                 \r
210                 if( !X_NodeAnime_reserved ){\r
211                         X_NodeAnime_reserved = true;\r
212                         \r
213                         if( X_Node_updateTimerID ){\r
214                                 if( X_NodeAnime_updateTimerID ) X_NodeAnime_updateTimerID = X_Timer_cancelFrame( X_NodeAnime_updateTimerID );\r
215 \r
216                                 X_System[ 'listen' ]( X_EVENT_UPDATED, X_NodeAnime_updateAnimations );\r
217                         } else {\r
218                                 X_System[ 'unlisten' ]( X_EVENT_UPDATED, X_NodeAnime_updateAnimations );\r
219                                 X_NodeAnime_updateTimerID = X_Timer_requestFrame( X_NodeAnime_updateAnimations );\r
220                         };\r
221                 };      \r
222         };\r
223 \r
224         return this;\r
225 };\r
226 \r
227 function X_NodeAnime_getFinite( a, b, c ){\r
228         if( a || a === 0 ) return a;\r
229         if( b || b === 0 ) return b;\r
230         if( c || c === 0 ) return c;\r
231         return NaN;\r
232 };\r
233 \r
234 /*\r
235  * 1.アニメーション中の要素の停止 ->後続アニメーションの開始\r
236  * 2.アニメーション待機中の要素の停止 -> 後続アニメーションの再調査\r
237  */\r
238 /**\r
239  * アニメーションの停止。\r
240  * @alias Node.prototype.stop\r
241  * @return {Node} メソッドチェーン\r
242  */\r
243 function X_Node_stop( option ){\r
244         var obj    = this[ '_anime' ],\r
245                 list   = X_NodeAnime_QUEUE,\r
246                 rm;\r
247         \r
248         if( !obj || !obj.phase ) return this;\r
249 \r
250         switch( obj.phase ){\r
251                 case 6 : // アニメーション開始可能 ??\r
252                 case 2 : // 準待機\r
253                 case 3 : // アニメーション待機中\r
254                         X_NodeAnime_needsDetection = true;\r
255                 case 1 :\r
256                         rm = true;\r
257 \r
258                 case 4 : // 強制停止(GPU転送予約)\r
259                 case 7 : // アニメーション中\r
260                         if( option & X_NODE_ANIME_RESET ){\r
261                                 X_Object_override( obj, X_NodeAnime_DEFAULT );\r
262                         }; // TODO 終了値で停止も,,,\r
263                         \r
264                         // obj.canceled = true;\r
265                         \r
266                         if( rm ) break; // 1,2,3,6 の場合ここまで\r
267                 \r
268                         obj.toX       = obj.x;\r
269                         obj.toY       = obj.y;\r
270                         obj.toRotate  = obj.rotate;\r
271                         obj.toSkewX   = obj.skewX;\r
272                         obj.toSkewY   = obj.skewY;\r
273                         obj.toScaleX  = obj.scaleX;\r
274                         obj.toScaleY  = obj.scaleY;\r
275                         obj.toAlpha   = obj.alpha;\r
276                         obj.toScrollX = obj.scrollX;\r
277                         obj.toScrollY = obj.scrollY;\r
278 \r
279                         obj.phase     = 4; // 強制解除\r
280                         X_NodeAnime_needsDetection = true;\r
281                         \r
282                 case 5 : // GPU解除待ち\r
283                         obj.releaseNow = !( option & X_NODE_ANIME_STAY_GPU );\r
284                         break;\r
285         };\r
286 \r
287         if( rm ){\r
288                 list.splice( list.indexOf( this ), 1 );\r
289                 obj.phase = 0;  \r
290         };\r
291 \r
292         return this;\r
293 };\r
294 /*\r
295  * remove(append swap 等でない部的に呼ばれている場合も), kill 時に\r
296  */\r
297 function X_NodeAnime_stopNow( xnode ){\r
298         var obj   = xnode[ '_anime' ],\r
299                 flags = xnode[ '_flags' ],\r
300                 list  = X_NodeAnime_QUEUE,\r
301                 skipUpdate;\r
302         \r
303         // if( !obj || !obj.phase ) return; 呼び出し側で検証済\r
304 \r
305         X_NodeAnime_needsDetection = true;\r
306         list.splice( list.indexOf( xnode ), 1 );\r
307         obj.phase = 0;\r
308 \r
309         // この部分 startUpdate へ?\r
310         if( flags & ~X_Node_BitMask_RESET_GPU ){\r
311                 skipUpdate = flags & X_NodeFlags_GPU_RESERVED;\r
312                 ( flags & X_NodeFlags_GPU_RELEASE_RESERVED ) || X_NodeAnime_updatePosition( xnode, obj, 0.5, false );\r
313                 skipUpdate || ( xnode[ '_rawObject' ].style.cssText = X_Node_CSS_objToCssText( xnode ) );\r
314                 xnode[ '_flags' ] &= X_Node_BitMask_RESET_GPU;\r
315         };\r
316 };\r
317 \r
318 /*\r
319  * 1. 新規アニメーションが現在アニメーション中の要素の親か子であればアニメーションを待機\r
320  */\r
321 function X_NodeAnime_detectWaitAnimation( xnode, duration, isTest ){\r
322         var list = X_NodeAnime_QUEUE,\r
323                 i    = 0, _xnode, obj;\r
324         \r
325         for( ; _xnode = list[ i ]; ++i ){\r
326                 if( _xnode === xnode ) break;\r
327                 \r
328                 // アニメーションの優先度はリストにいる順\r
329                 // まず先行する後続待機要素の中に、親子関係のものがいないか?探す\r
330                 if( _xnode[ '_anime' ].phase <= 3 ){\r
331                         if( xnode[ 'contains' ]( _xnode ) || _xnode[ 'contains' ]( xnode ) ){ // 祖先か?見た方が早そう\r
332                                 // -> いる、このような要素が複数いる場合、誰に後続すればいいか?判然としないため、準待機フラグを立てる\r
333                                 return 2;\r
334                         };\r
335                 };\r
336         };\r
337 \r
338         // -> いない、アニメーション中(開始可能も)の要素の中に、親子関係のものがいないか?探す\r
339         //           -> いる、待機状態へ\r
340         //           -> いない、アニメーションが可能\r
341         for( i = 0; _xnode = list[ i ]; ++i ){\r
342                 if( _xnode === xnode ) break;\r
343                 obj = _xnode[ '_anime' ];\r
344                 if( 6 <= obj.phase ){\r
345                         if( xnode[ 'contains' ]( _xnode ) || _xnode[ 'contains' ]( xnode ) ){\r
346                                 return isTest ? 3 : _xnode;\r
347                         };\r
348                 };\r
349         };\r
350         // アニメーション可能\r
351         return duration ? 6 : 4; // duration がない場合は、アニメーション強制停止へ進みそこから GPU 解除待ちへ\r
352 };\r
353 \r
354 function X_NodeAnime_updateAnimations( e ){\r
355         var list = X_NodeAnime_QUEUE,\r
356                 now  = X_Timer_now(),\r
357                 c    = false,\r
358                 i, xnode, obj, _xnode,\r
359                 rm, progress, easing, lazy;\r
360         \r
361         if( X_NodeAnime_needsDetection ){\r
362                 X_NodeAnime_needsDetection = false;\r
363                 \r
364                 //\r
365                 list.sort( X_NodeAnime_sortAnimationNode );\r
366                 \r
367                 for( i = 0; xnode = list[ i ]; ++i ){\r
368                         obj = xnode[ '_anime' ];\r
369                         \r
370                         if( obj.phase <= 3 ){\r
371                                 if( !X_Type_isNumber( obj.phase = _xnode = X_NodeAnime_detectWaitAnimation( xnode, obj.duration ) ) ){\r
372                                         _xnode[ '_anime' ].follower = true;\r
373                                         obj.phase = 3; // 後続待機\r
374                                 };\r
375                         } else {\r
376                                 obj.follower = false;\r
377                         };\r
378                 };\r
379         };\r
380         \r
381         for( i = list.length; i; ){\r
382                 rm    = false;\r
383                 xnode = list[ --i ];\r
384                 obj   = xnode[ '_anime' ];\r
385 \r
386                 switch( obj.phase ){\r
387                         case 7 : // アニメーション中\r
388                                 if( now < obj.toTime ){\r
389                                         obj.progress = progress = ( now - obj.fromTime ) / obj.duration;\r
390                                         easing      = obj.easing( progress );\r
391                                         obj.x       = ( obj.toX       - obj.fromX       ) * easing + obj.fromX;\r
392                                         obj.y       = ( obj.toY       - obj.fromY       ) * easing + obj.fromY;\r
393                                         obj.rotate  = ( obj.toRotate  - obj.fromRotate  ) * easing + obj.fromRotate;\r
394                                         obj.skewX   = ( obj.toSkewX   - obj.fromSkewX   ) * easing + obj.fromSkewX;\r
395                                         obj.skewY   = ( obj.toSkewY   - obj.fromSkewY   ) * easing + obj.fromSkewY;\r
396                                         obj.scaleX  = ( obj.toScaleX  - obj.fromScaleX  ) * easing + obj.fromScaleX;\r
397                                         obj.scaleY  = ( obj.toScaleY  - obj.fromScaleY  ) * easing + obj.fromScaleY;\r
398                                         obj.alpha   = ( obj.toAlpha   - obj.fromAlpha   ) * easing + obj.fromAlpha;\r
399                                         obj.scrollX = ( obj.toScrollX - obj.fromScrollX ) * easing + obj.fromScrollX;\r
400                                         obj.scrollY = ( obj.toScrollY - obj.fromScrollY ) * easing + obj.fromScrollY;\r
401                                         X_NodeAnime_updatePosition( xnode, obj, progress, true );\r
402                                         c = true;\r
403                                         break;\r
404                                 };\r
405                                 // アニメーション終了\r
406                                 xnode[ 'asyncDispatch' ]( X_EVENT_ANIME_END );\r
407                                 \r
408                         case 4 : // 強制停止(GPU転送予約)または transform や opacity の値のみ設定\r
409                                 lazy = !obj.follower && !obj.releaseNow && obj.lazyRelease;\r
410                                 X_NodeAnime_updatePosition( xnode, obj, 1, !!lazy );\r
411 \r
412                                 //if( obj.canceled ){\r
413                                 //      xnode[ 'asyncDispatch' ]( X_EVENT_CANCELED );\r
414                                 //} else {\r
415                                         \r
416                                 //};\r
417                                 \r
418                                 if( lazy ){\r
419                                         console.log( 'アニメーション終了(' + obj.phase + ') -> GPU 解除待機 ' + lazy );\r
420                                         obj.toTime = now + lazy;\r
421                                         obj.phase = 5; // GPU解除待ち\r
422                                         c = true;\r
423                                 } else {\r
424                                         console.log( 'アニメーション終了(' + obj.phase + ') -> ' );\r
425                                         rm = true;\r
426                                 };\r
427                                 break;\r
428 \r
429                         case 6 : // アニメーション開始可能\r
430                                 obj.fromTime = now;\r
431                                 obj.toTime   = now + obj.duration;\r
432                                 obj.phase    = 7; // アニメーション中\r
433                                 obj.progress = 0;                       \r
434                                 xnode[ 'asyncDispatch' ]( X_EVENT_ANIME_START );\r
435                                 c = true;\r
436                                 //obj.canceled  = false;\r
437                                 ( !obj.inited || X_NodeAnime_translateZ ) && X_NodeAnime_updatePosition( xnode, obj, 0, true );\r
438                                 break;\r
439                         \r
440                         case 5 : // GPU解除待ち\r
441                                 if( obj.toTime <= now || obj.follower || obj.releaseNow ){\r
442                                         X_NodeAnime_translateZ && X_NodeAnime_updatePosition( xnode, obj, 1, false );\r
443                                         rm = true;\r
444                                 } else {\r
445                                         c = true;\r
446                                 };\r
447                                 break;\r
448                         \r
449                         default : // 2 or 3\r
450                                 // 待機状態でも親要素が GPU 化していなければ、開始値をセットすることは可能\r
451                                 obj.inited || X_NodeAnime_updatePosition( xnode, obj, 0, false );\r
452                                 obj.inited = true;\r
453                                 break;\r
454                 };\r
455                 \r
456                 obj.releaseNow = false;\r
457                 \r
458                 if( rm ){\r
459                         X_NodeAnime_translateZ && xnode[ 'asyncDispatch' ]( X_EVENT_GPU_RELEASED );\r
460                         // 後続のアニメーションがある場合\r
461                         if( obj.follower ) X_NodeAnime_needsDetection = c = true;\r
462                         list.splice( i, 1 );\r
463                         obj.phase = 0;\r
464                 };\r
465         };\r
466         \r
467         //c && console.log( 'anime... ' + X_Node_updateTimerID );\r
468         \r
469         if( X_NodeAnime_reserved = c ){\r
470                 if( X_Node_updateTimerID ){\r
471                         // scrollbox では X_System X_EVENT_UPDATED は不可。。。\r
472                         !e || e.type !== X_EVENT_UPDATED ?\r
473                                 X_System[ 'listen' ]( X_EVENT_UPDATED, X_NodeAnime_updateAnimations ) :\r
474                                 X_NodeAnime_updateTimerID && X_Timer_cancelFrame( X_NodeAnime_updateTimerID );\r
475                         X_NodeAnime_updateTimerID = 0;\r
476                 } else {\r
477                         X_System[ 'unlisten' ]( X_EVENT_UPDATED, X_NodeAnime_updateAnimations );\r
478                         X_NodeAnime_updateTimerID = X_Timer_requestFrame( X_NodeAnime_updateAnimations );\r
479                 };\r
480         } else {\r
481                 X_System[ 'unlisten' ]( X_EVENT_UPDATED, X_NodeAnime_updateAnimations );\r
482                 X_NodeAnime_updateTimerID = 0;\r
483         };\r
484 };\r
485 \r
486 /*\r
487  * アニメーション開始、アニメーション中、強制停止(GPU転送予約)、GPU解除待ち の要素をリストの先頭に\r
488  */\r
489 function X_NodeAnime_sortAnimationNode( xnode1, xnode2 ){\r
490         var a = 4 <= xnode1[ '_anime' ].phase,\r
491                 b = 4 <= xnode2[ '_anime' ].phase;\r
492         \r
493     if( ( a && b ) && ( !a && !b ) ){ // Chrome のみ\r
494         return xnode1[ '_anime' ].uid - xnode2[ '_anime' ].uid;\r
495     };\r
496     return a ? -1 : 1;\r
497 };\r
498 \r
499 function X_NodeAnime_updatePosition( xnode, obj, ratio, useGPU ){\r
500         var str = '',\r
501                 x, y, rotate, skewX, skewY, scaleX, scaleY, alpha,\r
502                 scrollX, scrollY;\r
503         \r
504         if( ratio === 1 ){\r
505                 x      = obj.x = obj.toX;\r
506                 y      = obj.y = obj.toY;\r
507                 rotate = X_Node_CSS_ieMathRangeFix( obj.rotate = obj.toRotate );\r
508                 skewX  = X_Node_CSS_ieMathRangeFix( obj.skewX  = obj.toSkewX );\r
509                 skewY  = X_Node_CSS_ieMathRangeFix( obj.skewY  = obj.toSkewY );\r
510                 scaleX = obj.scaleX = obj.toScaleX;\r
511                 scaleY = obj.scaleY = obj.toScaleY;\r
512                 alpha  = obj.alpha  = obj.toAlpha;\r
513                 obj.scrollX = obj.toScrollX;\r
514                 obj.scrollY = obj.toScrollY;\r
515         } else {\r
516                 x      = obj.x;\r
517                 y      = obj.y;\r
518                 rotate = X_Node_CSS_ieMathRangeFix( obj.rotate );\r
519                 skewX  = X_Node_CSS_ieMathRangeFix( obj.skewX );\r
520                 skewY  = X_Node_CSS_ieMathRangeFix( obj.skewY );\r
521                 scaleX = obj.scaleX;\r
522                 scaleY = obj.scaleY;\r
523                 alpha  = obj.alpha;\r
524         };\r
525         \r
526         //console.log( 'updatePosition x:' + x + ' gpu:' + !!useGPU );\r
527         if( obj.transform ){\r
528                 if( ( x === x || y === y ) && ( x !== 0 && y !== 0 ) ){\r
529                         if( X_UA[ 'Safari' ] && X_UA[ 'Windows' ] ){\r
530                                 // http://shinimae.hatenablog.com/entry/2016/01/13/151748\r
531                                 str = ' -webkit-translate(' + ( x | 0 ) + 'px,' + ( y | 0 ) + 'px)';\r
532                         } else {\r
533                                 str = ' translate(' + ( x | 0 ) + 'px,' + ( y | 0 ) + 'px)';\r
534                         };\r
535                 };\r
536                 if( rotate < 0 || 0 < rotate ) str += ' rotate(' + rotate + 'deg)'; // opera は rad?\r
537                 if( skewX  < 0 || 0 < skewX  ) str += ' skewX('  + skewX  + 'deg)';\r
538                 if( skewY  < 0 || 0 < skewY  ) str += ' skewY('  + skewY  + 'deg)';\r
539                 if( scaleX < 1 || 1 < scaleX ) str += ' scaleX(' + scaleX + ')';\r
540                 if( scaleY < 1 || 1 < scaleY ) str += ' scaleY(' + scaleY + ')';\r
541                 xnode[ 'css' ]( 'transform', ( str ? str.substr( 1 ) : '' ) + ( useGPU ? X_NodeAnime_translateZ : '' ) );\r
542                 \r
543                 if( X_NodeAnime_translateZ ){\r
544                         if( useGPU ){\r
545                                 if( xnode[ '_flags' ] & X_NodeFlags_GPU_RELEASE_RESERVED ){\r
546                                         xnode[ '_flags' ] &= X_Node_BitMask_RESET_GPU;\r
547                                         xnode[ '_flags' ] |= X_NodeFlags_GPU_NOW;\r
548                                 } else\r
549                                 if( !( xnode[ '_flags' ] & X_NodeFlags_GPU_NOW ) ){\r
550                                         xnode[ '_flags' ] &= X_Node_BitMask_RESET_GPU;\r
551                                         xnode[ '_flags' ] |= X_NodeFlags_GPU_RESERVED;\r
552                                 };\r
553                         } else {\r
554                                 if( xnode[ '_flags' ] & X_NodeFlags_GPU_NOW ){\r
555                                         xnode[ '_flags' ] &= X_Node_BitMask_RESET_GPU;\r
556                                         xnode[ '_flags' ] |= X_NodeFlags_GPU_RELEASE_RESERVED;\r
557                                 } else\r
558                                 if( xnode[ '_flags' ] & X_NodeFlags_GPU_RESERVED ){\r
559                                         xnode[ '_flags' ] &= X_Node_BitMask_RESET_GPU;\r
560                                 };\r
561                         };              \r
562                 };              \r
563         } else\r
564         if( obj.fallback === 32 ){\r
565                 xnode[ 'css' ]( 'dxtransform', [ x | 0, y | 0, rotate || 0, skewX || 0, skewY || 0, scaleX, scaleY, obj.altX, obj.altY ] );\r
566         } else {\r
567                 x === x && xnode[ 'css' ]( obj.altX, ( x | 0 ) + 'px' );\r
568                 y === y && xnode[ 'css' ]( obj.altY, ( y | 0 ) + 'px' );\r
569                 \r
570                 switch( obj.fallback ){\r
571                         case 4 :\r
572                                 xnode[ 'css' ]( 'zoom', scaleX );\r
573                                 break;\r
574                         case 2 :\r
575                                 xnode[ 'css' ]( 'fontSize', scaleX + 'em' );\r
576                                 break;\r
577                         case 1 :\r
578                                 \r
579                                 break;\r
580                 };\r
581         };\r
582         \r
583         if( obj.doScroll && xnode[ '_rawObject' ] ){\r
584                 console.log( 'ok ' + ratio );\r
585                 xnode[ '_rawObject' ].scrollLeft = obj.scrollX | 0;\r
586                 xnode[ '_rawObject' ].scrollTop  = obj.scrollY | 0;\r
587                 //X_Node_reserveUpdate();\r
588         };\r
589         \r
590         alpha === alpha && xnode[ 'css' ]( 'opacity', alpha );\r
591 };\r
592 \r
593 \r
594 var\r
595         X_NodeAnime_ease = {\r
596                 'quadratic' : function (k) {\r
597                                 return k * ( 2 - k );\r
598                                 /*{\r
599                         style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',\r
600                         fn: function (k) {\r
601                                 return k * ( 2 - k );\r
602                         }*/\r
603                 },\r
604                 'circular' : function (k) {\r
605                                 return Math.sqrt( 1 - ( --k * k ) );\r
606                         /*style: 'cubic-bezier(0.1, 0.57, 0.1, 1)',     // Not properly "circular" but this looks better, it should be (0.075, 0.82, 0.165, 1)\r
607                         fn: function (k) {\r
608                                 return Math.sqrt( 1 - ( --k * k ) );\r
609                         }*/\r
610                 },\r
611                 'back' : function (k) {\r
612                                 return --k * k * ( 5 * k + 4 ) + 1;\r
613                         /*style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',\r
614                         fn: function (k) {\r
615                                 var b = 4;\r
616                                 return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;\r
617                         }*/\r
618                 },\r
619                 'bounce' : function (k, X) {\r
620                                 X = 7.5625;\r
621                                 if ( k < ( 1 / 2.75 ) ) {\r
622                                         return X * k * k;\r
623                                 } else\r
624                                 if ( k < ( 2 / 2.75 ) ) {\r
625                                         return X * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;\r
626                                 } else\r
627                                 if ( k < ( 2.5 / 2.75 ) ) {\r
628                                         return X * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;\r
629                                 } else {\r
630                                         return X * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;\r
631                                 }\r
632                         /*style: '',\r
633                         fn: function (k) {\r
634                                 if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {\r
635                                         return 7.5625 * k * k;\r
636                                 } else if ( k < ( 2 / 2.75 ) ) {\r
637                                         return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;\r
638                                 } else if ( k < ( 2.5 / 2.75 ) ) {\r
639                                         return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;\r
640                                 } else {\r
641                                         return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;\r
642                                 }\r
643                         }*/\r
644                 },\r
645                 'elastic' : function (k) {\r
646                                 return k === 0 ? 0 : k === 1 ? 1 : ( 0.4 * Math.pow( 2, - 10 * k ) * Math.sin( ( k - 0.055 ) * 28.56 ) + 1 );\r
647                         /*style: '',\r
648                         fn: function (k) {\r
649                                 var f = 0.22,\r
650                                         e = 0.4;\r
651 \r
652                                 if ( k === 0 ) { return 0; }\r
653                                 if ( k == 1 ) { return 1; }\r
654 \r
655                                 return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );\r
656                         } */\r
657                 }\r
658         };\r