--- /dev/null
+\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
+\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
+ \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_updateTimerID = 0,\r
+ X_Node_Anime_needsDetection = false,\r
+ X_Node_Anime_hasTransform = !!X_Node_CSS_VENDER_PREFIX[ 'transform' ],\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
+\r
+Node.prototype.animate = function( start, dest, duration, easing, wait ){\r
+ var obj = this._anime || ( this._anime = {} );\r
+ \r
+ obj.duration = X.Type.isFinite( duration ) && 0 <= 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 : 0; //\r
+ obj.wait = X.Type.isFinite( wait ) ? wait : 1000;\r
+ \r
+ obj.startTime = X_Timer_now();\r
+ obj.startX = start.x || 0;\r
+ obj.startY = start.y || 0;\r
+ obj.startA = 0 <= start.opacity && start.opacity <= 1 ? start.opacity : 1;\r
+ \r
+ obj.destTime = obj.startTime + obj.duration;\r
+ obj.destX = dest.x || 0;\r
+ obj.destY = dest.y || 0;\r
+ obj.destA = 0 <= dest.opacity && dest.opacity <= 1 ? dest.opacity : 1;\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();\r
+ } else {\r
+ X_Node_Anime_updateTimerID || ( X_Node_Anime_updateTimerID = X.Timer.requestFrame( X_Node_Anime_updateAnimationsNoTransition ) ); \r
+ };\r
+\r
+ X_Node_ANIMATIONS.indexOf( this ) === -1 &&\r
+ ( X_Node_ANIMATIONS[ X_Node_ANIMATIONS.length ] = this );\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 = 99;\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
+ };\r
+ return this;\r
+};\r
+\r
+function X_Node_Anime_reserveUpdate(){\r
+ if( !X_Node_Anime_updateTimerID ){\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
+};\r
+\r
+function X_Node_Anime_updateAnimations(){\r
+ var i = X_Node_ANIMATIONS.length, ret, c = false;\r
+ \r
+ if( X_Node_Anime_needsDetection ) X_Node_Anime_detectAnimationlayers();\r
+ \r
+ for( ; i; ){\r
+ xnode = X_Node_ANIMATIONS[ --i ];\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
+ };\r
+ };\r
+ \r
+ X_Node_Anime_updateTimerID = 0;\r
+ if( c ){\r
+ X_Node_Anime_reserveUpdate();\r
+ };\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, changed, remove;\r
+\r
+ for( ; i; ){\r
+ xnode = X_Node_ANIMATIONS[ --i ];\r
+ parent = hasGPUChild = false;\r
+ \r
+ for( j = l; j; ){\r
+ _xnode = X_Node_ANIMATIONS[ --j ];\r
+ \r
+ if( xnode.parent === _xnode.parent ){\r
+ continue;\r
+ } else\r
+ if( _xnode.contains( xnode ) ){\r
+ if( _xnode._anime.phase === 3 || _xnode._anime.phase === 10 ){\r
+ _xnode._anime.phase = 15;\r
+ } else\r
+ if( xnode._anime.gpuParent ){\r
+ changed = 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
+ changed = 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.phase === 10 ){\r
+ xnode._anime.phase = 15;\r
+ } else\r
+ if( _xnode._anime.gpuParent ){\r
+ changed = 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
+ changed = hasGPUChild = true;\r
+ _xnode._anime.phase -= 8;\r
+ };\r
+ break;\r
+ };\r
+ };\r
+ if( !parent && xnode._anime.phase !== 15 ){\r
+ if( xnode._anime.phase === 0 ){\r
+ // 新規\r
+ changed = changed || !xnode._anime.gpuParent;\r
+ xnode._anime.phase = hasGPUChild ? 7 : 8;// 非GPU -> GPU 子に GPU アニメをもつなら、タイミングをずらす。\r
+ } else {\r
+ // 非GPU -> GPU\r
+ changed = changed || !xnode._anime.gpuParent;\r
+ xnode._anime.phase = hasGPUChild ? 13 : 14;// 非GPU -> GPU 子に GPU アニメをもつなら、タイミングをずらす。\r
+ };\r
+ };\r
+ };\r
+ \r
+ X_Node_Anime_needsDetection = false;\r
+};\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_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
+ X_Node_Anime_updateTransition( xnode, obj.duration, obj.easing );\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
+ break;\r
+ \r
+ case 3 : // アニメーションの解除\r
+ X_Node_Anime_updateTransition( xnode, 0 );\r
+ obj.phase = obj.gpuParent ? 10 : 4;\r
+ break;\r
+\r
+ case 4 :\r
+ // アニメーションは停止・GPU = false -> リストから削除\r
+ obj.gpuParent = false;\r
+ return true;\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_updateTransition( xnode, 0 );\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
+ obj.startTime = now;\r
+ //X_Node_Anime_updateTransition( xnode, time, obj.easing );\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_updateTransition( xnode, 0 );\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
+ \r
+ case 99 : // stop() : アニメーションの中断して削除\r
+ current = X_Node_Anime_getComputedPosition( xnode );\r
+ \r
+ X_Node_Anime_updateTransition( xnode, 0 );\r
+ X_Node_Anime_updatePosition( xnode, current.x, current.y, current.a, obj.gpuParent );\r
+ obj.phase = obj.gpuParent ? 10 : 4;\r
+ break;\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
+ } else {\r
+ x = + parseInt( matrix.left );\r
+ y = + parseInt( matrix.top );\r
+ };\r
+ \r
+ return { x: x, y: y, a : matrix[ X_Node_CSS_Support[ 'opacity' ] ] };\r
+};\r
+\r
+function X_Node_Anime_onTransitionEnd(){\r
+ this._anime.phase = 3;\r
+ X_Node_Anime_needsDetection = true;\r
+ X_Node_Anime_reserveUpdate();\r
+\r
+ return X.Callback.UN_LISTEN | X.Callback.PREVENT_DEFAULT;\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_updateTransition( xnode, time, easing ){\r
+ // 開始座標のセット(新規のみ)\r
+ // アニメーション指定のセット(または解除)(対象のみ)\r
+ // 目標座標のセット\r
+ if( time ){\r
+ xnode.listenOnce( 'transitionend', X_Node_Anime_onTransitionEnd );\r
+ } else {\r
+ xnode.unlisten( 'transitionend', X_Node_Anime_onTransitionEnd );\r
+ };\r
+ xnode.css({\r
+ transitionProperty : X_Node_Anime_transitionProps + ',opacity',\r
+ willChange : time ? X_Node_Anime_transitionProps + ',opacity' : '',\r
+ backfaceVisibility : time ? 'hidden' : '',\r
+ transitionTimingFunction : time ? easing.style : '',\r
+ transitionDelay : '0s',\r
+ transitionDuration : time ? time + 'ms' : ''\r
+ });\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
+ opacity : opacity\r
+ });\r
+ } else {\r
+ xnode.css({\r
+ left : ( x | 0 ) + 'px',\r
+ top : ( y | 0 ) + 'px',\r
+ opacity : opacity\r
+ });\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
+ } 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
+ \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
+ };\r
+};\r
+\r