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=03de673a8f96a1a7b7271eeb82ce845e0d6fea13;hpb=83a329e3210a40f383282389a48a33ea34ccfa1f;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 03de673..e82a647 100644 --- a/0.6.x/js/20_ui/15_ScrollBox.js +++ b/0.6.x/js/20_ui/15_ScrollBox.js @@ -1,76 +1,129 @@ +/* + * 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 ], - scrollX : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ], - scrollY : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ], - enabled : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ], - bounce : [ 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 ], + 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, - supportAttrs : X.UI.Attr.createAttrDef( X.UI.Attr.Support, X_UI_ScrollBox_SUPPORT_ATTRS ), + directionLockThreshold : 10, + scrollXEnabled : true, + scrollYEnabled : true, + scrollEnabled : true, + bounceEnabled : true, + momentumEnabled : true, + bounceTime : 600, + useWheel : true, + useKey : true, + hasScrollShadow : true, + scrollShadowColor : '#000', - scrolling : false, + scrolling : false, initiated : '', moved : false, - distX : 0, - distY : 0, - directionX : 0, - directionY : 0, directionLocked : '', startTime : 0, endTime : 0, - isAnimating : false, - startX : 0, - startY : 0, - absStartX : 0, - absStartY : 0, - pointX : 0, - pointY : 0, - maxScrollX : 0, - maxScrollY : 0, + isInTransition : false, + hasHScroll : false, hasVScroll : false, @@ -78,135 +131,264 @@ X.UI._ScrollBox = X.UI._ChromeBox.inherits( wheelTimeout : 0, requestFrameID : 0, - _scrollX : 0, - _scrollY : 0, - scrollXPercent : 0, - scrollYPercent : 0, + fontSize : 0, + + scrollX : 0, // px + scrollY : 0, // px + scrollXMax : 0, // px + scrollYMax : 0, // px + scrollXRatio : 0, // この値は scroll 不要になっても保持される。 scroll 必要時に参照される + scrollYRatio : 0, + startX : 0, // px + startY : 0, // px + absStartX : 0, // px + absStartY : 0, // px + pointX : 0, // px + pointY : 0, // px + distX : 0, // px + distY : 0, // px + 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 ); + 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; - this._check(); + XUI_Box.prototype.creationComplete.apply( this, arguments ); }, calculate : function(){ - this.lastScrollWidth = this.scrollWidth; - this.lastScrollHeight = this.scrollHeight; + this.lastScrollWidth = this._containerNode.boxWidth; + this.lastScrollHeight = this._containerNode.boxHeight; 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 の解放。 if( - this.lastScrollWidth !== this.scrollWidth || this.lastScrollHeight !== this.scrollHeight || + this.lastScrollWidth !== this._containerNode.boxWidth || + this.lastScrollHeight !== this._containerNode.boxHeight || this.lastBoxWidth !== this.boxWidth || this.lastBoxHeight !== this.boxHeight ){ - // scroll の停止、GPU の解除 - this._check(); + console.log( 'scroll - calc' ); + XUI_rootData[ 'listenOnce' ]( XUI_Event.LAYOUT_COMPLETE, this, X_UI_ScrollBox_onLayoutComplete ); }; - - }, - - scrollBy : function( x, y, opt_time, opt_easing ){ - this.scrollTo( this.x + x, this.y + y, opt_time, opt_easing ); }, - scrollTo : function( x, y, opt_time, opt_easing ){ - opt_time = 0 <= opt_time ? opt_time : 0; - opt_easing = opt_easing || 'circular'; - - this.isInTransition = !!opt_time; - - this.containerNode.animate( - { - x : this.x, - y : this.y - }, - { - x : x, - y : y - }, - opt_time, opt_easing, 1000 - ); + scrollTo : function( x, y, opt_time, opt_easing, opt_release ){ + //if( this.scrollX === x && this.scrollY === y ) return; + + opt_time = 0 <= opt_time ? opt_time : 0; + opt_easing = opt_easing || 'circular'; + opt_release = 0 <= opt_release ? opt_release : 300; - this.x = x; - this.y = y; + this.isInTransition = 0 < opt_time; - if( this.indicators ){ - for( i = this.indicators.length; i--; ){ - this.indicators[ i ].updatePosition( opt_time, opt_easing ); - }; - }; + X_UI_ScrollBox_translate( this, x, y, opt_time, opt_easing, opt_release ); }, - _check : function(){ - var needVScroll, needHScroll; - if( this.boxWidth < this._containerNode.scrollWidth || this.boxHeight < this._containerNode.scrollHeight ){ - // scroll - if( this.scrolling ){ - // fix scroll position from scrollXPercent, scrollYPercent - // - - } else { - // create scroller - - - this[ 'listen' ]( X.UI.Event.POINTER_START, X_UI_ScrollBox_onStart ); - - - this._move( 0, 0 ); - - this.scrolling = true; - }; - } else - // no scroll + _remove : function(){ + XUI_AbstractUINode.prototype._remove.apply( this, arguments ); + if( this.scrolling ){ - // remove scroller - this[ 'unlisten' ]( X.UI.Event.POINTER_START ); + // 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 ); - ( this._scrollX !== 0 || this._scrollY !== 0 ) && this._move( 0, 0 ); + XUI_rootData[ 'unlisten' ]( XUI_Event.LAYOUT_COMPLETE, this, X_UI_ScrollBox_onLayoutComplete ); + this[ 'unlisten' ]( XUI_Event.SCROLL_END, XUI_rootData, XUI_rootData.calculate ); - delete this.scrolling; - }; - }, - - _move : function( x, y ){ - - }, - - _remove : function(){ - X.UI._AbstractUINode.prototype._remove.apply( this, arguments ); - if( this.scrolling ){ - // remove scroll + XUI_ScrollBox_useCSSP ? this.xnodeSlider[ 'stop' ]() : this.xnode[ 'stop' ](); + + XUI_ScrollBox_current === this && XUI_ScrollBox_start( null ); + + this.scrolling = false; }; } } ); +function X_UI_ScrollBox_onLayoutBefore( e ){ + 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_onStart( e ){ - var ret = X_Callback_NONE; - - // React to left mouse button only - if( e.pointerType === 'mouse' && e.button !== 0 ){ - return ret; +function X_UI_ScrollBox_onLayoutComplete( e ){ + // scroll の停止、GPU の解除 + 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.scrollXMax < -1 ); // < 0 だと + this.hasVScroll = this.scrollYEnabled && ( this.scrollYMax < -1 ); + + if( !this.hasHScroll ){ + this.scrollXMax = 0; }; - if( !this.enabled || ( this.initiated && e.pointerType !== this.initiated ) ){ + if( !this.hasVScroll ){ + this.scrollYMax = 0; + }; + + delete this.endTime; + delete this.directionX; + delete this.directionY; + + X_UI_ScrollBox_resetPosition( this, 0 ); + + if( this.hasHScroll || this.hasVScroll ){ + // scroll が必要。 + if( this.scrolling ){ + X_UI_ScrollBox_translate( this, this.scrollXMax * this.scrollXRatio, this.scrollYMax * this.scrollYRatio, 100, '', 300 ); + } else { + // scroller 作る + 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 ); + + 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' ]( 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 ) && X_UI_ScrollBox_translate( this, 0, 0, 100, '', 300 ); + + delete this.scrolling; + delete this.scrollXRatio; + delete this.scrollYRatio; + }; +}; + +// 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 + }); + }; + + 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' ); + }; + + 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; }; @@ -220,40 +402,44 @@ function X_UI_ScrollBox_onStart( e ){ this.startTime = X_Timer_now(); // スクロール中の停止 - if( this.isAnimating ){ - delete this.isAnimating; - this[ 'dispatch' ]( X.UI.Event.SCROLL_END ); - }; - - this.startX = this.x; - this.startY = this.y; - this.absStartX = this.x; - this.absStartY = this.y; + 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; + this.absStartX = this.scrollX; + this.absStartY = this.scrollY; this.pointX = e.pageX; this.pointY = e.pageY; - this[ 'listen' ]( X.UI.Event.POINTER_MOVE, X_UI_ScrollBox_onMove ); - this[ 'listen' ]( X.UI.Event.POINTER_END , X_UI_ScrollBox_onEnd ); + console.log( 'scrollstart ' + e.pageY + e.target.className() ); - //console.log( 'start : 3' ); - 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 でスクロール開始 - - if( !this.enabled || e.pointerType !== this.initiated ){ + +//console.log( 'scrollmove ' + e.buttons + ' ' + e.button ); + + if( !this.scrollEnabled || e.pointerType !== this.initiated ){ return ret; }; // gpu の用意 - if( !this.containerNode[ '_anime' ] ){ - console.log( 'gpuレイヤーの用意' ); - this._translate( this.x, this.y ); + 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 ); return ret; }; @@ -297,51 +483,56 @@ 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.x + deltaX;// - this.minusX; - newY = this.y + deltaY;// - this.minusY; + 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.bounce ? this.x + ( 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 ){ - newY = this.bounce ? this.y + ( 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; - this._translate( newX, newY ); + console.log( 'mov2 x:' + newX + ' y:' + newY ); + X_UI_ScrollBox_translate( this, newX, newY, 0, '', 300 ); if( 300 < timestamp - this.startTime ){ this.startTime = timestamp; - this.startX = this.x; - this.startY = this.y; + this.startX = this.scrollX; + 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, X_UI_ScrollBox_onMove ); - this[ 'unlisten' ]( X.UI.Event.POINTER_END, X_UI_ScrollBox_onEnd ); - if( !this.enabled || e.pointerType !== this.initiated ){ + //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; }; @@ -350,169 +541,181 @@ function X_UI_ScrollBox_onEnd( e ){ this.endTime = X_Timer_now(); duration = this.endTime - this.startTime; - newX = Math.round( this.x ); - newY = Math.round( this.y ); + newX = Math.round( this.scrollX ); + newY = Math.round( this.scrollY ); distanceX = Math.abs(newX - this.startX); distanceY = Math.abs(newY - this.startY); // reset if we are outside of the boundaries - if( X_UI_ScrollBox_resetPosition( this, this.options.bounceTime ) ){ - return ret; + if( X_UI_ScrollBox_resetPosition( this, this.bounceTime ) ){ + //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 ); - 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; }; - - this.scrollTo( newX, newY, 0 ); // ensures that the last position is rounded // start momentum animation if needed - if( this.options.momentum && duration < 300 ){ + if( this.momentumEnabled && duration < 300 ){ momentumX = this.hasHScroll ? - X_UI_ScrollBox_momentum( this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0, this.options.deceleration ) :{ destination: newX, duration: 0 }; + 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.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0, this.options.deceleration ) : { destination: newY, duration: 0 }; + 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 ); - this.isInTransition = 1; + time = Math.max( momentumX.duration, momentumY.duration ) | 0; + this.isInTransition = true; + } else { + //console.log( '慣性無し' ); }; - if( newX != this.x || newY != this.y ){ + 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'; }; - this.scrollTo( newX, newY, time, easing ); - return ret; + //console.log( 'end2 x:' + newX + ' y:' + newY + '<-y:' + this.scrollY + ' t:' + time ); + this.scrollTo( newX, newY, time, easing, 1000 ); + } 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 ); }; - 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 ){ - var x = this.x, - y = this.y; + var x = that.scrollX, + y = that.scrollY; time = time || 0; - if( !this.hasHScroll || 0 < this.x ){ + if( !that.hasHScroll || 0 < that.scrollX ){ x = 0; } else - if( this.x < this.maxScrollX ){ - x = this.maxScrollX; + if( that.scrollX < that.scrollXMax ){ + x = that.scrollXMax; }; - if( !this.hasVScroll || 0 < this.y ){ + if( !that.hasVScroll || 0 < that.scrollY ){ y = 0; } else - if( this.y < this.maxScrollY ){ - y = this.maxScrollY; + if( that.scrollY < that.scrollYMax ){ + y = that.scrollYMax; }; - if( x === this.x && y === this.y ){ - console.log( 'no バウンド' ); + if( x === that.scrollX && y === that.scrollY ){ + //console.log( 'no バウンド y:' + y + ' max:' + that.scrollYMax ); return false; }; - console.log( 'バウンド!' ); - this.scrollTo( x, y, time, this.options.bounceEasing ); + //console.log( ' ===> resetPosition - バウンド!' ); + //console.log( ' x:' + x + ' y:' + y ); + that.scrollTo( x, y, time, that.bounceEasing, 1000 ); return true; }; -function X_UI_ScrollBox_translate( x, y ){ - this.containerNode.animate( - { - x : this.x, - y : this.y - }, - { - x : x, - y : y - }, - 0, '', 300 - ); - - this.x = x; - this.y = y; - - if( this.indicators ){ - for( i = this.indicators.length; i--; ){ - this.indicators[ i ].updatePosition(); - }; +function X_UI_ScrollBox_onAnimeEnd( e ){ + if( this.isInTransition && !X_UI_ScrollBox_resetPosition( this, this.bounceTime ) ){ + this.isInTransition = false; + //console.log( '-2-' ); + this[ 'dispatch' ]( XUI_Event.SCROLL_END ); }; + //console.log(' -2.1- '+this.isInTransition ); + return X_CALLBACK_NONE; }; -function X_UI_ScrollBox_refresh( remove ){ - this.maxScrollX = this.boxWidth - this.containerNode.boxWidth; - this.maxScrollY = this.boxHeight - this.containerNode.boxHeight; +function X_UI_ScrollBox_momentum( current, start, time, lowerMargin, wrapperSize, deceleration ){ + var distance = current - start, + speed = Math.abs( distance ) / time, + destination, + duration; - this.hasHScroll = this.User[ 'attr' ]( 'scrollX' ) && this.maxScrollX < 0; - this.hasVScroll = this.User[ 'attr' ]( 'scrollY' ) && this.maxScrollY < 0; + deceleration = deceleration === undefined ? 0.0006 : deceleration; - if( !this.hasHScroll ){ - this.maxScrollX = 0; - this.scrollerWidth = this.wrapperWidth; - }; + destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 ); + duration = speed / deceleration; - if( !this.hasVScroll ){ - this.maxScrollY = 0; - this.scrollerHeight = this.wrapperHeight; + 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; }; - delete this.endTime; - delete this.directionX; - delete this.directionY; - - this.wrapperOffset = this.xnodeWrapper[ 'offset' ](); - - //this[ 'dispatch' ]('refresh'); - - X_UI_ScrollBox_resetPosition( this, 0 ); + return { + destination : Math.round( destination ), + duration : duration + }; }; X.UI.ScrollBox = X.UI.ChromeBox.inherits( 'ScrollBox', - X_Class.SUPER_ACCESS, - X.UI._ScrollBox, + X_Class.NONE, { Constructor : function(){ - var args = [], - i = arguments.length, - arg, layout; + 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 = [ + XUI_Layout_Vertical, + { + name : 'ScrollBox-Scroller', + role : 'container', + width : 'auto', + minWidth : '100%', + height : 'auto', + minHeight : '100%' + } + ], + 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; }; }; - /* - this.style = DisplayNodeStyle( this, - X_Class_newPrivate( + + X_Pair_create( + this, + XUI_ScrollBox( this, - X.UI.Layout.Canvas, + null, [ - Box( - layout || VerticalLayoutManager, - { - name : 'ScrollBox-Scroller', - role : 'container' - }, - args - ) + slider || X.UI.VBox.apply( 0, args ) ] ) ); - this.style.addName( 'ScrollBox' ); */ + + //attr && this.attr( attr ); }, scrollX : function(){ @@ -530,4 +733,5 @@ X.UI.ScrollBox = X.UI.ChromeBox.inherits( } } -); \ No newline at end of file +); +