X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F20_ui%2F15_ScrollBox.js;h=e82a647c8036482834b8ca7d95c9cd54d1be6d35;hb=bafa8683f87b2f909d1301fca80684bf9ff221ed;hp=cd5e4f45fef0afb597acf7b0ec01f90811685ba7;hpb=5d1a420699bfb2a488fb7a33cb1f9d6ed3164ee4;p=pettanr%2FclientJs.git diff --git a/0.6.x/js/20_ui/15_ScrollBox.js b/0.6.x/js/20_ui/15_ScrollBox.js index cd5e4f4..e82a647 100644 --- a/0.6.x/js/20_ui/15_ScrollBox.js +++ b/0.6.x/js/20_ui/15_ScrollBox.js @@ -1,55 +1,107 @@ +/* + * scroll 要素は常にひとつ + * ScrollManager + * indicatorX, Y は再利用 + */ +var XUI_ScrollBox_useCSSP = !X_UA[ 'IE5' ], + XUI_ScrollBox_current, + XUI_ScrollBox_indicatorV, + XUI_ScrollBox_indicatorH; + +function XUI_ScrollBox_start( scrollBox ){ + // 既存スクロールの停止 + if( XUI_ScrollBox_current && XUI_ScrollBox_current !== scrollBox ){ + XUI_ScrollBox_indicatorV && + XUI_ScrollBox_current[ 'unlisten' ]( [ X_EVENT_CANCELED, XUI_Event.SCROLL_END ], XUI_ScrollBox_indicatorV, XUI_ScrollBox_indicatorHandleEvent ); + + XUI_ScrollBox_indicatorH && + XUI_ScrollBox_current[ 'unlisten' ]( [ X_EVENT_CANCELED, XUI_Event.SCROLL_END ], XUI_ScrollBox_indicatorH, XUI_ScrollBox_indicatorHandleEvent ); + }; - -function X_UI_ScrollBox_momentum( current, start, time, lowerMargin, wrapperSize, deceleration ){ - var distance = current - start, - speed = Math.abs( distance ) / time, - destination, - duration; - - deceleration = deceleration === undefined ? 0.0006 : deceleration; - - destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 ); - duration = speed / deceleration; - - if( destination < lowerMargin ){ - destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin; - distance = Math.abs( destination - current ); - duration = distance / speed; + if( scrollBox && scrollBox.hasVScroll ){ + if( !XUI_ScrollBox_indicatorV ){ + XUI_ScrollBox_indicatorV = X_Doc_create( 'div' )[ 'className' ]( 'ScrollBox-IndicatorV' ); + }; + if( scrollBox.xnode !== XUI_ScrollBox_indicatorV.parent ){ + console.log( '*** Scroll Indicator add ***' ); + scrollBox.xnode[ 'append' ]( XUI_ScrollBox_indicatorV ); + XUI_ScrollBox_indicatorV[ 'animate' ]({ + 'from' : { opacity : 0 }, + 'to' : { opacity : 0.5 }, + 'duration' : 900, + 'easing' : 'circular', + 'lazyRelease' : 300 + }); + scrollBox + [ 'listen' ]( [ X_EVENT_CANCELED, XUI_Event.SCROLL_END ], XUI_ScrollBox_indicatorV, XUI_ScrollBox_indicatorHandleEvent ); + }; } else - if ( destination > 0 ) { - destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0; - distance = Math.abs( current ) + destination; - duration = distance / speed; + if( XUI_ScrollBox_indicatorV ){ + console.log( '*** Scroll Indicator remove ***' ); + XUI_ScrollBox_indicatorV[ 'remove' ](); + }; + + if( scrollBox && scrollBox.hasHScroll ){ + if( !XUI_ScrollBox_indicatorH ){ + XUI_ScrollBox_indicatorH = X_Doc_create( 'div' )[ 'className' ]( 'ScrollBox-IndicatorH' ); + }; + if( scrollBox.xnode !== XUI_ScrollBox_indicatorH.parent ){ + scrollBox.xnode[ 'append' ]( XUI_ScrollBox_indicatorH ); + XUI_ScrollBox_indicatorH[ 'animate' ]({ + 'from' : { opacity : 0 }, + 'to' : { opacity : 0.5 }, + 'duration' : 900, + 'easing' : 'circular', + 'lazyRelease' : 300 + }); + scrollBox + [ 'listen' ]( [ X_EVENT_CANCELED, XUI_Event.SCROLL_END ], XUI_ScrollBox_indicatorH, XUI_ScrollBox_indicatorHandleEvent ); + }; + } else + if( XUI_ScrollBox_indicatorH ){ + XUI_ScrollBox_indicatorH[ 'remove' ](); }; + + XUI_ScrollBox_current = scrollBox; +}; - return { - destination : Math.round( destination ), - duration : duration +function XUI_ScrollBox_indicatorHandleEvent( e ){ + //if( !XUI_ScrollBox_useCSSP ) return; + switch( e.type ){ + case X_EVENT_CANCELED : + case XUI_Event.SCROLL_END : + console.log( '-fadeout-' ); + this[ 'animate' ]({ + 'from' : { opacity : 0.5 }, + 'to' : { opacity : 0 }, + 'duration' : 900, + 'easing' : 'circular', + 'lazyRelease' : 300 + }); + break; }; }; + var X_UI_ScrollBox_SUPPORT_ATTRS = { // スクロール開始するために必要な移動距離、縦か横、どちらか制限する場合、より重要 - directionLockThreshold : [ 10, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.LENGTH ], - scrollXEnabled : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ], - scrollYEnabled : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ], - scrollEnabled : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ], - bounceEnabled : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ], - bounceTime : [ 600, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.TIME ], - useWheel : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ], - useKey : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ], - hasScrollShadow : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ], - scrollShadowColor : [ '#000', X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.COLOR ] + directionLockThreshold : [ 10, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.LENGTH ], + scrollXEnabled : [ true, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.BOOLEAN ], + scrollYEnabled : [ true, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.BOOLEAN ], + scrollEnabled : [ true, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.BOOLEAN ], + bounceEnabled : [ true, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.BOOLEAN ], + bounceTime : [ 300, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.TIME ], + useWheel : [ true, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.BOOLEAN ], + useKey : [ true, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.BOOLEAN ], + hasScrollShadow : [ true, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.BOOLEAN ], + scrollShadowColor : [ '#000', XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.COLOR ] }; - -X.UI._ScrollBox = X.UI._ChromeBox.inherits( +var XUI_ScrollBox = XUI_ChromeBox.inherits( '_ScrollBox', - X_Class.PRIVATE_DATA | X_Class.SUPER_ACCESS, + X_Class.NONE, { - //elmScroll : null, - //elmScroller : null, - //elmScrollbar : null, + layout : XUI_Layout_Canvas, directionLockThreshold : 10, scrollXEnabled : true, @@ -63,8 +115,6 @@ X.UI._ScrollBox = X.UI._ChromeBox.inherits( hasScrollShadow : true, scrollShadowColor : '#000', - supportAttrs : XUI_Attr_createAttrDef( X.UI.Attr.Support, X_UI_ScrollBox_SUPPORT_ATTRS ), - scrolling : false, initiated : '', @@ -72,7 +122,6 @@ X.UI._ScrollBox = X.UI._ChromeBox.inherits( directionLocked : '', startTime : 0, endTime : 0, - isAnimating : false, isInTransition : false, hasHScroll : false, @@ -86,8 +135,10 @@ X.UI._ScrollBox = X.UI._ChromeBox.inherits( scrollX : 0, // px scrollY : 0, // px - maxScrollX : 0, // px - maxScrollY : 0, // px + scrollXMax : 0, // px + scrollYMax : 0, // px + scrollXRatio : 0, // この値は scroll 不要になっても保持される。 scroll 必要時に参照される + scrollYRatio : 0, startX : 0, // px startY : 0, // px absStartX : 0, // px @@ -96,29 +147,26 @@ X.UI._ScrollBox = X.UI._ChromeBox.inherits( pointY : 0, // px distX : 0, // px distY : 0, // px - directionX : 0, // px - directionY : 0, // px - scrollXPercent : 0, // この値は scroll 不要になっても保持される。 scroll 必要時に参照される - scrollYPercent : 0, + directionX : 0, // -1, 0, 1 + directionY : 0, // -1, 0, 1 lastScrollWidth : 0, lastScrollHeight : 0, lastBoxWidth : 0, lastBoxHeight : 0, - _containerNode : null, - scrollManager : null, + _containerNode : null, + xnodeSlider : null, - Constructor : function( layout, args ){ - this[ 'Super' ]( layout, args ); - this._containerNode = X_Class_getPrivate( this.containerNode ); - this._containerXNode = this._containerNode.xnode[ 'className' ]( 'ScrollSlider' ).listen( X_EVENT_ANIME_END, this, X_UI_ScrollBox_onAnimeEnd ); + Constructor : function( user, layout, args ){ + this[ 'Super' ]( user, layout, args ); + this._containerNode = X_Pair_get( this.containerNode ); + this.xnodeSlider = this._containerNode.xnode[ 'className' ]( 'ScrollSlider' )[ 'listen' ]( X_EVENT_ANIME_END, this, X_UI_ScrollBox_onAnimeEnd ); this.xnode[ 'className' ]( 'ScrollBox' ); }, creationComplete : function(){ - X.UI._Box.prototype.creationComplete.apply( this, arguments ); - this.scrollManager; + XUI_Box.prototype.creationComplete.apply( this, arguments ); }, calculate : function(){ @@ -127,7 +175,7 @@ X.UI._ScrollBox = X.UI._ChromeBox.inherits( this.lastBoxWidth = this.boxWidth; this.lastBoxHeight = this.boxHeight; - X.UI._Box.prototype.calculate.apply( this, arguments ); + XUI_Box.prototype.calculate.apply( this, arguments ); // TODO root の layout_complete 後に。 // TODO calculate 前に scroll の解放。 @@ -137,7 +185,8 @@ X.UI._ScrollBox = X.UI._ChromeBox.inherits( this.lastScrollHeight !== this._containerNode.boxHeight || this.lastBoxWidth !== this.boxWidth || this.lastBoxHeight !== this.boxHeight ){ - X_UI_rootData[ 'listenOnce' ]( X.UI.Event.LAYOUT_COMPLETE, this, X_UI_ScrollBox_onLayoutComplete ); + console.log( 'scroll - calc' ); + XUI_rootData[ 'listenOnce' ]( XUI_Event.LAYOUT_COMPLETE, this, X_UI_ScrollBox_onLayoutComplete ); }; }, @@ -154,9 +203,23 @@ X.UI._ScrollBox = X.UI._ChromeBox.inherits( }, _remove : function(){ - X.UI._AbstractUINode.prototype._remove.apply( this, arguments ); + XUI_AbstractUINode.prototype._remove.apply( this, arguments ); + if( this.scrolling ){ - // remove scroll + // scroller 削除 + this[ 'unlisten' ]( XUI_Event._POINTER_DOWN, X_UI_ScrollBox_onStart ) + [ 'unlisten' ]( XUI_Event.DRAG, X_UI_ScrollBox_onMove ) + [ 'unlisten' ]( XUI_Event.DRAG_END, X_UI_ScrollBox_onEnd ); + XUI_rootData[ 'unlisten' ]( XUI_Event.LAYOUT_BEFORE, this, X_UI_ScrollBox_onLayoutBefore ); + + XUI_rootData[ 'unlisten' ]( XUI_Event.LAYOUT_COMPLETE, this, X_UI_ScrollBox_onLayoutComplete ); + this[ 'unlisten' ]( XUI_Event.SCROLL_END, XUI_rootData, XUI_rootData.calculate ); + + XUI_ScrollBox_useCSSP ? this.xnodeSlider[ 'stop' ]() : this.xnode[ 'stop' ](); + + XUI_ScrollBox_current === this && XUI_ScrollBox_start( null ); + + this.scrolling = false; }; } @@ -164,100 +227,167 @@ X.UI._ScrollBox = X.UI._ChromeBox.inherits( ); function X_UI_ScrollBox_onLayoutBefore( e ){ - //this._containerXNode.stop(); + if( e[ 'cancelable' ] && this.isInTransition && X_NodeAnime_translateZ ){ + this[ 'listenOnce' ]( XUI_Event.SCROLL_END, XUI_rootData, XUI_rootData.calculate ); + return X_CALLBACK_PREVENT_DEFAULT; + }; + this.scrollXRatio = this.scrollX / this.scrollXMax; + this.scrollYRatio = this.scrollY / this.scrollYMax; + XUI_ScrollBox_useCSSP ? this.xnodeSlider[ 'stop' ]() : this.xnode[ 'stop' ](); + this.isInTransition = false; + return X_CALLBACK_NONE; }; function X_UI_ScrollBox_onLayoutComplete( e ){ // scroll の停止、GPU の解除 - var font = this.fontSize = this._containerXNode.call( 'fontSize' ); - - this.maxScrollX = ( this.boxWidth - this._containerNode.boxWidth ) * font | 0; - this.maxScrollY = ( this.boxHeight - this._containerNode.boxHeight ) * font | 0; + var font = this.fontSize = this.xnodeSlider[ 'call' ]( 'fontSize' ); + + this.scrollXMax = ( this.boxWidth - this._containerNode.boxWidth ) * font | 0; + this.scrollYMax = ( this.boxHeight - this._containerNode.boxHeight ) * font | 0; - this.hasHScroll = this.scrollXEnabled && this.maxScrollX < 0; - this.hasVScroll = this.scrollYEnabled && this.maxScrollY < 0; + this.hasHScroll = this.scrollXEnabled && ( this.scrollXMax < -1 ); // < 0 だと + this.hasVScroll = this.scrollYEnabled && ( this.scrollYMax < -1 ); if( !this.hasHScroll ){ - this.maxScrollX = 0; - //this.scrollWidth = this.boxWidth; + this.scrollXMax = 0; }; if( !this.hasVScroll ){ - this.maxScrollY = 0; - //this.scrollHeight = this.boxHeight; + this.scrollYMax = 0; }; delete this.endTime; delete this.directionX; delete this.directionY; - //this.wrapperOffset = this.xnodeWrapper[ 'offset' ](); - - //this[ 'dispatch' ]('refresh'); - X_UI_ScrollBox_resetPosition( this, 0 ); if( this.hasHScroll || this.hasVScroll ){ // scroll が必要。 if( this.scrolling ){ - this.scrollTo( this.maxScrollX * this.scrollXPercent, this.maxScrollY * this.scrollYPercent, 100 ); + X_UI_ScrollBox_translate( this, this.scrollXMax * this.scrollXRatio, this.scrollYMax * this.scrollYRatio, 100, '', 300 ); } else { // scroller 作る - this[ 'listen' ]( X.UI.Event._POINTER_DOWN, this, X_UI_ScrollBox_onStart ); - X_UI_rootData[ 'listen' ]( X.UI.Event.LAYOUT_BEFORE, this, X_UI_ScrollBox_onLayoutBefore ); + this[ 'listen' ]( XUI_Event._POINTER_DOWN, X_UI_ScrollBox_onStart ) + [ 'listen' ]( XUI_Event.DRAG, X_UI_ScrollBox_onMove ) + [ 'listen' ]( XUI_Event.DRAG_END, X_UI_ScrollBox_onEnd ); + XUI_rootData[ 'listen' ]( XUI_Event.LAYOUT_BEFORE, this, X_UI_ScrollBox_onLayoutBefore ); - this.scrollTo( this.maxScrollX * this.scrollXPercent, this.maxScrollY * this.scrollYPercent ); + X_UI_ScrollBox_translate( this, this.scrollXMax * this.scrollXRatio, this.scrollYMax * this.scrollYRatio, 100, '', 300 ); this.scrolling = true; }; } else // scroll 不要 if( this.scrolling ){ // scroller 削除 - this[ 'unlisten' ]( X.UI.Event._POINTER_DOWN, this, X_UI_ScrollBox_onStart ); - X_UI_rootData[ 'unlisten' ]( X.UI.Event.LAYOUT_BEFORE, this, X_UI_ScrollBox_onLayoutBefore ); + this[ 'unlisten' ]( XUI_Event._POINTER_DOWN, X_UI_ScrollBox_onStart ) + [ 'unlisten' ]( XUI_Event.DRAG, X_UI_ScrollBox_onMove ) + [ 'unlisten' ]( XUI_Event.DRAG_END, X_UI_ScrollBox_onEnd ); + XUI_rootData[ 'unlisten' ]( XUI_Event.LAYOUT_BEFORE, this, X_UI_ScrollBox_onLayoutBefore ); - ( this.scrollX !== 0 || this.scrollY !== 0 ) && this.scrollTo( 0, 0 ); + ( this.scrollX !== 0 || this.scrollY !== 0 ) && X_UI_ScrollBox_translate( this, 0, 0, 100, '', 300 ); delete this.scrolling; + delete this.scrollXRatio; + delete this.scrollYRatio; }; }; - function X_UI_ScrollBox_translate( that, x, y, opt_time, opt_easing, opt_release ){ - - opt_time = 0 <= opt_time ? opt_time : 0; - opt_easing = opt_easing === '' ? '' : opt_easing || 'circular'; - opt_release = 0 <= opt_release ? opt_release : 300; - - that._containerXNode.animate( - { - x : that.scrollX, - y : that.scrollY - }, - { - x : x | 0, - y : y | 0 - }, - opt_time, opt_easing, opt_release - ); - - that.scrollX = x | 0; - that.scrollY = y | 0; - - if( that.indicators ){ - for( i = that.indicators.length; i--; ){ - that.indicators[ i ].updatePosition(); - }; - }; - }; +// TODO use scrollLeft, scrollTop +function X_UI_ScrollBox_translate( that, x, y, opt_time, opt_easing, opt_release ){ + var scrollBoxH = that.fontSize * that.boxHeight, + scrollBoxW = that.fontSize * that.boxWidth, + indicatorH, indicatorW; + + opt_time = 0 <= opt_time ? opt_time : 0; + opt_easing = opt_easing === '' ? '' : opt_easing || 'circular'; + opt_release = 0 <= opt_release ? opt_release : 300; + + console.log( 'scr ' + y ); + + if( !XUI_ScrollBox_useCSSP ){ + that.xnode[ 'animate' ]({ + 'from' : { + scrollX : -that.scrollX, + scrollY : -that.scrollY + }, + 'to' : { + scrollX : -x, + scrollY : -y + }, + 'duration' : opt_time, + 'easing' : opt_easing + }); + } else { + that.xnodeSlider[ 'animate' ]({ + 'from' : { + x : that.scrollX, + y : that.scrollY + }, + 'to' : { + x : x, + y : y + }, + 'duration' : opt_time, + 'easing' : opt_easing, + 'lazyRelease' : opt_release + }); + }; -function X_UI_ScrollBox_onStart( e ){ - var ret = X_Callback_NONE; + if( X_UA[ 'IE' ] < 6 ){ + XUI_ScrollBox_indicatorV && XUI_ScrollBox_indicatorV[ 'css' ]( 'left', ( scrollBoxW - that.fontSize * 0.6 | 0 ) + 'px' ); + XUI_ScrollBox_indicatorH && XUI_ScrollBox_indicatorH[ 'css' ]( 'top' , ( scrollBoxH - that.fontSize * 0.6 | 0 ) + 'px' ); + }; - // React to left mouse button only - if( e.pointerType === 'mouse' && e.button !== 0 ){ - return ret; + if( that.hasVScroll && XUI_ScrollBox_indicatorV && XUI_ScrollBox_indicatorV.parent === that.xnode ){ + indicatorH = scrollBoxH * scrollBoxH / ( - that.scrollYMax + scrollBoxH ); + scrollBoxH -= indicatorH; + + XUI_ScrollBox_indicatorV + [ 'css' ]({ + height : ( indicatorH | 0 ) + 'px' + }) + [ 'animate' ]({ + 'from' : { + y : scrollBoxH * that.scrollY / that.scrollYMax }, + 'to' : { + y : scrollBoxH * y / that.scrollYMax, + opacity : 0.5 + }, + 'duration' : opt_time, + 'easing' : opt_easing, + 'lazyRelease' : opt_release + }); + }; + if( that.hasHScroll && XUI_ScrollBox_indicatorH && XUI_ScrollBox_indicatorH.parent === that.xnode ){ + indicatorW = scrollBoxW * scrollBoxW / ( - that.scrollXMax + scrollBoxW ); + scrollBoxW -= indicatorW; + XUI_ScrollBox_indicatorH + [ 'css' ]({ + width : ( indicatorW | 0 ) + 'px' + }) + [ 'animate' ]({ + 'from' : { + x : scrollBoxW * that.scrollX / that.scrollXMax }, + 'to' : { + x : scrollBoxW * x / that.scrollXMax, + opacity : 0.5 + }, + 'duration' : opt_time, + 'easing' : opt_easing, + 'lazyRelease' : opt_release + }); }; + that.scrollX = x; + that.scrollXEm = x / that.fontSize; + that.scrollY = y; + that.scrollYEm = y / that.fontSize; +}; + +function X_UI_ScrollBox_onStart( e ){ + var ret = X_CALLBACK_NONE; + if( !this.scrollEnabled || ( this.initiated && e.pointerType !== this.initiated ) ){ return ret; }; @@ -272,10 +402,13 @@ function X_UI_ScrollBox_onStart( e ){ this.startTime = X_Timer_now(); // スクロール中の停止 - if( this.isInTransition || this.isAnimating ){ - this.isInTransition = this.isAnimating = false; - this[ 'dispatch' ]( X.UI.Event.SCROLL_END ); - }; + if( this.isInTransition ){ + this.isInTransition = false; + //console.log( '-1-' ); + this[ 'dispatch' ]( XUI_Event.SCROLL_END ); + // TODO current位置 + XUI_ScrollBox_useCSSP ? this.xnodeSlider[ 'stop' ]() : this.xnode[ 'stop' ](); + }; this.startX = this.scrollX; this.startY = this.scrollY; @@ -284,25 +417,26 @@ function X_UI_ScrollBox_onStart( e ){ this.pointX = e.pageX; this.pointY = e.pageY; - this[ 'listen' ]( X.UI.Event._POINTER_MOVE, this, X_UI_ScrollBox_onMove ); - this[ 'listen' ]( [ X.UI.Event._POINTER_UP, X.UI.Event._POINTER_CANCEL ], this, X_UI_ScrollBox_onEnd ); + console.log( 'scrollstart ' + e.pageY + e.target.className() ); - return ret | X_Callback_PREVENT_DEFAULT; + return ret | X_CALLBACK_PREVENT_DEFAULT; }; function X_UI_ScrollBox_onMove( e ){ - var ret = X_Callback_NONE, + var ret = X_CALLBACK_NONE, deltaX, deltaY, timestamp, newX, newY, absDistX, absDistY; // 規定以上の move でスクロール開始 - + +//console.log( 'scrollmove ' + e.buttons + ' ' + e.button ); + if( !this.scrollEnabled || e.pointerType !== this.initiated ){ return ret; }; // gpu の用意 - if( !this._containerXNode[ '_anime' ] ){ + if( !XUI_ScrollBox_useCSSP ? ( !this.xnode[ '_anime' ] || !this.xnode[ '_anime' ].phase ) : ( !this.xnodeSlider[ '_anime' ] || !this.xnodeSlider[ '_anime' ].phase ) ){ //console.log( 'gpuレイヤーの用意 ' + e.pageY ); //console.log( 'mov1 x:' + this.scrollX + ' y:' + this.scrollY ); X_UI_ScrollBox_translate( this, this.scrollX, this.scrollY, 0, '', 300 ); @@ -349,31 +483,33 @@ function X_UI_ScrollBox_onMove( e ){ deltaY = this.hasVScroll ? deltaY : 0; if( !this.moved ){ - this[ 'dispatch' ]( X.UI.Event.SCROLL_BEFORE_MOVE ); + this[ 'dispatch' ]( XUI_Event.SCROLL_BEFORE_MOVE ); this.moved = true; this.minusX = deltaX; this.minusY = deltaY; + // indicator + XUI_ScrollBox_start( this ); } else { - this[ 'dispatch' ]( X.UI.Event.SCROLL_MOVE ); + this[ 'dispatch' ]( XUI_Event.SCROLL_MOVE ); }; newX = this.scrollX + deltaX;// - this.minusX; newY = this.scrollY + deltaY;// - this.minusY; // Slow down if outside of the boundaries - if( 0 < newX || newX < this.maxScrollX ){ - newX = this.bounceEnabled ? this.scrollX + ( deltaX ) / 3 : 0 < newX ? 0 : this.maxScrollX; + if( 0 < newX || newX < this.scrollXMax ){ + newX = this.bounceEnabled ? this.scrollX + ( deltaX ) / 3 : 0 < newX ? 0 : this.scrollXMax; }; - if( 0 < newY || newY < this.maxScrollY ){ - //console.log( 'slow... ' + newY + ' ' + this.maxScrollY ); - newY = this.bounceEnabled ? this.scrollY + ( deltaY ) / 3 : 0 < newY ? 0 : this.maxScrollY; + if( 0 < newY || newY < this.scrollYMax ){ + //console.log( 'slow... ' + newY + ' ' + this.scrollYMax ); + newY = this.bounceEnabled ? this.scrollY + ( deltaY ) / 3 : 0 < newY ? 0 : this.scrollYMax; }; this.directionX = 0 < deltaX ? -1 : deltaX < 0 ? 1 : 0; this.directionY = 0 < deltaY ? -1 : deltaY < 0 ? 1 : 0; - //console.log( 'mov2 x:' + newX + ' y:' + newY ); + console.log( 'mov2 x:' + newX + ' y:' + newY ); X_UI_ScrollBox_translate( this, newX, newY, 0, '', 300 ); if( 300 < timestamp - this.startTime ){ @@ -382,21 +518,21 @@ function X_UI_ScrollBox_onMove( e ){ this.startY = this.scrollY; }; // イベントの拘束 - return ret | X_Callback_PREVENT_DEFAULT | X_Callback_MONOPOLY; + return ret | X_CALLBACK_PREVENT_DEFAULT | X_CALLBACK_CAPTURE_POINTER; }; function X_UI_ScrollBox_onEnd( e ){ - var ret = X_Callback_NONE, + var ret = X_CALLBACK_NONE, time = 0, easing = '', newX, newY, momentumX, momentumY, duration, distanceX, distanceY; - - this[ 'unlisten' ]( X.UI.Event._POINTER_MOVE, this, X_UI_ScrollBox_onMove ); - this[ 'unlisten' ]( [ X.UI.Event._POINTER_UP, X.UI.Event._POINTER_CANCEL ], this, X_UI_ScrollBox_onEnd ); + + //console.log( e.type + ' onend ' + XUI_Event.POINTER_OUT ); if( !this.scrollEnabled || e.pointerType !== this.initiated ){ + //console.log( e.type + ' onend 1 ' + e.pointerType + ' ' + this.initiated ); return ret; }; @@ -412,49 +548,49 @@ function X_UI_ScrollBox_onEnd( e ){ // reset if we are outside of the boundaries if( X_UI_ScrollBox_resetPosition( this, this.bounceTime ) ){ - return ret; + //console.log( e.type + ' onend 2 ' + XUI_Event.POINTER_OUT ); + return ret | X_CALLBACK_PREVENT_DEFAULT | X_CALLBACK_RELEASE_POINTER; }; // we scrolled less than 10 pixels if( !this.moved ){ - // this[ 'dispatch' ]( X_EVENT_CANCELED ); - console.log( 'we scrolled less than 10 pixels' ); - return ret; + this[ 'dispatch' ]( X_EVENT_CANCELED ); + //console.log( 'we scrolled less than 10 pixels ' + e.pageY ); + return ret | X_CALLBACK_PREVENT_DEFAULT | X_CALLBACK_RELEASE_POINTER; }; // start momentum animation if needed if( this.momentumEnabled && duration < 300 ){ momentumX = this.hasHScroll ? - X_UI_ScrollBox_momentum( this.scrollX, this.startX, duration, this.maxScrollX, this.bounceEnabled ? this.boxWidth * this.fontSize : 0, this.deceleration ) : + X_UI_ScrollBox_momentum( this.scrollX, this.startX, duration, this.scrollXMax, this.bounceEnabled ? this.boxWidth * this.fontSize : 0, this.deceleration ) : { destination: newX, duration: 0 }; momentumY = this.hasVScroll ? - X_UI_ScrollBox_momentum( this.scrollY, this.startY, duration, this.maxScrollY, this.bounceEnabled ? this.boxHeight * this.fontSize : 0, this.deceleration ) : + X_UI_ScrollBox_momentum( this.scrollY, this.startY, duration, this.scrollYMax, this.bounceEnabled ? this.boxHeight * this.fontSize : 0, this.deceleration ) : { destination: newY, duration: 0 }; newX = momentumX.destination; newY = momentumY.destination; time = Math.max( momentumX.duration, momentumY.duration ) | 0; this.isInTransition = true; } else { - console.log( '慣性無し' ); + //console.log( '慣性無し' ); }; if( newX != this.scrollX || newY != this.scrollY ){ // change easing function when scroller goes out of the boundaries - if( 0 < newX || newX < this.maxScrollX || 0 < newY || newY < this.maxScrollY ){ + if( 0 < newX || newX < this.scrollXMax || 0 < newY || newY < this.scrollYMax ){ easing = 'quadratic'; }; - console.log( 'end2 x:' + newX + ' y:' + newY + ' t:' + time ); + //console.log( 'end2 x:' + newX + ' y:' + newY + '<-y:' + this.scrollY + ' t:' + time ); this.scrollTo( newX, newY, time, easing, 1000 ); - return ret; + } else { + //console.log( 'end1 x:' + newX + ' y:' + newY ); + this.scrollTo( newX, newY, 0, '', 1000 ); // ensures that the last position is rounded + //console.log( '-3-' ); + this[ 'dispatch' ]( XUI_Event.SCROLL_END ); }; - console.log( 'end1 x:' + newX + ' y:' + newY ); - this.scrollTo( newX, newY, 0, '', 1000 ); // ensures that the last position is rounded - - this[ 'dispatch' ]( X.UI.Event.SCROLL_END ); - - return ret; + return ret | X_CALLBACK_PREVENT_DEFAULT | X_CALLBACK_RELEASE_POINTER; }; function X_UI_ScrollBox_resetPosition( that, time ){ @@ -466,82 +602,120 @@ function X_UI_ScrollBox_resetPosition( that, time ){ if( !that.hasHScroll || 0 < that.scrollX ){ x = 0; } else - if( that.scrollX < that.maxScrollX ){ - x = that.maxScrollX; + if( that.scrollX < that.scrollXMax ){ + x = that.scrollXMax; }; if( !that.hasVScroll || 0 < that.scrollY ){ y = 0; } else - if( that.scrollY < that.maxScrollY ){ - y = that.maxScrollY; + if( that.scrollY < that.scrollYMax ){ + y = that.scrollYMax; }; if( x === that.scrollX && y === that.scrollY ){ - console.log( 'no バウンド y:' + y + ' max:' + that.maxScrollY ); + //console.log( 'no バウンド y:' + y + ' max:' + that.scrollYMax ); return false; }; - //console.log( 'バウンド!' ); - //console.log( 'rese x:' + x + ' y:' + y ); + //console.log( ' ===> resetPosition - バウンド!' ); + //console.log( ' x:' + x + ' y:' + y ); that.scrollTo( x, y, time, that.bounceEasing, 1000 ); return true; }; function X_UI_ScrollBox_onAnimeEnd( e ){ - if( e.target !== this._containerXNode || !this.isInTransition ){ - return X_Callback_NONE; - }; - if( !X_UI_ScrollBox_resetPosition( this, this.bounceTime ) ){ + if( this.isInTransition && !X_UI_ScrollBox_resetPosition( this, this.bounceTime ) ){ this.isInTransition = false; - this.dispatch( X.UI.Event.SCROLL_END ); + //console.log( '-2-' ); + this[ 'dispatch' ]( XUI_Event.SCROLL_END ); + }; + //console.log(' -2.1- '+this.isInTransition ); + return X_CALLBACK_NONE; +}; + +function X_UI_ScrollBox_momentum( current, start, time, lowerMargin, wrapperSize, deceleration ){ + var distance = current - start, + speed = Math.abs( distance ) / time, + destination, + duration; + + deceleration = deceleration === undefined ? 0.0006 : deceleration; + + destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 ); + duration = speed / deceleration; + + if( destination < lowerMargin ){ + destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin; + distance = Math.abs( destination - current ); + duration = distance / speed; + } else + if ( destination > 0 ) { + destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0; + distance = Math.abs( current ) + destination; + duration = distance / speed; + }; + + return { + destination : Math.round( destination ), + duration : duration }; - return X_Callback_NONE; }; X.UI.ScrollBox = X.UI.ChromeBox.inherits( 'ScrollBox', - X_Class.SUPER_ACCESS, - X.UI._ScrollBox, + X_Class.NONE, { Constructor : function(){ + var supports, slider; + + if( XUI_ScrollBox.prototype.usableAttrs === XUI_ChromeBox.prototype.usableAttrs ){ + XUI_ScrollBox.prototype.usableAttrs = supports = XUI_Attr_createAttrDef( XUI_Attr_Support, X_UI_ScrollBox_SUPPORT_ATTRS ); + + XUI_ScrollBox.prototype.attrClass = XUI_Attr_preset( XUI_Box.prototype.attrClass, supports, { width : '100%', height : '100%', bgColor : 0x111111 } ); + }; + var args = [ - 0, // layout + XUI_Layout_Vertical, { - name : 'ScrollBox-Scroller', - role : 'container', - width : 'auto', - minWidth : '100%', - height : 'auto', - minHeight : '100%' + name : 'ScrollBox-Scroller', + role : 'container', + width : 'auto', + minWidth : '100%', + height : 'auto', + minHeight : '100%' } ], - i = arguments.length, - arg, layout; + l = arguments.length, i = 0, j = 1, + arg, attr; - for( ; i; ){ - arg = arguments[ --i ]; - if( arg[ 'instanceOf' ] && arg[ 'instanceOf' ]( X.UI.Layout.Base ) ){ - layout = arg; - } else { - args[ args.length ] = arg; + for( ; i < l; ++i ){ + arg = arguments[ i ]; + if( arg[ 'instanceOf' ] && arg[ 'instanceOf' ]( XUI_LayoutBase ) ){ + args[ 0 ] = arg; + } else + if( arg[ 'instanceOf' ] && arg[ 'instanceOf' ]( X.UI.AbstractUINode ) ){ + args[ ++j ] = arg; + } else + if( X_Type_isObject( arg ) ){ + args[ ++j ] = attr = arg; + slider = attr.scrollSlider; }; }; - args[ 0 ] = X.UI.Layout.Vertical; - - X_Class_newPrivate( + X_Pair_create( this, - X.UI.Layout.Canvas, - [ - { - width : '100%', - height : '100%' - }, - X.UI.VBox.apply( 0, args ) - ] + XUI_ScrollBox( + this, + null, + [ + slider || X.UI.VBox.apply( 0, args ) + ] + ) ); + + //attr && this.attr( attr ); }, scrollX : function(){ @@ -559,4 +733,5 @@ X.UI.ScrollBox = X.UI.ChromeBox.inherits( } } -); \ No newline at end of file +); +