\r
+var X_NodeAnime_QUEUE = [],\r
+ X_NodeAnime_uid = 0,\r
+ X_NodeAnime_reserved = false,\r
+ X_NodeAnime_updateTimerID = 0,\r
+ X_NodeAnime_needsDetection = false,\r
+ \r
+ X_NodeAnime_hasTransform = !!X_Node_CSS_VENDER_PREFIX[ 'transform' ],\r
+ /* Opera mobile で translateZ(0) が有効だと XY が 0 0 になる */\r
+ /* GPUレイヤーにいる間に要素のコンテンツを変更をすると transitionend が動かなくなるっぽい Mac safari と firefox */\r
+ X_NodeAnime_translateZ = X_Node_CSS_VENDER_PREFIX[ 'perspective' ] && !X_UA[ 'OperaMobile' ] && !X_UA[ 'OperaTablet' ] ? ' translateZ(0)' : '',\r
\r
-var\r
- ease = {\r
- quadratic: {\r
- style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',\r
- fn: function (k) {\r
- return k * ( 2 - k );\r
- }\r
- },\r
- circular: {\r
- 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
- fn: function (k) {\r
- return Math.sqrt( 1 - ( --k * k ) );\r
- }\r
- },\r
- back: {\r
- style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',\r
- fn: function (k) {\r
- var b = 4;\r
- return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;\r
- }\r
- },\r
- bounce: {\r
- style: '',\r
- fn: function (k) {\r
- if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {\r
- return 7.5625 * k * k;\r
- } else if ( k < ( 2 / 2.75 ) ) {\r
- return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;\r
- } else if ( k < ( 2.5 / 2.75 ) ) {\r
- return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;\r
- } else {\r
- return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;\r
- }\r
- }\r
- },\r
- elastic: {\r
- style: '',\r
- fn: function (k) {\r
- var f = 0.22,\r
- e = 0.4;\r
+ X_NodeAnime_transitionProps = X_NodeAnime_hasTransform ? X_Node_CSS_VENDER_PREFIX[ 'transform' ] : 'left,top';\r
\r
- if ( k === 0 ) { return 0; }\r
- if ( k == 1 ) { return 1; }\r
+/*\r
+ * phase:\r
+ * 0: アニメーション無\r
+ * 1: 登録されたばかり\r
+ * 2: 準待機\r
+ * 3: 後続待機\r
+ * 4: 強制停止(GPU転送予約)\r
+ * 5: GPU解除待ち\r
+ * 6: 開始可能\r
+ * 7: アニメーション中\r
+ * TODO node.css( 'opacity' ) の上書き\r
+ */\r
\r
- return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );\r
- }\r
- }\r
+var X_NODE_ANIME_RESET = 1,\r
+ X_NODE_ANIME_STAY_GPU = 2;\r
+\r
+/**\r
+ * GPU サポートの効いたアニメーションの設定 X.Event.ANIME_START, X.Event.ANIME_END, X.Event.GPU_RELEASED\r
+ * @alias Node.prototype.animate\r
+ * @param {object} start { x : 0, y : 0, opacity : 1 }\r
+ * @param {object} dest { x : 100, y : 100, opacity : 0 }\r
+ * @param {number=} duartion アニメーション時間 ms\r
+ * @param {string=} easing 'quadratic', 'circular', 'back', 'bounce', 'elastic'\r
+ * @param {number=} wait GPU レイヤーの遅延解除 ms\r
+ * @param {number=} option フォールバックについて\r
+ * @return {Node} メソッドチェーン\r
+ */\r
+function X_Node_animate( start, dest, duration, easing, lazyRelease, option ){\r
+ var list = X_NodeAnime_QUEUE,\r
+ obj = this[ '_anime' ];\r
+ \r
+ if( !( this[ '_flags' ] & X_NodeFlags_IN_TREE ) ){\r
+ alert( '@animate 要素はツリーに追加されていません!' );\r
+ // それでもアニメーションしてタイマー代わりにするとか、、、?\r
+ return this;\r
+ };\r
+ \r
+ if( !obj ){\r
+ this[ '_anime' ] = obj = {\r
+ x : 0, y : 0, a : 1,\r
+ duration : 0\r
+ //phase, lazyRelease, easing, follower, releaseNow\r
+ };\r
+ };\r
+ \r
+ if( 0 <= duration && X_Type_isFinite( duration ) ){\r
+ obj.duration = duration;\r
};\r
+\r
+ obj.easing = X_Type_isFunction( easing ) ? easing : X_NodeAnime_ease[ easing ] || X_NodeAnime_ease[ 'circular' ];\r
\r
-// 新規アニメーションが追加された場合、\r
-// tree が dirty なら AFTER_COMMIT を待つ\r
-// 1) xnode の既存アニメーションとの親子関係の調査\r
-// 親なら -> 既存アニメーションの GPU レイヤー解除\r
-// 子なら -> GPU レイヤーを設定しない\r
-// 2) 目標座標のセット\r
-// 3) アニメーション完了後も GPU レイヤーはしばらく解除しない スクロール等の連続アニメーション時に GPU 転送時間で画面ががたつくから\r
-// アニメ中の remove\r
-\r
-var X_Node_ANIMATIONS = [],\r
- X_Node_Anime_reserved = false,\r
- X_Node_Anime_updateTimerID = 0,\r
- X_Node_Anime_needsDetection = false,\r
- X_Node_Anime_hasTransform = !!X_Node_CSS_VENDER_PREFIX[ 'transform' ],\r
- /* Opera mobile で translateZ(0) が有効だと XY が 0 0 になる */\r
- X_Node_Anime_translateZ = X_Node_CSS_VENDER_PREFIX[ 'perspective' ] && !X.UA.OperaMobile && !X.UA.OperaTablet ? ' translateZ(0)' : '',\r
- X_Node_Anime_hasTransition = !!X_Node_CSS_VENDER_PREFIX[ 'transitionDelay' ] && !X.UA.Opera, // Opera12(XP,8.1) 切った方がスムース\r
- X_Node_Anime_transitionProps = X_Node_Anime_hasTransform ? X_Node_CSS_VENDER_PREFIX[ 'transform' ] : 'left,top';\r
+// form :\r
+ obj.startX = obj.x = X_NodeAnime_getFinite( start[ 'x' ], obj.x );\r
+ obj.startY = obj.y = X_NodeAnime_getFinite( start[ 'y' ], obj.y );\r
+ obj.startA = obj.a = X_NodeAnime_getFinite( start[ 'opacity' ], obj.a );\r
\r
-// gpu化だけ transformX , willChange\r
-// 終了位置の変更\r
-// 中断\r
+ // to :\r
+ obj.destX = X_NodeAnime_getFinite( dest[ 'x' ], obj.x );\r
+ obj.destY = X_NodeAnime_getFinite( dest[ 'y' ], obj.y );\r
+ obj.destA = X_NodeAnime_getFinite( dest[ 'opacity' ], obj.a );\r
\r
+ obj.lazyRelease = 0 <= lazyRelease && X_Type_isFinite( lazyRelease ) ? lazyRelease : 0;\r
+ obj.inited = false;\r
\r
-Node.prototype.animate = function( start, dest, duration, easing, wait ){\r
- var obj = this._anime || ( this._anime = {} );\r
- \r
- obj.duration = 0 <= duration && X.Type.isFinite( duration ) ? duration : 500;\r
- obj.easing = ease[ easing ] || ease.circular;\r
- // 現在 GPUレイヤーのtop になっているか?将来については phase で判定\r
- obj.gpuParent = obj.gpuParent || false;\r
- obj.phase = duration === 0 ? 9 : obj.phase === 9 ? 9 : 0; //\r
- obj.wait = X.Type.isFinite( wait ) ? wait : 1000;\r
+ if( !obj.duration && 6 <= obj.phase ){\r
+ this[ 'stop' ](); // 現在値で停止\r
+ } else {\r
+ if( !obj.phase ){\r
+ list[ list.length ] = this;\r
+ obj.phase = 1;\r
+ obj.uid = ++X_NodeAnime_uid;\r
+ X_NodeAnime_needsDetection = true;\r
+ } else\r
+ if( obj.phase < 4 ){\r
+ list.splice( list.indexOf( this ), 1 );\r
+ list[ list.length ] = this;\r
+ obj.uid = ++X_NodeAnime_uid;\r
+ X_NodeAnime_needsDetection = true;\r
+ } else\r
+ if( obj.duration ){\r
+ // リストの先頭にいるため検査不要でアニメーション開始可能 4, 5, 6, 7\r
+ obj.phase = 6;\r
+ } else\r
+ // GPU 転送予約、または transform や opacity の値のみ設定\r
+ if( obj.phase !== 5 ){ // GPU解除待ち ではない -> 4. 6, 7\r
+ obj.phase = 4; // 強制停止(GPU転送予約)または値のみ更新\r
+ obj.releaseNow = false; // TODO folower がいるため GPU 転送できないケースあり\r
+ X_NodeAnime_needsDetection = true;\r
+ };\r
\r
- obj.startTime = X_Timer_now();\r
- obj.startX = ( start.x || start.x === 0 ) ? start.x : obj.x || 0;\r
- obj.startY = ( start.y || start.y === 0 ) ? start.y : obj.y || 0;\r
- obj.startA = 0 <= start.opacity && start.opacity <= 1 ? start.opacity : obj.a || 1;\r
- \r
- obj.destTime = obj.startTime + obj.duration;\r
- obj.destX = ( dest.x || dest.x === 0 ) ? dest.x : obj.destX || 0;\r
- obj.destY = ( dest.y || dest.y === 0 ) ? dest.y : obj.destY || 0;\r
- obj.destA = 0 <= dest.opacity && dest.opacity <= 1 ? dest.opacity : obj.destA || 1;\r
+ if( !X_NodeAnime_reserved ){\r
+ X_NodeAnime_reserved = true;\r
+ \r
+ if( X_Node_updateTimerID ){\r
+ if( X_NodeAnime_updateTimerID ) X_NodeAnime_updateTimerID = X_Timer_cancelFrame( X_NodeAnime_updateTimerID );\r
\r
- \r
- if( obj.gpuTimerID ){\r
- X.Timer.remove( obj.gpuTimerID );\r
- delete obj.gpuTimerID;\r
- };\r
- \r
- X_Node_Anime_needsDetection = true;\r
- if( X_Node_Anime_hasTransition ){\r
- X_Node_Anime_reserveUpdate( true );\r
- } else {\r
- X_Node_Anime_updateTimerID || ( X_Node_Anime_updateTimerID = X.Timer.requestFrame( X_Node_Anime_updateAnimationsNoTransition ) ); \r
+ X_System[ 'listen' ]( X_EVENT_UPDATED, X_NodeAnime_updateAnimations );\r
+ } else {\r
+ X_System[ 'unlisten' ]( X_EVENT_UPDATED, X_NodeAnime_updateAnimations );\r
+ X_NodeAnime_updateTimerID = X_Timer_requestFrame( X_NodeAnime_updateAnimations );\r
+ };\r
+ }; \r
};\r
\r
- X_Node_ANIMATIONS.indexOf( this ) === -1 &&\r
- ( X_Node_ANIMATIONS[ X_Node_ANIMATIONS.length ] = this );\r
- \r
- //console.log( 'animate ' + this._id + ' y:' + obj.startY + ' > ' + obj.destY + ' d:' + obj.duration );\r
- \r
return this;\r
};\r
\r
-Node.prototype.stop = function(){\r
- var obj = this._anime;\r
- if( !obj ) return;\r
- if( X_Node_Anime_hasTransition ){\r
- obj.phase = 100;\r
- X_Node_Anime_needsDetection = true;\r
- X_Node_Anime_reserveUpdate();\r
- } else {\r
- X_Node_ANIMATIONS.splice( X_Node_ANIMATIONS.indexOf( this ), 1 );\r
- obj.gpuTimerID && X.Timer.remove( obj.gpuTimerID );\r
- delete this._anime; \r
+function X_NodeAnime_getFinite( a, b ){\r
+ if( a || a === 0 ) return a;\r
+ if( b || b === 0 ) return b;\r
+ return NaN;\r
+};\r
+\r
+/*\r
+ * 1.アニメーション中の要素の停止 ->後続アニメーションの開始\r
+ * 2.アニメーション待機中の要素の停止 -> 後続アニメーションの再調査\r
+ */\r
+/**\r
+ * アニメーションの停止。\r
+ * @alias Node.prototype.stop\r
+ * @return {Node} メソッドチェーン\r
+ */\r
+function X_Node_stop( option ){\r
+ var obj = this[ '_anime' ],\r
+ list = X_NodeAnime_QUEUE,\r
+ i, rm, j, xnode, _obj;\r
+ \r
+ if( !obj || !obj.phase ) return this;\r
+\r
+ switch( obj.phase ){\r
+ case 6 : // アニメーション開始可能 ??\r
+ case 2 : // 準待機\r
+ case 3 : // アニメーション待機中\r
+ X_NodeAnime_needsDetection = true;\r
+ case 1 :\r
+ rm = true;\r
+\r
+ case 4 : // 強制停止(GPU転送予約)\r
+ case 7 : // アニメーション中\r
+ if( option & X_NODE_ANIME_RESET ){\r
+ obj.startX = obj.startY = obj.destX = obj.destY = obj.x = obj.y = 0;\r
+ obj.startA = obj.destA = obj.a = 1;\r
+ }; // TODO 終了値で停止も,,,\r
+ \r
+ // obj.canceled = true;\r
+ \r
+ if( rm ) break; // 1,2,3,6 の場合ここまで\r
+ \r
+ obj.destX = obj.x;\r
+ obj.destY = obj.y;\r
+ obj.destA = obj.a;\r
+\r
+ obj.phase = 4; // 強制解除\r
+ X_NodeAnime_needsDetection = true;\r
+ \r
+ case 5 : // GPU解除待ち\r
+ obj.releaseNow = !( option & X_NODE_ANIME_STAY_GPU );\r
+ break;\r
};\r
+\r
+ if( rm ){\r
+ list.splice( list.indexOf( this ), 1 );\r
+ obj.phase = 0; \r
+ };\r
+\r
return this;\r
};\r
+/*\r
+ * remove(append swap 等でない部的に呼ばれている場合も), kill 時に\r
+ */\r
+function X_NodeAnime_stopNow( xnode ){\r
+ var obj = xnode[ '_anime' ],\r
+ flags = xnode[ '_flags' ],\r
+ list = X_NodeAnime_QUEUE,\r
+ skipUpdate;\r
+ \r
+ // if( !obj || !obj.phase ) return; 呼び出し側で検証済\r
\r
-function X_Node_Anime_reserveUpdate( before ){\r
- if( !X_Node_Anime_reserved ){\r
- X_Node_Anime_reserved = true;\r
- \r
- if( X_Node_updateTimerID ){\r
- console.log( before ? '> BEFORE_UPDATE' : '> UPDATED' );\r
- X_System.listenOnce( before ? X.Event.BEFORE_UPDATE : X.Event.UPDATED, X_Node_Anime_updateAnimations );\r
- } else {\r
- console.log( '> Timer' );\r
- // Opera12 requestAnimationFrame では transition が動かない、、、\r
- X_Node_Anime_updateTimerID =\r
- X_UA.Opera ?\r
- X.Timer.once( 0, X_Node_Anime_updateAnimations ) :\r
- X.Timer.requestFrame( X_Node_Anime_updateAnimations ); \r
- };\r
+ X_NodeAnime_needsDetection = true;\r
+ list.splice( list.indexOf( xnode ), 1 );\r
+ obj.phase = 0;\r
+\r
+ // この部分 startUpdate へ?\r
+ if( flags & ~X_Node_BitMask_RESET_GPU ){\r
+ skipUpdate = flags & X_NodeFlags_GPU_RESERVED;\r
+ ( flags & X_NodeFlags_GPU_RELEASE_RESERVED ) || X_NodeAnime_updatePosition( xnode, obj.x, obj.y, obj.a, false );\r
+ skipUpdate || ( xnode[ '_rawObject' ].style.cssText = X_Node_CSS_objToCssText( xnode ) );\r
+ xnode[ '_flags' ] &= X_Node_BitMask_RESET_GPU;\r
};\r
};\r
\r
-function X_Node_Anime_updateAnimations( v ){\r
- var i = X_Node_ANIMATIONS.length, ret, c = false;\r
- \r
- console.log( v.type || v );\r
+/*\r
+ * 1. 新規アニメーションが現在アニメーション中の要素の親か子であればアニメーションを待機\r
+ */\r
+function X_NodeAnime_detectWaitAnimation( xnode, duration, isTest ){\r
+ var list = X_NodeAnime_QUEUE,\r
+ i = 0, _xnode;\r
\r
- //console.log( 'updateAnimations ' + i + ' ' + X_Node_Anime_needsDetection );\r
- \r
- if( X_Node_Anime_needsDetection ) X_Node_Anime_detectAnimationLayers();\r
- \r
- for( ; i; ){\r
- xnode = X_Node_ANIMATIONS[ --i ];\r
- //console.log( 'phase : ' + xnode._id + ' ' + xnode._anime.phase + ' ' + xnode._anime.duration );\r
- ret = X_Node_Anime_updateAnimation( xnode );\r
- if( ret === true ){\r
- X_Node_ANIMATIONS.splice( i, 1 );\r
- xnode._anime.gpuTimerID && X.Timer.remove( xnode._anime.gpuTimerID );\r
- delete xnode._anime;\r
- } else\r
- if( ret !== false ){\r
- c = true;\r
+ for( ; _xnode = list[ i ]; ++i ){\r
+ if( _xnode === xnode ) break;\r
+ \r
+ // アニメーションの優先度はリストにいる順\r
+ // まず先行する後続待機要素の中に、親子関係のものがいないか?探す\r
+ if( _xnode[ '_anime' ].phase <= 3 ){\r
+ if( xnode[ 'contains' ]( _xnode ) || _xnode[ 'contains' ]( xnode ) ){ // 祖先か?見た方が早そう\r
+ // -> いる、このような要素が複数いる場合、誰に後続すればいいか?判然としないため、準待機フラグを立てる\r
+ return 2;\r
+ };\r
};\r
};\r
- \r
- if( X_Node_Anime_updateTimerID && X_Node_updateTimerID ) X_Node_startUpdate();\r
- \r
- X_Node_Anime_updateTimerID = 0;\r
- X_Node_Anime_reserved = false;\r
- if( c ){\r
- X_Node_Anime_reserveUpdate();\r
+\r
+ // -> いない、アニメーション中(開始可能も)の要素の中に、親子関係のものがいないか?探す\r
+ // -> いる、待機状態へ\r
+ // -> いない、アニメーションが可能\r
+ for( i = 0; _xnode = list[ i ]; ++i ){\r
+ if( _xnode === xnode ) break;\r
+\r
+ if( 6 <= _xnode[ '_anime' ].phase ){\r
+ if( xnode[ 'contains' ]( _xnode ) || _xnode[ 'contains' ]( xnode ) ){\r
+ return isTest ? 3 : _xnode;\r
+ };\r
+ };\r
};\r
+ // アニメーション可能\r
+ return duration ? 6 : 4; // duration がない場合は、アニメーション強制停止へ進みそこから GPU 解除待ちへ\r
};\r
\r
-// TODO X.Timer.requestFrame 経由の BEFORE_UPDATE で更新を行う\r
-function X_Node_Anime_detectAnimationLayers(){\r
- var i = X_Node_ANIMATIONS.length,\r
- l = i,\r
- j, xnode, parent, hasGPUChild, remove;\r
-\r
- for( ; i; ){\r
- xnode = X_Node_ANIMATIONS[ --i ];\r
- parent = hasGPUChild = false;\r
- for( j = l; j; ){\r
- _xnode = X_Node_ANIMATIONS[ --j ];\r
+function X_NodeAnime_updateAnimations( e ){\r
+ var list = X_NodeAnime_QUEUE,\r
+ now = X_Timer_now(),\r
+ c = false,\r
+ i, xnode, obj, _xnode,\r
+ rm, easing, lazy;\r
+ \r
+ if( X_NodeAnime_needsDetection ){\r
+ X_NodeAnime_needsDetection = false;\r
+ \r
+ //\r
+ list.sort( X_NodeAnime_sortAnimationNode );\r
+ \r
+ for( i = 0; xnode = list[ i ]; ++i ){\r
+ obj = xnode[ '_anime' ];\r
\r
- if( xnode.parent === _xnode.parent ){\r
- continue;\r
- } else\r
- if( _xnode.contains( xnode ) ){\r
- if( ( _xnode._anime.phase === 3 && _xnode._anime.gpuParent ) || _xnode._anime.phase === 10 ){\r
- _xnode._anime.phase = 15;\r
- } else\r
- if( xnode._anime.gpuParent ){\r
- parent = true;\r
- xnode._anime.phase = xnode._anime.phase === 2 ? 6 : 15;// GPU レイヤーの解除 > アニメーションは継続, すでに終了フェイズなら破棄\r
- } else\r
- if( [ 7, 8, 9, 13, 14 ].indexOf( xnode._anime.phase ) !== -1 ){// GPU レイヤーの中止\r
- parent = true;\r
- xnode._anime.phase -= 8;\r
- };\r
- break;\r
- } else\r
- if( xnode.contains( _xnode ) ){\r
- if( ( xnode._anime.phase === 3 && xnode._anime.gpuParent ) || xnode._anime.phase === 10 ){\r
- xnode._anime.phase = 15;\r
- } else\r
- if( _xnode._anime.gpuParent ){\r
- hasGPUChild = true;\r
- _xnode._anime.phase = _xnode._anime.phase === 2 ? 6 : 15;// GPU レイヤーの解除 > アニメーションは継続, すでに終了フェイズなら破棄\r
- } else\r
- if( [ 7, 8, 9, 13, 14 ].indexOf( _xnode._anime.phase ) !== -1 ){// GPU レイヤーの中止\r
- hasGPUChild = true;\r
- _xnode._anime.phase -= 8;\r
+ if( obj.phase <= 3 ){\r
+ if( !X_Type_isNumber( obj.phase = _xnode = X_NodeAnime_detectWaitAnimation( xnode, obj.duration ) ) ){\r
+ _xnode[ '_anime' ].follower = true;\r
+ obj.phase = 3; // 後続待機\r
};\r
- break;\r
- };\r
- };\r
- if( !parent && xnode._anime.phase !== 15 ){\r
- if( xnode._anime.phase === 0 ){\r
- // 新規\r
- xnode._anime.phase = hasGPUChild ? 7 : 8;// 非GPU -> GPU 子に GPU アニメをもつなら、タイミングをずらす。\r
- } else\r
- if( [ 3, 4, 10, 100 ].indexOf( xnode._anime.phase ) === -1 ){\r
- // 非GPU -> GPU\r
- xnode._anime.phase = hasGPUChild ? 13 : 14;// 非GPU -> GPU 子に GPU アニメをもつなら、タイミングをずらす。\r
+ } else {\r
+ obj.follower = false;\r
};\r
};\r
};\r
\r
- X_Node_Anime_needsDetection = false;\r
-};\r
+ for( i = list.length; i; ){\r
+ rm = false;\r
+ xnode = list[ --i ];\r
+ obj = xnode[ '_anime' ];\r
\r
-function X_Node_Anime_updateAnimation( xnode ){\r
- var obj = xnode._anime,\r
- phase = obj.phase,\r
- current, time;\r
- switch( phase ){\r
- case -1 :// 子の GPU レイヤー解除待ち\r
- case 7 :\r
- ++obj.phase;\r
- break;\r
- case 0 : // 開始位置+アニメーションの設定 \r
- case 8 :\r
- X_Node_Anime_readyTransition( xnode );\r
- X_Node_Anime_updatePosition( xnode, obj.startX, obj.startY, obj.startA, phase === 8 );\r
- ++obj.phase;\r
- break;\r
- case 1 :\r
- case 9 : // 終了位置の設定\r
- obj.gpuParent = phase === 9;\r
- obj.duration ? X_Node_Anime_updateTransition( xnode ) : X_Node_Anime_clearTransition( xnode );\r
- X_Node_Anime_updatePosition( xnode, obj.destX, obj.destY, obj.destA, obj.gpuParent );\r
- obj.phase = 2;\r
- break;\r
- \r
- case 2 :\r
- // アニメーション中\r
- return false;\r
- \r
- case 3 : // アニメーションの解除\r
- X_Node_Anime_clearTransition( xnode );\r
- obj.phase = obj.gpuParent ? 10 : 4;\r
- break;\r
+ switch( obj.phase ){\r
+ case 7 : // アニメーション中\r
+ if( now < obj.destTime ){\r
+ easing = obj.progress = obj.easing.fn( ( now - obj.startTime ) / obj.duration );\r
+ X_NodeAnime_updatePosition( xnode, \r
+ obj.x = ( obj.destX - obj.startX ) * easing + obj.startX | 0,\r
+ obj.y = ( obj.destY - obj.startY ) * easing + obj.startY | 0,\r
+ obj.a = ( obj.destA - obj.startA ) * easing + obj.startA, true );\r
+ c = true;\r
+ break;\r
+ };\r
+ // アニメーション終了\r
+ xnode[ 'asyncDispatch' ]( X_EVENT_ANIME_END );\r
+ \r
+ case 4 : // 強制停止(GPU転送予約)\r
+ lazy = !obj.follower && !obj.releaseNow && obj.lazyRelease;\r
+ X_NodeAnime_updatePosition( xnode, obj.destX, obj.destY, obj.destA, !!lazy );\r
\r
- case 4 :\r
- // アニメーションは停止・GPU = false -> リストから削除\r
- obj.gpuParent = false;\r
- return true;\r
+ //if( obj.canceled ){\r
+ // xnode[ 'asyncDispatch' ]( X_EVENT_CANCELED );\r
+ //} else {\r
+ \r
+ //};\r
+ \r
+ if( lazy ){\r
+ console.log( 'アニメーション終了(' + obj.phase + ') -> GPU 解除待機 ' + lazy );\r
+ obj.releaseTime = now + lazy;\r
+ obj.phase = 5; // GPU解除待ち\r
+ c = true;\r
+ } else {\r
+ console.log( 'アニメーション終了(' + obj.phase + ') -> ' );\r
+ rm = true;\r
+ };\r
+ break;\r
\r
- case 10 :\r
- // アニメーションは停止・GPUレイヤーは解除していない(再アニメーションに備えて待機)\r
- if( !obj.gpuTimerID ){\r
- obj.gpuTimerID = X.Timer.once( obj.wait, xnode, X_Node_Anime_releaseGPULayer );\r
- };\r
- return false;\r
- \r
- case 5 :\r
- case 13 :\r
- // 子のGPU解除待ち\r
- ++obj.phase;\r
- break;\r
- \r
- // GPU レイヤーの変更> アニメーションは継続,但し残り時間が短ければ停止\r
- case 6 : \r
- case 14 :\r
- now = X_Timer_now();\r
- time = obj.duration - now + obj.startTime;\r
- if( time < 16 ){\r
- X_Node_Anime_clearTransition( xnode );\r
- X_Node_Anime_updatePosition( xnode, obj.destX, obj.destY, obj.destA, phase === 14 );\r
- obj.phase = phase === 14 ? 10 : 4;\r
- } else {\r
- current = X_Node_Anime_getComputedPosition( xnode );\r
- obj.startX = current.x;\r
- obj.startY = current.y;\r
- obj.startA = current.a;\r
- obj.duration = time;\r
+ case 6 : // アニメーション開始可能\r
obj.startTime = now;\r
- X_Node_Anime_updatePosition( xnode, current.x, current.y, current.a, phase === 14 );\r
- obj.phase = phase === 14 ? 9 : 1;\r
- };\r
- break;\r
- \r
- case 15 :\r
- // GPU有効で停止(待機)している xnode の解除\r
- console.log( 'GPU有効で停止(待機)している xnode の解除' + xnode._tag + xnode.getOrder() );\r
- X_Node_Anime_clearTransition( xnode );\r
- X_Node_Anime_updatePosition( xnode, obj.destX, obj.destY, obj.destA, false );\r
- obj.gpuTimerID && X.Timer.remove( obj.gpuTimerID );\r
- return true;\r
+ obj.destTime = now + obj.duration;\r
+ obj.phase = 7; // アニメーション中\r
+ obj.progress = 0; \r
+ xnode[ 'asyncDispatch' ]( X_EVENT_ANIME_START );\r
+ c = true;\r
+ //obj.canceled = false;\r
+ ( obj.inited && !X_NodeAnime_translateZ ) || X_NodeAnime_updatePosition( xnode, obj.startX, obj.startY, obj.startA, true );\r
+ break;\r
+ \r
+ case 5 : // GPU解除待ち\r
+ if( obj.releaseTime <= now || obj.follower || obj.releaseNow ){\r
+ X_NodeAnime_translateZ && X_NodeAnime_updatePosition( xnode, obj.destX, obj.destY, obj.destA, false );\r
+ rm = true;\r
+ } else {\r
+ c = true;\r
+ };\r
+ break;\r
+ \r
+ default : // 2 or 3\r
+ // 待機状態でも親要素が GPU 化していなければ、開始値をセットすることは可能\r
+ obj.inited || X_NodeAnime_updatePosition( xnode, obj.startX, obj.startY, obj.startA, false );\r
+ obj.inited = false;\r
+ break;\r
+ };\r
\r
- case 100 : // stop() : アニメーションの中断して削除\r
- console.log( 'stop() gpu:' + obj.gpuParent );\r
- current = X_Node_Anime_getComputedPosition( xnode );\r
- \r
- X_Node_Anime_clearTransition( xnode );\r
- X_Node_Anime_updatePosition( xnode, current.x, current.y, current.a, obj.gpuParent );\r
- obj.phase = obj.gpuParent ? 10 : 4;\r
- break;\r
+ obj.releaseNow = false;\r
\r
+ if( rm ){\r
+ X_NodeAnime_translateZ && xnode[ 'asyncDispatch' ]( X_EVENT_GPU_RELEASED );\r
+ // 後続のアニメーションがある場合\r
+ if( obj.follower ) X_NodeAnime_needsDetection = c = true;\r
+ list.splice( i, 1 );\r
+ obj.phase = 0;\r
+ };\r
};\r
-};\r
-\r
-function X_Node_Anime_getComputedPosition( that ) {\r
- var matrix = X_node_CSS_getComputedStyle( that._rawObject, null ),\r
- x, y;\r
-\r
- if ( X_Node_Anime_hasTransform ) {\r
- matrix = matrix[ X_Node_CSS_VENDER_PREFIX[ 'transform' ] ].split( ')' )[ 0 ].split( ', ' );\r
- x = + ( matrix[ 12 ] || matrix[ 4 ] );\r
- y = + ( matrix[ 13 ] || matrix[ 5 ] );\r
+ \r
+ //c && console.log( 'anime... ' + X_Node_updateTimerID );\r
+ \r
+ if( X_NodeAnime_reserved = c ){\r
+ if( X_Node_updateTimerID ){\r
+ // scrollbox では X_System X_EVENT_UPDATED は不可。。。\r
+ !e || e.type !== X_EVENT_UPDATED ?\r
+ X_System[ 'listen' ]( X_EVENT_UPDATED, X_NodeAnime_updateAnimations ) :\r
+ X_NodeAnime_updateTimerID && X_Timer_cancelFrame( X_NodeAnime_updateTimerID );\r
+ X_NodeAnime_updateTimerID = 0;\r
+ } else {\r
+ X_System[ 'unlisten' ]( X_EVENT_UPDATED, X_NodeAnime_updateAnimations );\r
+ X_NodeAnime_updateTimerID = X_Timer_requestFrame( X_NodeAnime_updateAnimations );\r
+ };\r
} else {\r
- x = + parseInt( matrix.left );\r
- y = + parseInt( matrix.top );\r
+ X_System[ 'unlisten' ]( X_EVENT_UPDATED, X_NodeAnime_updateAnimations );\r
+ X_NodeAnime_updateTimerID = 0;\r
};\r
- \r
- return { x: x, y: y, a : matrix[ X_Node_CSS_Support[ 'opacity' ] ] };\r
};\r
\r
-function X_Node_Anime_onTransitionEnd(){\r
- if( this._anime.phase !== 2 ) return X.Callback.PREVENT_DEFAULT;\r
- this._anime.phase = 3;\r
- X_Node_Anime_needsDetection = true;\r
- X_Node_Anime_reserveUpdate();\r
- console.log( 'トランジション終了' );\r
- return X.Callback.UN_LISTEN | X.Callback.PREVENT_DEFAULT | X.Callback.STOP_PROPAGATION;\r
-};\r
-\r
-function X_Node_Anime_releaseGPULayer(){\r
- var obj = this._anime;\r
- X_Node_Anime_updatePosition( this, obj.destX, obj.destY, obj.destA, false );\r
- X_Node_ANIMATIONS.splice( X_Node_ANIMATIONS.indexOf( this ), 1 );\r
- delete obj.gpuTimerID;\r
- delete this._anime;\r
- console.log( 'GPUレイヤーの破棄' );\r
-};\r
-\r
-function X_Node_Anime_readyTransition( xnode ){\r
- xnode.css({\r
- willChange : X_Node_Anime_transitionProps + ',opacity,width,height',\r
- backfaceVisibility : 'hidden',\r
- transitionTimingFunction : xnode._anime.easing.style,\r
- transitionDelay : '0s'\r
- });\r
-};\r
-\r
-function X_Node_Anime_updateTransition( xnode ){\r
- // 開始座標のセット(新規のみ)\r
- // アニメーション指定のセット(または解除)(対象のみ)\r
- // 目標座標のセット\r
- xnode.listenOnce( 'transitionend', X_Node_Anime_onTransitionEnd );\r
-\r
- xnode.css({\r
- transitionProperty : X_Node_Anime_transitionProps + ',opacity,width,height',\r
- transitionDuration : xnode._anime.duration + 'ms'\r
- });\r
-};\r
-\r
-function X_Node_Anime_clearTransition( xnode ){\r
- // 開始座標のセット(新規のみ)\r
- // アニメーション指定のセット(または解除)(対象のみ)\r
- // 目標座標のセット\r
- xnode.unlisten( 'transitionend', X_Node_Anime_onTransitionEnd );\r
-\r
- xnode.css({\r
- willChange : '',\r
- backfaceVisibility : '',\r
- transitionTimingFunction : '',\r
- transitionDelay : '',\r
- transitionDuration : ''\r
- });\r
+/*\r
+ * アニメーション開始、アニメーション中、強制停止(GPU転送予約)、GPU解除待ち の要素をリストの先頭に\r
+ */\r
+function X_NodeAnime_sortAnimationNode( xnode1, xnode2 ){\r
+ var a = 4 <= xnode1[ '_anime' ].phase,\r
+ b = 4 <= xnode2[ '_anime' ].phase;\r
+ \r
+ if( ( a && b ) && ( !a && !b ) ){ // Chrome のみ\r
+ return xnode1[ '_anime' ].uid - xnode2[ '_anime' ].uid;\r
+ };\r
+ return a ? -1 : 1;\r
};\r
\r
-function X_Node_Anime_updatePosition( xnode, x, y, opacity, useGPU ){\r
- if( X_Node_Anime_hasTransform ){\r
- xnode.css({\r
- transform : 'translate(' + ( x | 0 ) + 'px,' + ( y | 0 ) + 'px)' + ( useGPU ? X_Node_Anime_translateZ : '' ),\r
+function X_NodeAnime_updatePosition( xnode, x, y, opacity, useGPU ){\r
+ //console.log( 'updatePosition x:' + x + ' gpu:' + !!useGPU );\r
+ if( X_NodeAnime_hasTransform ){\r
+ xnode[ 'css' ]({\r
+ transform : 'translate(' + ( x | 0 ) + 'px,' + ( y | 0 ) + 'px)' + ( useGPU ? X_NodeAnime_translateZ : '' ),\r
opacity : opacity\r
});\r
} else {\r
- xnode.css({\r
- left : ( x | 0 ) + 'px',\r
- top : ( y | 0 ) + 'px',\r
- opacity : opacity\r
- });\r
+ x === x && xnode[ 'css' ]( 'left', ( x | 0 ) + 'px' );\r
+ y === y && xnode[ 'css' ]( 'top', ( y | 0 ) + 'px' );\r
+ opacity === opacity && xnode[ 'css' ]( 'opacity', opacity );\r
};\r
- // xnode._anime.x, y\r
-};\r
\r
-function X_Node_Anime_updateAnimationsNoTransition(){\r
- var i = X_Node_ANIMATIONS.length,\r
- now = X_Timer_now(),\r
- obj,\r
- newX, newY, newA, easing,\r
- c = false;\r
- \r
- for( ; i; ){\r
- xnode = X_Node_ANIMATIONS[ --i ];\r
- obj = xnode._anime;\r
-\r
- if( obj.destTime <= now ){\r
- X_Node_Anime_updatePosition( xnode, obj.destX, obj.destY, obj.destA, false );\r
- xnode.asyncDispatch( 'transitionend' );\r
- \r
- X_Node_ANIMATIONS.splice( i, 1 );\r
- delete xnode._anime; \r
+ if( X_NodeAnime_translateZ ){\r
+ if( useGPU ){\r
+ if( xnode[ '_flags' ] & X_NodeFlags_GPU_RELEASE_RESERVED ){\r
+ xnode[ '_flags' ] &= X_Node_BitMask_RESET_GPU;\r
+ xnode[ '_flags' ] |= X_NodeFlags_GPU_NOW;\r
+ } else\r
+ if( !( xnode[ '_flags' ] & X_NodeFlags_GPU_NOW ) ){\r
+ xnode[ '_flags' ] &= X_Node_BitMask_RESET_GPU;\r
+ xnode[ '_flags' ] |= X_NodeFlags_GPU_RESERVED;\r
+ };\r
} else {\r
- easing = obj.easing.fn( ( now - obj.startTime ) / obj.duration );\r
- newX = ( obj.destX - obj.startX ) * easing + obj.startX;\r
- newY = ( obj.destY - obj.startY ) * easing + obj.startY;\r
- newA = ( obj.destA - obj.startA ) * easing + obj.startA;\r
- X_Node_Anime_updatePosition( xnode, newX, newY, newA, false );\r
- obj.x = newX;\r
- obj.y = newY;\r
- obj.a = newA;\r
- c = true; \r
- };\r
- };\r
- \r
- if( c ){\r
- X_Node_Anime_updateTimerID = X.Timer.requestFrame( X_Node_Anime_updateAnimationsNoTransition );\r
- } else {\r
- X_Node_Anime_updateTimerID = 0;\r
+ if( xnode[ '_flags' ] & X_NodeFlags_GPU_NOW ){\r
+ xnode[ '_flags' ] &= X_Node_BitMask_RESET_GPU;\r
+ xnode[ '_flags' ] |= X_NodeFlags_GPU_RELEASE_RESERVED;\r
+ } else\r
+ if( xnode[ '_flags' ] & X_NodeFlags_GPU_RESERVED ){\r
+ xnode[ '_flags' ] &= X_Node_BitMask_RESET_GPU;\r
+ };\r
+ }; \r
};\r
};\r
\r
+\r
+var\r
+ X_NodeAnime_ease = {\r
+ 'quadratic' : {\r
+ style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',\r
+ fn: function (k) {\r
+ return k * ( 2 - k );\r
+ }\r
+ },\r
+ 'circular' : {\r
+ 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
+ fn: function (k) {\r
+ return Math.sqrt( 1 - ( --k * k ) );\r
+ }\r
+ },\r
+ 'back' : {\r
+ style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',\r
+ fn: function (k) {\r
+ var b = 4;\r
+ return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;\r
+ }\r
+ },\r
+ 'bounce' : {\r
+ style: '',\r
+ fn: function (k) {\r
+ if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {\r
+ return 7.5625 * k * k;\r
+ } else if ( k < ( 2 / 2.75 ) ) {\r
+ return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;\r
+ } else if ( k < ( 2.5 / 2.75 ) ) {\r
+ return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;\r
+ } else {\r
+ return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;\r
+ }\r
+ }\r
+ },\r
+ 'elastic' : {\r
+ style: '',\r
+ fn: function (k) {\r
+ var f = 0.22,\r
+ e = 0.4;\r
+\r
+ if ( k === 0 ) { return 0; }\r
+ if ( k == 1 ) { return 1; }\r
+\r
+ return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );\r
+ }\r
+ }\r
+ };\r