1 /**************************************************************************** 2 Copyright (c) 2011-2012 cocos2d-x.org 3 Copyright (c) 2013-2014 Chukong Technologies Inc. 4 5 http://www.cocos2d-x.org 6 7 Permission is hereby granted, free of charge, to any person obtaining a copy 8 of this software and associated documentation files (the "Software"), to deal 9 in the Software without restriction, including without limitation the rights 10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 copies of the Software, and to permit persons to whom the Software is 12 furnished to do so, subject to the following conditions: 13 14 The above copyright notice and this permission notice shall be included in 15 all copies or substantial portions of the Software. 16 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 THE SOFTWARE. 24 ****************************************************************************/ 25 26 /** 27 * The ScrollView control of Cocos UI 28 * @class 29 * @extends ccui.Layout 30 * 31 * @property {Number} innerWidth - Inner container width of the scroll view 32 * @property {Number} innerHeight - Inner container height of the scroll view 33 * @property {ccui.ScrollView.DIR_NONE | ccui.ScrollView.DIR_VERTICAL | ccui.ScrollView.DIR_HORIZONTAL | ccui.ScrollView.DIR_BOTH} direction - Scroll direction of the scroll view 34 * @property {Boolean} bounceEnabled - Indicate whether bounce is enabled 35 * @property {Boolean} inertiaScrollEnabled - Indicate whether inertiaScroll is enabled 36 * @property {Number} touchTotalTimeThreshold - Touch total time threshold 37 */ 38 ccui.ScrollView = ccui.Layout.extend(/** @lends ccui.ScrollView# */{ 39 _innerContainer: null, 40 _direction: null, 41 42 _topBoundary: 0, 43 _bottomBoundary: 0, 44 _leftBoundary: 0, 45 _rightBoundary: 0, 46 47 _touchMoveDisplacements: null, 48 _touchMoveTimeDeltas: null, 49 _touchMovePreviousTimestamp: 0, 50 _touchTotalTimeThreshold : 0.5, 51 52 _autoScrolling: false, 53 _autoScrollTargetDelta: null, 54 _autoScrollAttenuate: true, 55 _autoScrollStartPosition : null, 56 _autoScrollTotalTime: 0, 57 _autoScrollAccumulatedTime: 0, 58 _autoScrollCurrentlyOutOfBoundary: false, 59 _autoScrollBraking: false, 60 _autoScrollBrakingStartPosition: null, 61 62 _bePressed: false, 63 64 _childFocusCancelOffset: 0, 65 66 bounceEnabled: false, 67 68 _outOfBoundaryAmount: null, 69 _outOfBoundaryAmountDirty: true, 70 71 inertiaScrollEnabled: false, 72 73 _scrollBarEnabled: true, 74 _verticalScrollBar: null, 75 _horizontalScrollBar: null, 76 77 _scrollViewEventListener: null, 78 _scrollViewEventSelector: null, 79 _className: "ScrollView", 80 81 /** 82 * Allocates and initializes a UIScrollView. 83 * Constructor of ccui.ScrollView. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. 84 * @example 85 * // example 86 * var uiScrollView = new ccui.ScrollView(); 87 */ 88 ctor: function () { 89 ccui.Layout.prototype.ctor.call(this); 90 this.setClippingEnabled(true); 91 this._innerContainer.setTouchEnabled(false); 92 93 this._direction = ccui.ScrollView.DIR_NONE; 94 95 this._childFocusCancelOffset = 5; 96 this.inertiaScrollEnabled = true; 97 98 this._outOfBoundaryAmount = cc.p(0, 0); 99 this._autoScrollTargetDelta = cc.p(0, 0); 100 this._autoScrollStartPosition = cc.p(0, 0); 101 this._autoScrollBrakingStartPosition = cc.p(0, 0); 102 this._touchMoveDisplacements = []; 103 this._touchMoveTimeDeltas = []; 104 this._touchMovePreviousTimestamp = 0; 105 106 this._scrollBarEnabled = true; 107 this._initScrollBar(); 108 109 this.setTouchEnabled(true); 110 }, 111 112 /** 113 * Initializes a ccui.ScrollView. Please do not call this function by yourself, you should pass the parameters to constructor to initialize it. 114 * @returns {boolean} 115 */ 116 init: function () { 117 if (ccui.Layout.prototype.init.call(this)) { 118 119 120 return true; 121 } 122 return false; 123 }, 124 125 /** 126 * Calls the parent class' onEnter and schedules update function. 127 * @override 128 */ 129 onEnter: function () { 130 ccui.Layout.prototype.onEnter.call(this); 131 this.scheduleUpdate(); 132 }, 133 134 onExit: function () { 135 cc.renderer._removeCache(this.__instanceId); 136 ccui.Layout.prototype.onExit.call(this); 137 }, 138 139 /** 140 * When a widget is in a layout, you could call this method to get the next focused widget within a specified _direction. <br/> 141 * If the widget is not in a layout, it will return itself 142 * 143 * @param {Number} _direction the _direction to look for the next focused widget in a layout 144 * @param {ccui.Widget} current the current focused widget 145 * @returns {ccui.Widget} 146 */ 147 findNextFocusedWidget: function(direction, current){ 148 if (this.getLayoutType() === ccui.Layout.LINEAR_VERTICAL 149 || this.getLayoutType() === ccui.Layout.LINEAR_HORIZONTAL) { 150 return this._innerContainer.findNextFocusedWidget(direction, current); 151 } else 152 return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, current); 153 }, 154 155 _initRenderer: function () { 156 ccui.Layout.prototype._initRenderer.call(this); 157 158 this._innerContainer = new ccui.Layout(); 159 this._innerContainer.setColor(cc.color(255,255,255)); 160 this._innerContainer.setOpacity(255); 161 this._innerContainer.setCascadeColorEnabled(true); 162 this._innerContainer.setCascadeOpacityEnabled(true); 163 164 this.addProtectedChild(this._innerContainer, 1, 1); 165 }, 166 167 _createRenderCmd: function(){ 168 if(cc._renderType === cc.game.RENDER_TYPE_WEBGL) 169 return new ccui.ScrollView.WebGLRenderCmd(this); 170 else 171 return new ccui.ScrollView.CanvasRenderCmd(this); 172 }, 173 174 _onSizeChanged: function () { 175 ccui.Layout.prototype._onSizeChanged.call(this); 176 var locSize = this._contentSize; 177 this._topBoundary = locSize.height; 178 this._rightBoundary = locSize.width; 179 var innerSize = this._innerContainer.getContentSize(); 180 this._innerContainer.setContentSize(cc.size(Math.max(innerSize.width, locSize.width), Math.max(innerSize.height, locSize.height))); 181 this._innerContainer.setPosition(0, locSize.height - this._innerContainer.getContentSize().height); 182 183 if(this._verticalScrollBar) 184 this._verticalScrollBar.onScrolled(this._getHowMuchOutOfBoundary()); 185 186 if(this._horizontalScrollBar) 187 this._horizontalScrollBar.onScrolled(this._getHowMuchOutOfBoundary()); 188 }, 189 190 /** 191 * Changes inner container size of ScrollView. <br/> 192 * Inner container size must be larger than or equal the size of ScrollView. 193 * @param {cc.Size} size inner container size. 194 */ 195 setInnerContainerSize: function (size) { 196 var innerContainer = this._innerContainer, 197 locSize = this._contentSize, 198 innerSizeWidth = locSize.width, innerSizeHeight = locSize.height; 199 200 if (size.width < locSize.width) 201 cc.log("Inner width <= ScrollView width, it will be force sized!"); 202 else 203 innerSizeWidth = size.width; 204 205 if (size.height < locSize.height) 206 cc.log("Inner height <= ScrollView height, it will be force sized!"); 207 else 208 innerSizeHeight = size.height; 209 210 innerContainer.setContentSize(cc.size(innerSizeWidth, innerSizeHeight)); 211 212 var pos = this._innerContainer.getPosition(); 213 var contAP = this._innerContainer.getAnchorPoint(); 214 215 if (this._innerContainer.getLeftBoundary() != 0.0) 216 { 217 pos.x = contAP.x * innerSizeWidth; 218 } 219 if (this._innerContainer.getTopBoundary() != this._contentSize.height) 220 { 221 pos.y = this._contentSize.height - (1.0 - contAP.y) * innerSizeHeight; 222 } 223 this.setInnerContainerPosition(pos); 224 225 this._updateScrollBar(cc.p(0 ,0)); 226 }, 227 228 _setInnerWidth: function (width) { 229 var locW = this._contentSize.width, 230 innerWidth = locW, 231 container = this._innerContainer, 232 oldInnerWidth = container.width; 233 if (width < locW) 234 cc.log("Inner width <= scrollview width, it will be force sized!"); 235 else 236 innerWidth = width; 237 container.width = innerWidth; 238 239 switch (this._direction) { 240 case ccui.ScrollView.DIR_HORIZONTAL: 241 case ccui.ScrollView.DIR_BOTH: 242 if (container.getRightBoundary() <= locW) { 243 var newInnerWidth = container.width; 244 var offset = oldInnerWidth - newInnerWidth; 245 this._scrollChildren(offset, 0); 246 } 247 break; 248 } 249 var innerAX = container.anchorX; 250 if (container.getLeftBoundary() > 0.0) 251 container.x = innerAX * innerWidth; 252 if (container.getRightBoundary() < locW) 253 container.x = locW - ((1.0 - innerAX) * innerWidth); 254 }, 255 256 _setInnerHeight: function (height) { 257 var locH = this._contentSize.height, 258 innerHeight = locH, 259 container = this._innerContainer, 260 oldInnerHeight = container.height; 261 if (height < locH) 262 cc.log("Inner height <= scrollview height, it will be force sized!"); 263 else 264 innerHeight = height; 265 container.height = innerHeight; 266 267 switch (this._direction) { 268 case ccui.ScrollView.DIR_VERTICAL: 269 case ccui.ScrollView.DIR_BOTH: 270 var newInnerHeight = innerHeight; 271 var offset = oldInnerHeight - newInnerHeight; 272 this._scrollChildren(0, offset); 273 break; 274 } 275 var innerAY = container.anchorY; 276 if (container.getLeftBoundary() > 0.0) 277 container.y = innerAY * innerHeight; 278 if (container.getRightBoundary() < locH) 279 container.y = locH - ((1.0 - innerAY) * innerHeight); 280 }, 281 /** 282 * Set inner container position 283 * 284 * @param {cc.Point} position Inner container position. 285 */ 286 setInnerContainerPosition: function(position) 287 { 288 if(position.x === this._innerContainer.getPositionX() && position.y === this._innerContainer.getPositionY()) 289 { 290 return; 291 } 292 this._innerContainer.setPosition(position); 293 this._outOfBoundaryAmountDirty = true; 294 295 // Process bouncing events 296 if(this.bounceEnabled) 297 { 298 for(var _direction = ccui.ScrollView.MOVEDIR_TOP; _direction < ccui.ScrollView.MOVEDIR_RIGHT; ++_direction) 299 { 300 if(this._isOutOfBoundary(_direction)) 301 { 302 this._processScrollEvent(_direction, true); 303 } 304 } 305 } 306 307 this._dispatchEvent(ccui.ScrollView.EVENT_CONTAINER_MOVED); 308 }, 309 310 /** 311 * Get inner container position 312 * 313 * @return The inner container position. 314 */ 315 getInnerContainerPosition: function() 316 { 317 return this._innerContainer.getPosition(); 318 }, 319 320 /** 321 * Returns inner container size of ScrollView. <br/> 322 * Inner container size must be larger than or equal ScrollView's size. 323 * 324 * @return {cc.Size} inner container size. 325 */ 326 getInnerContainerSize: function () { 327 return this._innerContainer.getContentSize(); 328 }, 329 _getInnerWidth: function () { 330 return this._innerContainer.width; 331 }, 332 _getInnerHeight: function () { 333 return this._innerContainer.height; 334 }, 335 336 _isInContainer: function (widget) { 337 if(!this._clippingEnabled) 338 return true; 339 var wPos = widget._position, 340 wSize = widget._contentSize, 341 wAnchor = widget._anchorPoint, 342 size = this._customSize, 343 pos = this._innerContainer._position, 344 bottom = 0, left = 0; 345 if ( 346 // Top 347 (bottom = wPos.y - wAnchor.y * wSize.height) >= size.height - pos.y || 348 // Bottom 349 bottom + wSize.height <= -pos.y || 350 // right 351 (left = wPos.x - wAnchor.x * wSize.width) >= size.width - pos.x || 352 // left 353 left + wSize.width <= -pos.x 354 ) 355 return false; 356 else return true; 357 }, 358 359 updateChildren: function () { 360 var child, i, l; 361 var childrenArray = this._innerContainer._children; 362 for(i = 0, l = childrenArray.length; i < l; i++) { 363 child = childrenArray[i]; 364 if(child._inViewRect === true && this._isInContainer(child) === false) 365 child._inViewRect = false; 366 else if (child._inViewRect === false && this._isInContainer(child) === true) 367 child._inViewRect = true; 368 } 369 }, 370 /** 371 * Add child to ccui.ScrollView. 372 * @param {cc.Node} widget 373 * @param {Number} [zOrder] 374 * @param {Number|string} [tag] tag or name 375 * @returns {boolean} 376 */ 377 addChild: function (widget, zOrder, tag) { 378 if(!widget) 379 return false; 380 if(this._isInContainer(widget) === false) 381 widget._inViewRect = false; 382 zOrder = zOrder || widget.getLocalZOrder(); 383 tag = tag || widget.getTag(); 384 return this._innerContainer.addChild(widget, zOrder, tag); 385 }, 386 387 /** 388 * Removes all children. 389 */ 390 removeAllChildren: function () { 391 this.removeAllChildrenWithCleanup(true); 392 }, 393 394 /** 395 * Removes all children. 396 * @param {Boolean} cleanup 397 */ 398 removeAllChildrenWithCleanup: function(cleanup){ 399 this._innerContainer.removeAllChildrenWithCleanup(cleanup); 400 }, 401 402 /** 403 * Removes widget child 404 * @override 405 * @param {ccui.Widget} child 406 * @param {Boolean} cleanup 407 * @returns {boolean} 408 */ 409 removeChild: function (child, cleanup) { 410 return this._innerContainer.removeChild(child, cleanup); 411 }, 412 413 /** 414 * Returns inner container's children 415 * @returns {Array} 416 */ 417 getChildren: function () { 418 return this._innerContainer.getChildren(); 419 }, 420 421 /** 422 * Gets the count of inner container's children 423 * @returns {Number} 424 */ 425 getChildrenCount: function () { 426 return this._innerContainer.getChildrenCount(); 427 }, 428 429 /** 430 * Gets a child from the container given its tag 431 * @param {Number} tag 432 * @returns {ccui.Widget} 433 */ 434 getChildByTag: function (tag) { 435 return this._innerContainer.getChildByTag(tag); 436 }, 437 438 /** 439 * Gets a child from the container given its name 440 * @param {String} name 441 * @returns {ccui.Widget} 442 */ 443 getChildByName: function (name) { 444 return this._innerContainer.getChildByName(name); 445 }, 446 447 _flattenVectorByDirection: function(vector) 448 { 449 var result = cc.p(0 ,0); 450 result.x = (this._direction === ccui.ScrollView.DIR_VERTICAL ? 0 : vector.x); 451 result.y = (this._direction === ccui.ScrollView.DIR_HORIZONTAL ? 0 : vector.y); 452 return result; 453 }, 454 455 _getHowMuchOutOfBoundary: function(addition) 456 { 457 if(addition === undefined) 458 addition = cc.p(0, 0); 459 460 if(addition.x === 0 && addition.y === 0 && !this._outOfBoundaryAmountDirty) 461 { 462 return this._outOfBoundaryAmount; 463 } 464 465 var outOfBoundaryAmount = cc.p(0, 0); 466 467 if(this._innerContainer.getLeftBoundary() + addition.x > this._leftBoundary) 468 { 469 outOfBoundaryAmount.x = this._leftBoundary - (this._innerContainer.getLeftBoundary() + addition.x); 470 } 471 else if(this._innerContainer.getRightBoundary() + addition.x < this._rightBoundary) 472 { 473 outOfBoundaryAmount.x = this._rightBoundary - (this._innerContainer.getRightBoundary() + addition.x); 474 } 475 476 if(this._innerContainer.getTopBoundary() + addition.y < this._topBoundary) 477 { 478 outOfBoundaryAmount.y = this._topBoundary - (this._innerContainer.getTopBoundary() + addition.y); 479 } 480 else if(this._innerContainer.getBottomBoundary() + addition.y > this._bottomBoundary) 481 { 482 outOfBoundaryAmount.y = this._bottomBoundary - (this._innerContainer.getBottomBoundary() + addition.y); 483 } 484 485 if(addition.x === 0 && addition.y === 0 ) 486 { 487 this._outOfBoundaryAmount = outOfBoundaryAmount; 488 this._outOfBoundaryAmountDirty = false; 489 } 490 return outOfBoundaryAmount; 491 }, 492 493 _isOutOfBoundary: function(dir) 494 { 495 var outOfBoundary = this._getHowMuchOutOfBoundary(); 496 if(dir !== undefined) 497 { 498 switch (dir) 499 { 500 case ccui.ScrollView.MOVEDIR_TOP: 501 return outOfBoundary.y > 0; 502 case ccui.ScrollView.MOVEDIR_BOTTOM: 503 return outOfBoundary.y < 0; 504 case ccui.ScrollView.MOVEDIR_LEFT: 505 return outOfBoundary.x < 0; 506 case ccui.ScrollView.MOVEDIR_RIGHT: 507 return outOfBoundary.x > 0; 508 } 509 } 510 else 511 { 512 return !this._fltEqualZero(outOfBoundary); 513 } 514 515 return false; 516 }, 517 518 519 _moveInnerContainer: function(deltaMove, canStartBounceBack) 520 { 521 var adjustedMove = this._flattenVectorByDirection(deltaMove); 522 523 this.setInnerContainerPosition(cc.pAdd(this.getInnerContainerPosition(), adjustedMove)); 524 525 var outOfBoundary =this._getHowMuchOutOfBoundary(); 526 this._updateScrollBar(outOfBoundary); 527 528 if(this.bounceEnabled && canStartBounceBack) 529 { 530 this._startBounceBackIfNeeded(); 531 } 532 }, 533 534 _updateScrollBar: function(outOfBoundary) 535 { 536 if(this._verticalScrollBar) 537 { 538 this._verticalScrollBar.onScrolled(outOfBoundary); 539 } 540 if(this._horizontalScrollBar) 541 { 542 this._horizontalScrollBar.onScrolled(outOfBoundary); 543 } 544 }, 545 546 _calculateTouchMoveVelocity: function() 547 { 548 var totalTime = 0; 549 for(var i = 0; i < this._touchMoveTimeDeltas.length; ++i) 550 { 551 totalTime += this._touchMoveTimeDeltas[i]; 552 } 553 if(totalTime == 0 || totalTime >= this._touchTotalTimeThreshold) 554 { 555 return cc.p(0, 0); 556 } 557 558 var totalMovement = cc.p(0 ,0); 559 560 for(var i = 0; i < this._touchMoveDisplacements.length; ++i) 561 { 562 totalMovement.x += this._touchMoveDisplacements[i].x; 563 totalMovement.y += this._touchMoveDisplacements[i].y; 564 } 565 566 return cc.pMult(totalMovement, 1 / totalTime); 567 }, 568 569 /** 570 * Set the touch total time threshold 571 * @param {Number} touchTotalTimeThreshold 572 */ 573 setTouchTotalTimeThreshold: function(touchTotalTimeThreshold) 574 { 575 this._touchTotalTimeThreshold = touchTotalTimeThreshold; 576 }, 577 578 579 /** 580 * Get the touch total time threshold 581 * @returns {Number} 582 */ 583 getTouchTotalTimeThreshold: function() 584 { 585 return this._touchTotalTimeThreshold; 586 }, 587 588 _startInertiaScroll: function(touchMoveVelocity) 589 { 590 var MOVEMENT_FACTOR = 0.7; 591 var inertiaTotalMovement = cc.pMult(touchMoveVelocity, MOVEMENT_FACTOR); 592 this._startAttenuatingAutoScroll(inertiaTotalMovement, touchMoveVelocity); 593 }, 594 595 _startBounceBackIfNeeded: function() 596 { 597 if (!this.bounceEnabled) 598 { 599 return false; 600 } 601 var bounceBackAmount = this._getHowMuchOutOfBoundary(); 602 if(this._fltEqualZero(bounceBackAmount)) 603 { 604 return false; 605 } 606 607 var BOUNCE_BACK_DURATION = 1.0; 608 this._startAutoScroll(bounceBackAmount, BOUNCE_BACK_DURATION, true); 609 return true; 610 }, 611 612 _startAutoScrollToDestination: function(destination, timeInSec, attenuated) 613 { 614 this._startAutoScroll(cc.pSub(destination , this._innerContainer.getPosition()), timeInSec, attenuated); 615 }, 616 617 _calculateAutoScrollTimeByInitialSpeed: function(initialSpeed) 618 { 619 // Calculate the time from the initial speed according to quintic polynomial. 620 return Math.sqrt(Math.sqrt(initialSpeed / 5)); 621 }, 622 623 _startAttenuatingAutoScroll: function(deltaMove, initialVelocity) 624 { 625 var time = this._calculateAutoScrollTimeByInitialSpeed(cc.pLength(initialVelocity)); 626 this._startAutoScroll(deltaMove, time, true); 627 }, 628 629 _startAutoScroll: function(deltaMove, timeInSec, attenuated) 630 { 631 var adjustedDeltaMove = this._flattenVectorByDirection(deltaMove); 632 633 this._autoScrolling = true; 634 this._autoScrollTargetDelta = adjustedDeltaMove; 635 this._autoScrollAttenuate = attenuated; 636 this._autoScrollStartPosition = this._innerContainer.getPosition(); 637 this._autoScrollTotalTime = timeInSec; 638 this._autoScrollAccumulatedTime = 0; 639 this._autoScrollBraking = false; 640 this._autoScrollBrakingStartPosition = cc.p(0,0 ); 641 642 // If the destination is also out of boundary of same side, start brake from beggining. 643 var currentOutOfBoundary = this._getHowMuchOutOfBoundary(); 644 if(!this._fltEqualZero(currentOutOfBoundary)) 645 { 646 this._autoScrollCurrentlyOutOfBoundary = true; 647 var afterOutOfBoundary = this._getHowMuchOutOfBoundary(adjustedDeltaMove); 648 if(currentOutOfBoundary.x * afterOutOfBoundary.x > 0 || currentOutOfBoundary.y * afterOutOfBoundary.y > 0) 649 { 650 this._autoScrollBraking = true; 651 } 652 } 653 }, 654 655 /** 656 * Immediately stops inner container scroll initiated by any of the "scrollTo*" member functions 657 */ 658 stopAutoScroll: function() 659 { 660 this._autoScrolling = false; 661 this._autoScrollAttenuate = true; 662 this._autoScrollTotalTime = 0; 663 this._autoScrollAccumulatedTime = 0; 664 }, 665 666 _isNecessaryAutoScrollBrake: function() 667 { 668 if(this._autoScrollBraking) 669 { 670 return true; 671 } 672 673 if(this._isOutOfBoundary()) 674 { 675 // It just went out of boundary. 676 if(!this._autoScrollCurrentlyOutOfBoundary) 677 { 678 this._autoScrollCurrentlyOutOfBoundary = true; 679 this._autoScrollBraking = true; 680 this._autoScrollBrakingStartPosition = this.getInnerContainerPosition(); 681 return true; 682 } 683 } 684 else 685 { 686 this._autoScrollCurrentlyOutOfBoundary = false; 687 } 688 return false; 689 }, 690 691 _getAutoScrollStopEpsilon: function() 692 { 693 return 0.0001; 694 }, 695 696 _fltEqualZero: function(point) 697 { 698 return (Math.abs(point.x) <= 0.0001 && Math.abs(point.y) <= 0.0001); 699 }, 700 701 _processAutoScrolling: function(deltaTime) 702 { 703 var OUT_OF_BOUNDARY_BREAKING_FACTOR = 0.05; 704 // Make auto scroll shorter if it needs to deaccelerate. 705 var brakingFactor = (this._isNecessaryAutoScrollBrake() ? OUT_OF_BOUNDARY_BREAKING_FACTOR : 1); 706 707 // Elapsed time 708 this._autoScrollAccumulatedTime += deltaTime * (1 / brakingFactor); 709 710 // Calculate the progress percentage 711 var percentage = Math.min(1, this._autoScrollAccumulatedTime / this._autoScrollTotalTime); 712 if(this._autoScrollAttenuate) 713 { 714 percentage -= 1; 715 percentage = percentage * percentage * percentage * percentage * percentage + 1; 716 } 717 718 // Calculate the new position 719 var newPosition = cc.pAdd(this._autoScrollStartPosition, cc.pMult(this._autoScrollTargetDelta,percentage)); 720 var reachedEnd = Math.abs(percentage - 1) <= this._getAutoScrollStopEpsilon(); 721 722 if(this.bounceEnabled) 723 { 724 // The new position is adjusted if out of boundary 725 newPosition = cc.pAdd(this._autoScrollBrakingStartPosition, cc.pMult(cc.pSub(newPosition, this._autoScrollBrakingStartPosition), brakingFactor)); 726 } 727 else 728 { 729 // Don't let go out of boundary 730 var moveDelta = cc.pSub(newPosition, this.getInnerContainerPosition()); 731 var outOfBoundary = this._getHowMuchOutOfBoundary(moveDelta); 732 if(!this._fltEqualZero(outOfBoundary)) 733 { 734 newPosition.x += outOfBoundary.x; 735 newPosition.y += outOfBoundary.y; 736 737 reachedEnd = true; 738 } 739 } 740 741 // Finish auto scroll if it ended 742 if(reachedEnd) 743 { 744 this._autoScrolling = false; 745 this._dispatchEvent(ccui.ScrollView.EVENT_AUTOSCROLL_ENDED); 746 } 747 748 this._moveInnerContainer(cc.pSub(newPosition, this.getInnerContainerPosition()), reachedEnd); 749 }, 750 751 _jumpToDestination: function (desOrX, y) 752 { 753 if(desOrX.x === undefined) 754 { 755 desOrX = cc.p(desOrX, y); 756 } 757 758 this._autoScrolling = false; 759 this._moveInnerContainer(cc.pSub(desOrX, this.getInnerContainerPosition()), true); 760 }, 761 762 _scrollChildren: function(deltaMove) 763 { 764 var realMove = deltaMove; 765 if(this.bounceEnabled) 766 { 767 // If the position of the inner container is out of the boundary, the offsets should be divided by two. 768 var outOfBoundary = this._getHowMuchOutOfBoundary(); 769 realMove.x *= (outOfBoundary.x == 0 ? 1 : 0.5); 770 realMove.y *= (outOfBoundary.y == 0 ? 1 : 0.5); 771 } 772 773 if(!this.bounceEnabled) 774 { 775 var outOfBoundary = this._getHowMuchOutOfBoundary(realMove); 776 realMove.x += outOfBoundary.x; 777 realMove.y += outOfBoundary.y; 778 } 779 780 var scrolledToLeft = false; 781 var scrolledToRight = false; 782 var scrolledToTop = false; 783 var scrolledToBottom = false; 784 785 if (realMove.y > 0.0) // up 786 { 787 var icBottomPos = this._innerContainer.getBottomBoundary(); 788 if (icBottomPos + realMove.y >= this._bottomBoundary) 789 { 790 scrolledToBottom = true; 791 } 792 } 793 else if (realMove.y < 0.0) // down 794 { 795 var icTopPos = this._innerContainer.getTopBoundary(); 796 if (icTopPos + realMove.y <= this._topBoundary) 797 { 798 scrolledToTop = true; 799 } 800 } 801 802 if (realMove.x < 0.0) // left 803 { 804 var icRightPos = this._innerContainer.getRightBoundary(); 805 if (icRightPos + realMove.x <= this._rightBoundary) 806 { 807 scrolledToRight = true; 808 } 809 } 810 else if (realMove.x > 0.0) // right 811 { 812 var icLeftPos = this._innerContainer.getLeftBoundary(); 813 if (icLeftPos + realMove.x >= this._leftBoundary) 814 { 815 scrolledToLeft = true; 816 } 817 } 818 this._moveInnerContainer(realMove, false); 819 820 if(realMove.x != 0 || realMove.y != 0) 821 { 822 this._processScrollingEvent(); 823 } 824 if(scrolledToBottom) 825 { 826 this._processScrollEvent(ccui.ScrollView.MOVEDIR_BOTTOM, false); 827 } 828 if(scrolledToTop) 829 { 830 this._processScrollEvent(ccui.ScrollView.MOVEDIR_TOP, false); 831 } 832 if(scrolledToLeft) 833 { 834 this._processScrollEvent(ccui.ScrollView.MOVEDIR_LEFT, false); 835 } 836 if(scrolledToRight) 837 { 838 this._processScrollEvent(ccui.ScrollView.MOVEDIR_RIGHT, false); 839 } 840 }, 841 842 /** 843 * Scroll inner container to bottom boundary of ScrollView. 844 * @param {Number} time 845 * @param {Boolean} attenuated 846 */ 847 scrollToBottom: function (time, attenuated) { 848 this._startAutoScrollToDestination(cc.p(this._innerContainer.getPositionX(), 0), time, attenuated); 849 }, 850 851 /** 852 * Scroll inner container to top boundary of ScrollView. 853 * @param {Number} time 854 * @param {Boolean} attenuated 855 */ 856 scrollToTop: function (time, attenuated) { 857 this._startAutoScrollToDestination( 858 cc.p(this._innerContainer.getPositionX(), this._contentSize.height - this._innerContainer.getContentSize().height), time, attenuated); 859 }, 860 861 /** 862 * Scroll inner container to left boundary of ScrollView. 863 * @param {Number} time 864 * @param {Boolean} attenuated 865 */ 866 scrollToLeft: function (time, attenuated) { 867 this._startAutoScrollToDestination(cc.p(0, this._innerContainer.getPositionY()), time, attenuated); 868 }, 869 870 /** 871 * Scroll inner container to right boundary of ScrollView. 872 * @param {Number} time 873 * @param {Boolean} attenuated 874 */ 875 scrollToRight: function (time, attenuated) { 876 this._startAutoScrollToDestination( 877 cc.p(this._contentSize.width - this._innerContainer.getContentSize().width, this._innerContainer.getPositionY()), time, attenuated); 878 }, 879 880 /** 881 * Scroll inner container to top and left boundary of ScrollView. 882 * @param {Number} time 883 * @param {Boolean} attenuated 884 */ 885 scrollToTopLeft: function (time, attenuated) { 886 if (this._direction !== ccui.ScrollView.DIR_BOTH) { 887 cc.log("Scroll direction is not both!"); 888 return; 889 } 890 this._startAutoScrollToDestination(cc.p(0, this._contentSize.height - this._innerContainer.getContentSize().height), time, attenuated); 891 }, 892 893 /** 894 * Scroll inner container to top and right boundary of ScrollView. 895 * @param {Number} time 896 * @param {Boolean} attenuated 897 */ 898 scrollToTopRight: function (time, attenuated) { 899 if (this._direction !== ccui.ScrollView.DIR_BOTH) { 900 cc.log("Scroll direction is not both!"); 901 return; 902 } 903 var inSize = this._innerContainer.getContentSize(); 904 this._startAutoScrollToDestination(cc.p(this._contentSize.width - inSize.width, 905 this._contentSize.height - inSize.height), time, attenuated); 906 }, 907 908 /** 909 * Scroll inner container to bottom and left boundary of ScrollView. 910 * @param {Number} time 911 * @param {Boolean} attenuated 912 */ 913 scrollToBottomLeft: function (time, attenuated) { 914 if (this._direction !== ccui.ScrollView.DIR_BOTH) { 915 cc.log("Scroll direction is not both!"); 916 return; 917 } 918 this._startAutoScrollToDestination(cc.p(0, 0), time, attenuated); 919 }, 920 921 /** 922 * Scroll inner container to bottom and right boundary of ScrollView. 923 * @param {Number} time 924 * @param {Boolean} attenuated 925 */ 926 scrollToBottomRight: function (time, attenuated) { 927 if (this._direction !== ccui.ScrollView.DIR_BOTH) { 928 cc.log("Scroll direction is not both!"); 929 return; 930 } 931 this._startAutoScrollToDestination(cc.p(this._contentSize.width - this._innerContainer.getContentSize().width, 0), time, attenuated); 932 }, 933 934 /** 935 * Scroll inner container to vertical percent position of ScrollView. 936 * @param {Number} percent 937 * @param {Number} time 938 * @param {Boolean} attenuated 939 */ 940 scrollToPercentVertical: function (percent, time, attenuated) { 941 var minY = this._contentSize.height - this._innerContainer.getContentSize().height; 942 var h = -minY; 943 this._startAutoScrollToDestination(cc.p(this._innerContainer.getPositionX(), minY + percent * h / 100), time, attenuated); 944 }, 945 946 /** 947 * Scroll inner container to horizontal percent position of ScrollView. 948 * @param {Number} percent 949 * @param {Number} time 950 * @param {Boolean} attenuated 951 */ 952 scrollToPercentHorizontal: function (percent, time, attenuated) { 953 var w = this._innerContainer.getContentSize().width - this._contentSize.width; 954 this._startAutoScrollToDestination(cc.p(-(percent * w / 100), this._innerContainer.getPositionY()), time, attenuated); 955 }, 956 957 /** 958 * Scroll inner container to both _direction percent position of ScrollView. 959 * @param {cc.Point} percent 960 * @param {Number} time 961 * @param {Boolean} attenuated 962 */ 963 scrollToPercentBothDirection: function (percent, time, attenuated) { 964 if (this._direction !== ccui.ScrollView.DIR_BOTH) 965 return; 966 var minY = this._contentSize.height - this._innerContainer.getContentSize().height; 967 var h = -minY; 968 var w = this._innerContainer.getContentSize().width - this._contentSize.width; 969 this._startAutoScrollToDestination(cc.p(-(percent.x * w / 100), minY + percent.y * h / 100), time, attenuated); 970 }, 971 972 /** 973 * Move inner container to bottom boundary of ScrollView. 974 */ 975 jumpToBottom: function () { 976 this._jumpToDestination(this._innerContainer.getPositionX(), 0); 977 }, 978 979 /** 980 * Move inner container to top boundary of ScrollView. 981 */ 982 jumpToTop: function () { 983 this._jumpToDestination(this._innerContainer.getPositionX(), this._contentSize.height - this._innerContainer.getContentSize().height); 984 }, 985 986 /** 987 * Move inner container to left boundary of ScrollView. 988 */ 989 jumpToLeft: function () { 990 this._jumpToDestination(0, this._innerContainer.getPositionY()); 991 }, 992 993 /** 994 * Move inner container to right boundary of ScrollView. 995 */ 996 jumpToRight: function () { 997 this._jumpToDestination(this._contentSize.width - this._innerContainer.getContentSize().width, this._innerContainer.getPositionY()); 998 }, 999 1000 /** 1001 * Move inner container to top and left boundary of ScrollView. 1002 */ 1003 jumpToTopLeft: function () { 1004 if (this._direction !== ccui.ScrollView.DIR_BOTH) { 1005 cc.log("Scroll _direction is not both!"); 1006 return; 1007 } 1008 this._jumpToDestination(0, this._contentSize.height - this._innerContainer.getContentSize().height); 1009 }, 1010 1011 /** 1012 * Move inner container to top and right boundary of ScrollView. 1013 */ 1014 jumpToTopRight: function () { 1015 if (this._direction !== ccui.ScrollView.DIR_BOTH) { 1016 cc.log("Scroll _direction is not both!"); 1017 return; 1018 } 1019 var inSize = this._innerContainer.getContentSize(); 1020 this._jumpToDestination(this._contentSize.width - inSize.width, this._contentSize.height - inSize.height); 1021 }, 1022 1023 /** 1024 * Move inner container to bottom and left boundary of ScrollView. 1025 */ 1026 jumpToBottomLeft: function () { 1027 if (this._direction !== ccui.ScrollView.DIR_BOTH) { 1028 cc.log("Scroll _direction is not both!"); 1029 return; 1030 } 1031 this._jumpToDestination(0, 0); 1032 }, 1033 1034 /** 1035 * Move inner container to bottom and right boundary of ScrollView. 1036 */ 1037 jumpToBottomRight: function () { 1038 if (this._direction !== ccui.ScrollView.DIR_BOTH) { 1039 cc.log("Scroll _direction is not both!"); 1040 return; 1041 } 1042 this._jumpToDestination(this._contentSize.width - this._innerContainer.getContentSize().width, 0); 1043 }, 1044 1045 /** 1046 * Move inner container to vertical percent position of ScrollView. 1047 * @param {Number} percent The destination vertical percent, accept value between 0 - 100 1048 */ 1049 jumpToPercentVertical: function (percent) { 1050 var minY = this._contentSize.height - this._innerContainer.getContentSize().height; 1051 var h = -minY; 1052 this._jumpToDestination(this._innerContainer.getPositionX(), minY + percent * h / 100); 1053 }, 1054 1055 /** 1056 * Move inner container to horizontal percent position of ScrollView. 1057 * @param {Number} percent The destination vertical percent, accept value between 0 - 100 1058 */ 1059 jumpToPercentHorizontal: function (percent) { 1060 var w = this._innerContainer.getContentSize().width - this._contentSize.width; 1061 this._jumpToDestination(-(percent * w / 100), this._innerContainer.getPositionY()); 1062 }, 1063 1064 /** 1065 * Move inner container to both _direction percent position of ScrollView. 1066 * @param {cc.Point} percent The destination vertical percent, accept value between 0 - 100 1067 */ 1068 jumpToPercentBothDirection: function (percent) { 1069 if (this._direction !== ccui.ScrollView.DIR_BOTH) 1070 return; 1071 var inSize = this._innerContainer.getContentSize(); 1072 var minY = this._contentSize.height - inSize.height; 1073 var h = -minY; 1074 var w = inSize.width - this._contentSize.width; 1075 this._jumpToDestination(-(percent.x * w / 100), minY + percent.y * h / 100); 1076 }, 1077 1078 _gatherTouchMove: function(delta) 1079 { 1080 var NUMBER_OF_GATHERED_TOUCHES_FOR_MOVE_SPEED = 5; 1081 while(this._touchMoveDisplacements.length >= NUMBER_OF_GATHERED_TOUCHES_FOR_MOVE_SPEED) 1082 { 1083 this._touchMoveDisplacements.splice(0,1); 1084 this._touchMoveTimeDeltas.splice(0,1) 1085 } 1086 this._touchMoveDisplacements.push(delta); 1087 1088 var timestamp = (new Date()).getTime(); 1089 this._touchMoveTimeDeltas.push((timestamp - this._touchMovePreviousTimestamp) / 1000); 1090 this._touchMovePreviousTimestamp = timestamp; 1091 }, 1092 1093 _handlePressLogic: function (touch) { 1094 this._bePressed = true; 1095 this._autoScrolling = false; 1096 1097 // Clear gathered touch move information 1098 1099 this._touchMovePreviousTimestamp = (new Date()).getTime(); 1100 this._touchMoveDisplacements.length = 0; 1101 this._touchMoveTimeDeltas.length = 0; 1102 1103 1104 if(this._verticalScrollBar) 1105 { 1106 this._verticalScrollBar.onTouchBegan(); 1107 } 1108 if(this._horizontalScrollBar) 1109 { 1110 this._horizontalScrollBar.onTouchBegan(); 1111 } 1112 }, 1113 1114 _handleMoveLogic: function (touch) { 1115 var touchPositionInNodeSpace = this.convertToNodeSpace(touch.getLocation()), 1116 previousTouchPositionInNodeSpace = this.convertToNodeSpace(touch.getPreviousLocation()); 1117 var delta = cc.pSub(touchPositionInNodeSpace, previousTouchPositionInNodeSpace); 1118 1119 this._scrollChildren(delta); 1120 this._gatherTouchMove(delta); 1121 }, 1122 1123 _handleReleaseLogic: function (touch) { 1124 1125 var touchPositionInNodeSpace = this.convertToNodeSpace(touch.getLocation()), 1126 previousTouchPositionInNodeSpace = this.convertToNodeSpace(touch.getPreviousLocation()); 1127 var delta = cc.pSub(touchPositionInNodeSpace, previousTouchPositionInNodeSpace); 1128 1129 this._gatherTouchMove(delta); 1130 1131 this._bePressed = false; 1132 1133 var bounceBackStarted = this._startBounceBackIfNeeded(); 1134 if(!bounceBackStarted && this.inertiaScrollEnabled) 1135 { 1136 var touchMoveVelocity = this._calculateTouchMoveVelocity(); 1137 if(touchMoveVelocity.x !== 0 || touchMoveVelocity.y !== 0) 1138 { 1139 this._startInertiaScroll(touchMoveVelocity); 1140 } 1141 } 1142 1143 if(this._verticalScrollBar) 1144 { 1145 this._verticalScrollBar.onTouchEnded(); 1146 } 1147 if(this._horizontalScrollBar) 1148 { 1149 this._horizontalScrollBar.onTouchEnded(); 1150 } 1151 }, 1152 1153 /** 1154 * The touch began event callback handler of ccui.ScrollView. 1155 * @param {cc.Touch} touch 1156 * @param {cc.Event} event 1157 * @returns {boolean} 1158 */ 1159 onTouchBegan: function (touch, event) { 1160 var pass = ccui.Layout.prototype.onTouchBegan.call(this, touch, event); 1161 if(!this._isInterceptTouch){ 1162 if (this._hit) 1163 this._handlePressLogic(touch); 1164 } 1165 return pass; 1166 }, 1167 1168 /** 1169 * The touch moved event callback handler of ccui.ScrollView. 1170 * @param {cc.Touch} touch 1171 * @param {cc.Event} event 1172 */ 1173 onTouchMoved: function (touch, event) { 1174 ccui.Layout.prototype.onTouchMoved.call(this, touch, event); 1175 if(!this._isInterceptTouch) 1176 this._handleMoveLogic(touch); 1177 }, 1178 1179 /** 1180 * The touch ended event callback handler of ccui.ScrollView. 1181 * @param {cc.Touch} touch 1182 * @param {cc.Event} event 1183 */ 1184 onTouchEnded: function (touch, event) { 1185 ccui.Layout.prototype.onTouchEnded.call(this, touch, event); 1186 if(!this._isInterceptTouch) 1187 this._handleReleaseLogic(touch); 1188 this._isInterceptTouch = false; 1189 }, 1190 1191 /** 1192 * The touch canceled event callback of ccui.ScrollView. 1193 * @param {cc.Touch} touch 1194 * @param {cc.Event} event 1195 */ 1196 onTouchCancelled: function (touch, event) { 1197 ccui.Layout.prototype.onTouchCancelled.call(this, touch, event); 1198 if (!this._isInterceptTouch) 1199 this._handleReleaseLogic(touch); 1200 this._isInterceptTouch = false; 1201 }, 1202 1203 /** 1204 * The update callback handler. 1205 * @param {Number} dt 1206 */ 1207 update: function (dt) { 1208 if (this._autoScrolling) 1209 this._processAutoScrolling(dt); 1210 }, 1211 1212 /** 1213 * Intercept touch event, handle its child's touch event. 1214 * @override 1215 * @param {number} event event type 1216 * @param {ccui.Widget} sender 1217 * @param {cc.Touch} touch 1218 */ 1219 interceptTouchEvent: function (event, sender, touch) { 1220 if (!this._touchEnabled) { 1221 ccui.Layout.prototype.interceptTouchEvent.call(this, event, sender, touch); 1222 return; 1223 } 1224 1225 if(this._direction === ccui.ScrollView.DIR_NONE) 1226 return; 1227 1228 var touchPoint = touch.getLocation(); 1229 switch (event) { 1230 case ccui.Widget.TOUCH_BEGAN: 1231 this._isInterceptTouch = true; 1232 this._touchBeganPosition.x = touchPoint.x; 1233 this._touchBeganPosition.y = touchPoint.y; 1234 this._handlePressLogic(touch); 1235 break; 1236 case ccui.Widget.TOUCH_MOVED: 1237 var offset = cc.pLength(cc.pSub(sender.getTouchBeganPosition(), touchPoint)); 1238 this._touchMovePosition.x = touchPoint.x; 1239 this._touchMovePosition.y = touchPoint.y; 1240 if (offset > this._childFocusCancelOffset) { 1241 sender.setHighlighted(false); 1242 this._handleMoveLogic(touch); 1243 } 1244 break; 1245 case ccui.Widget.TOUCH_CANCELED: 1246 case ccui.Widget.TOUCH_ENDED: 1247 this._touchEndPosition.x = touchPoint.x; 1248 this._touchEndPosition.y = touchPoint.y; 1249 this._handleReleaseLogic(touch); 1250 if (sender.isSwallowTouches()) 1251 this._isInterceptTouch = false; 1252 break; 1253 } 1254 }, 1255 1256 _processScrollEvent: function(_directionEvent, bounce) 1257 { 1258 var event = 0; 1259 1260 switch(_directionEvent) 1261 { 1262 case ccui.ScrollView.MOVEDIR_TOP: 1263 event = (bounce ? ccui.ScrollView.EVENT_BOUNCE_TOP : ccui.ScrollView.EVENT_SCROLL_TO_TOP); 1264 break; 1265 case ccui.ScrollView.MOVEDIR_BOTTOM: 1266 event = (bounce ? ccui.ScrollView.EVENT_BOUNCE_BOTTOM : ccui.ScrollView.EVENT_SCROLL_TO_BOTTOM); 1267 break; 1268 case ccui.ScrollView.MOVEDIR_LEFT: 1269 event = (bounce ? ccui.ScrollView.EVENT_BOUNCE_LEFT : ccui.ScrollView.EVENT_SCROLL_TO_LEFT); 1270 break; 1271 case ccui.ScrollView.MOVEDIR_RIGHT: 1272 event = (bounce ? ccui.ScrollView.EVENT_BOUNCE_RIGHT : ccui.ScrollView.EVENT_SCROLL_TO_RIGHT); 1273 break; 1274 } 1275 1276 this._dispatchEvent(event); 1277 }, 1278 1279 _processScrollingEvent: function() 1280 { 1281 this._dispatchEvent( ccui.ScrollView.EVENT_SCROLLING); 1282 }, 1283 1284 _dispatchEvent: function(event) 1285 { 1286 if(this._scrollViewEventSelector){ 1287 if (this._scrollViewEventListener) 1288 this._scrollViewEventSelector.call(this._scrollViewEventListener, this, event); 1289 else 1290 this._scrollViewEventSelector(this, event); 1291 } 1292 if(this._ccEventCallback) 1293 this._ccEventCallback(this, event); 1294 }, 1295 1296 /** 1297 * Adds callback function called ScrollView event triggered 1298 * @param {Function} selector 1299 * @param {Object} [target=] 1300 * @deprecated since v3.0, please use addEventListener instead. 1301 */ 1302 addEventListenerScrollView: function (selector, target) { 1303 this._scrollViewEventSelector = selector; 1304 this._scrollViewEventListener = target; 1305 }, 1306 1307 /** 1308 * Adds callback function called ScrollView event triggered 1309 * @param {Function} selector 1310 */ 1311 addEventListener: function(selector){ 1312 this._ccEventCallback = selector; 1313 }, 1314 1315 /** 1316 * Changes scroll _direction of ScrollView. 1317 * @param {ccui.ScrollView.DIR_NONE | ccui.ScrollView.DIR_VERTICAL | ccui.ScrollView.DIR_HORIZONTAL | ccui.ScrollView.DIR_BOTH} dir 1318 * Direction::VERTICAL means vertical scroll, Direction::HORIZONTAL means horizontal scroll 1319 */ 1320 setDirection: function (dir) { 1321 this._direction = dir; 1322 1323 if(this._scrollBarEnabled) 1324 { 1325 this._removeScrollBar(); 1326 this._initScrollBar(); 1327 } 1328 }, 1329 1330 /** 1331 * Returns scroll direction of ScrollView. 1332 * @returns {ccui.ScrollView.DIR_NONE | ccui.ScrollView.DIR_VERTICAL | ccui.ScrollView.DIR_HORIZONTAL | ccui.ScrollView.DIR_BOTH} 1333 */ 1334 getDirection: function () { 1335 return this._direction; 1336 }, 1337 1338 /** 1339 * Sets bounce enabled 1340 * @param {Boolean} enabled 1341 */ 1342 setBounceEnabled: function (enabled) { 1343 this.bounceEnabled = enabled; 1344 }, 1345 1346 /** 1347 * Returns whether bounce is enabled 1348 * @returns {boolean} 1349 */ 1350 isBounceEnabled: function () { 1351 return this.bounceEnabled; 1352 }, 1353 1354 /** 1355 * Sets inertiaScroll enabled 1356 * @param {boolean} enabled 1357 */ 1358 setInertiaScrollEnabled: function (enabled) { 1359 this.inertiaScrollEnabled = enabled; 1360 }, 1361 1362 /** 1363 * Returns whether inertiaScroll is enabled 1364 * @returns {boolean} 1365 */ 1366 isInertiaScrollEnabled: function () { 1367 return this.inertiaScrollEnabled; 1368 }, 1369 1370 /** 1371 * Toggle scroll bar enabled. 1372 * @param {boolean} enabled True if enable scroll bar, false otherwise. 1373 */ 1374 setScrollBarEnabled: function(enabled) 1375 { 1376 if(this._scrollBarEnabled === enabled) 1377 { 1378 return; 1379 } 1380 1381 if(this._scrollBarEnabled) 1382 { 1383 this._removeScrollBar(); 1384 } 1385 this._scrollBarEnabled = enabled; 1386 if(this._scrollBarEnabled) 1387 { 1388 this._initScrollBar(); 1389 } 1390 }, 1391 /** 1392 * Query scroll bar state. 1393 * @returns {boolean} True if scroll bar is enabled, false otherwise. 1394 */ 1395 isScrollBarEnabled: function() 1396 { 1397 return this._scrollBarEnabled; 1398 }, 1399 1400 /** 1401 * Set the scroll bar positions from the left-bottom corner (horizontal) and right-top corner (vertical). 1402 * @param {cc.Point} positionFromCorner The position from the left-bottom corner (horizontal) and right-top corner (vertical). 1403 */ 1404 setScrollBarPositionFromCorner: function(positionFromCorner) 1405 { 1406 if(this._direction !== ccui.ScrollView.DIR_HORIZONTAL) 1407 { 1408 this.setScrollBarPositionFromCornerForVertical(positionFromCorner); 1409 } 1410 if(this._direction !== ccui.ScrollView.DIR_VERTICAL) 1411 { 1412 this.setScrollBarPositionFromCornerForHorizontal(positionFromCorner); 1413 } 1414 }, 1415 1416 /** 1417 * Set the vertical scroll bar position from right-top corner. 1418 * @param {cc.Point} positionFromCorner The position from right-top corner 1419 */ 1420 setScrollBarPositionFromCornerForVertical: function(positionFromCorner) 1421 { 1422 cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); 1423 cc.assert(this._direction !== ccui.ScrollView.DIR_HORIZONTAL, "Scroll view doesn't have a vertical scroll bar!"); 1424 this._verticalScrollBar.setPositionFromCorner(positionFromCorner); 1425 }, 1426 1427 /** 1428 * Get the vertical scroll bar's position from right-top corner. 1429 * @returns {cc.Point} 1430 */ 1431 getScrollBarPositionFromCornerForVertical: function() 1432 { 1433 cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); 1434 cc.assert(this._direction !== ccui.ScrollView.DIR_HORIZONTAL, "Scroll view doesn't have a vertical scroll bar!"); 1435 return this._verticalScrollBar.getPositionFromCorner(); 1436 }, 1437 1438 /** 1439 * Set the horizontal scroll bar position from left-bottom corner. 1440 * @param {cc.Point} positionFromCorner The position from left-bottom corner 1441 */ 1442 setScrollBarPositionFromCornerForHorizontal: function(positionFromCorner) 1443 { 1444 cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); 1445 cc.assert(this._direction !== ccui.ScrollView.DIR_VERTICAL, "Scroll view doesn't have a horizontal scroll bar!"); 1446 this._horizontalScrollBar.setPositionFromCorner(positionFromCorner); 1447 }, 1448 1449 /** 1450 * Get the horizontal scroll bar's position from right-top corner. 1451 * @returns {cc.Point} 1452 */ 1453 getScrollBarPositionFromCornerForHorizontal: function() 1454 { 1455 cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); 1456 cc.assert(this._direction !== ccui.ScrollView.DIR_VERTICAL, "Scroll view doesn't have a horizontal scroll bar!"); 1457 return this._horizontalScrollBar.getPositionFromCorner(); 1458 }, 1459 1460 /** 1461 * Set the scroll bar's width 1462 * @param {number} width The scroll bar's width 1463 */ 1464 setScrollBarWidth: function(width) 1465 { 1466 cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); 1467 if(this._verticalScrollBar) 1468 { 1469 this._verticalScrollBar.setWidth(width); 1470 } 1471 if(this._horizontalScrollBar) 1472 { 1473 this._horizontalScrollBar.setWidth(width); 1474 } 1475 }, 1476 1477 /** 1478 * Get the scroll bar's width 1479 * @returns {number} the scroll bar's width 1480 */ 1481 getScrollBarWidth: function() 1482 { 1483 cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); 1484 if(this._verticalScrollBar) 1485 { 1486 return this._verticalScrollBar.getWidth(); 1487 } 1488 if(this._horizontalScrollBar) 1489 { 1490 return this._horizontalScrollBar.getWidth(); 1491 } 1492 return 0; 1493 }, 1494 1495 /** 1496 * Set the scroll bar's color 1497 * @param {cc.Color} color the scroll bar's color 1498 */ 1499 setScrollBarColor: function(color) 1500 { 1501 cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); 1502 if(this._verticalScrollBar) 1503 { 1504 this._verticalScrollBar.setColor(color); 1505 } 1506 if(this._horizontalScrollBar) 1507 { 1508 this._horizontalScrollBar.setColor(color); 1509 } 1510 }, 1511 1512 /** 1513 * Get the scroll bar's color 1514 * @returns {cc.Color} the scroll bar's color 1515 */ 1516 getScrollBarColor: function() 1517 { 1518 cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); 1519 if(this._verticalScrollBar) 1520 { 1521 this._verticalScrollBar.getColor(); 1522 } 1523 if(this._horizontalScrollBar) 1524 { 1525 this._horizontalScrollBar.getColor(); 1526 } 1527 return cc.color.WHITE; 1528 }, 1529 1530 /** 1531 * Set the scroll bar's opacity 1532 * @param {number} opacity the scroll bar's opacity 1533 */ 1534 setScrollBarOpacity: function(opacity) 1535 { 1536 cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); 1537 if(this._verticalScrollBar) 1538 { 1539 this._verticalScrollBar.opacity = opacity; 1540 } 1541 if(this._horizontalScrollBar) 1542 { 1543 this._horizontalScrollBar.opacity = opacity; 1544 } 1545 }, 1546 1547 /** 1548 * Get the scroll bar's opacity 1549 * @returns {number} 1550 */ 1551 getScrollBarOpacity: function() 1552 { 1553 cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); 1554 if(this._verticalScrollBar) 1555 { 1556 return this._verticalScrollBar.opacity; 1557 } 1558 if(this._horizontalScrollBar) 1559 { 1560 return this._horizontalScrollBar.opacity; 1561 } 1562 return -1; 1563 }, 1564 1565 /** 1566 * Set scroll bar auto hide state 1567 * @param {boolean} autoHideEnabled scroll bar auto hide state 1568 */ 1569 setScrollBarAutoHideEnabled: function(autoHideEnabled) 1570 { 1571 cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); 1572 if(this._verticalScrollBar) 1573 { 1574 this._verticalScrollBar.autoHideEnabled = autoHideEnabled; 1575 } 1576 if(this._horizontalScrollBar) 1577 { 1578 this._horizontalScrollBar.autoHideEnabled = autoHideEnabled; 1579 } 1580 }, 1581 1582 /** 1583 * Query scroll bar auto hide state 1584 * @returns {boolean} 1585 */ 1586 isScrollBarAutoHideEnabled: function() 1587 { 1588 cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); 1589 if(this._verticalScrollBar) 1590 { 1591 return this._verticalScrollBar.autoHideEnabled; 1592 } 1593 if(this._horizontalScrollBar) 1594 { 1595 return this._horizontalScrollBar.autoHideEnabled; 1596 } 1597 return false; 1598 }, 1599 1600 /** 1601 * Set scroll bar auto hide time 1602 * @param {number} autoHideTime scroll bar auto hide state 1603 */ 1604 setScrollBarAutoHideTime: function(autoHideTime) 1605 { 1606 cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); 1607 if(this._verticalScrollBar) 1608 { 1609 this._verticalScrollBar.autoHideTime = autoHideTime; 1610 } 1611 if(this._horizontalScrollBar) 1612 { 1613 this._horizontalScrollBar.autoHideTime = autoHideTime; 1614 } 1615 }, 1616 1617 /** 1618 * Get the scroll bar's auto hide time 1619 * @returns {number} 1620 */ 1621 getScrollBarAutoHideTime: function() 1622 { 1623 cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); 1624 if(this._verticalScrollBar) 1625 { 1626 return this._verticalScrollBar.autoHideTime; 1627 } 1628 if(this._horizontalScrollBar) 1629 { 1630 return this._horizontalScrollBar.autoHideTime; 1631 } 1632 return 0; 1633 }, 1634 1635 /** 1636 * Gets inner container of ScrollView. Inner container is the container of ScrollView's children. 1637 * @returns {ccui.Layout} 1638 */ 1639 getInnerContainer: function () { 1640 return this._innerContainer; 1641 }, 1642 1643 /** 1644 * Sets LayoutType of ccui.ScrollView. 1645 * @param {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE} type 1646 */ 1647 setLayoutType: function (type) { 1648 this._innerContainer.setLayoutType(type); 1649 }, 1650 1651 /** 1652 * Returns the layout type of ccui.ScrollView. 1653 * @returns {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE} 1654 */ 1655 getLayoutType: function () { 1656 return this._innerContainer.getLayoutType(); 1657 }, 1658 1659 _doLayout: function () { 1660 if (!this._doLayoutDirty) 1661 return; 1662 this._doLayoutDirty = false; 1663 }, 1664 1665 /** 1666 * Returns the "class name" of ccui.ScrollView. 1667 * @returns {string} 1668 */ 1669 getDescription: function () { 1670 return "ScrollView"; 1671 }, 1672 1673 _createCloneInstance: function(){ 1674 return new ccui.ScrollView(); 1675 }, 1676 1677 _copyClonedWidgetChildren: function (model) { 1678 ccui.Layout.prototype._copyClonedWidgetChildren.call(this, model); 1679 }, 1680 1681 _copySpecialProperties: function (scrollView) { 1682 if(scrollView instanceof ccui.ScrollView) { 1683 ccui.Layout.prototype._copySpecialProperties.call(this, scrollView); 1684 this.setInnerContainerSize(scrollView.getInnerContainerSize()); 1685 this.setInnerContainerPosition(scrollView.getInnerContainerPosition()); 1686 this.setDirection(scrollView._direction); 1687 1688 this._topBoundary = scrollView._topBoundary; 1689 this._bottomBoundary = scrollView._bottomBoundary; 1690 this._leftBoundary = scrollView._leftBoundary; 1691 this._rightBoundary = scrollView._rightBoundary; 1692 this._bePressed = scrollView._bePressed; 1693 this._childFocusCancelOffset = scrollView._childFocusCancelOffset; 1694 this._touchMoveDisplacements = scrollView._touchMoveDisplacements; 1695 this._touchMoveTimeDeltas = scrollView._touchMoveTimeDeltas; 1696 this._touchMovePreviousTimestamp = scrollView._touchMovePreviousTimestamp; 1697 this._autoScrolling = scrollView._autoScrolling; 1698 this._autoScrollAttenuate = scrollView._autoScrollAttenuate; 1699 this._autoScrollStartPosition = scrollView._autoScrollStartPosition; 1700 this._autoScrollTargetDelta = scrollView._autoScrollTargetDelta; 1701 this._autoScrollTotalTime = scrollView._autoScrollTotalTime; 1702 this._autoScrollAccumulatedTime = scrollView._autoScrollAccumulatedTime; 1703 this._autoScrollCurrentlyOutOfBoundary = scrollView._autoScrollCurrentlyOutOfBoundary; 1704 this._autoScrollBraking = scrollView._autoScrollBraking; 1705 this._autoScrollBrakingStartPosition = scrollView._autoScrollBrakingStartPosition; 1706 1707 this.setBounceEnabled(scrollView.bounceEnabled); 1708 this.setInertiaScrollEnabled(scrollView.inertiaScrollEnabled); 1709 1710 this._scrollViewEventListener = scrollView._scrollViewEventListener; 1711 this._scrollViewEventSelector = scrollView._scrollViewEventSelector; 1712 this._ccEventCallback = scrollView._ccEventCallback; 1713 1714 this.setScrollBarEnabled(scrollView.isScrollBarEnabled()); 1715 if(this.isScrollBarEnabled()) 1716 { 1717 if(this._direction !== ccui.ScrollView.DIR_HORIZONTAL) 1718 { 1719 this.setScrollBarPositionFromCornerForVertical(scrollView.getScrollBarPositionFromCornerForVertical()); 1720 } 1721 if(this._direction !== ccui.ScrollView.DIR_VERTICAL) 1722 { 1723 this.setScrollBarPositionFromCornerForHorizontal(scrollView.getScrollBarPositionFromCornerForHorizontal()); 1724 } 1725 this.setScrollBarWidth(scrollView.getScrollBarWidth()); 1726 this.setScrollBarColor(scrollView.getScrollBarColor()); 1727 this.setScrollBarAutoHideEnabled(scrollView.isScrollBarAutoHideEnabled()); 1728 this.setScrollBarAutoHideTime(scrollView.getScrollBarAutoHideTime()); 1729 } 1730 } 1731 }, 1732 1733 _initScrollBar: function() 1734 { 1735 if(this._direction !== ccui.ScrollView.DIR_HORIZONTAL && !this._verticalScrollBar) 1736 { 1737 this._verticalScrollBar = new ccui.ScrollViewBar(this, ccui.ScrollView.DIR_VERTICAL); 1738 this.addProtectedChild(this._verticalScrollBar, 2); 1739 } 1740 if(this._direction !== ccui.ScrollView.DIR_VERTICAL && !this._horizontalScrollBar) 1741 { 1742 this._horizontalScrollBar = new ccui.ScrollViewBar(this, ccui.ScrollView.DIR_HORIZONTAL); 1743 this.addProtectedChild(this._horizontalScrollBar, 2); 1744 } 1745 }, 1746 1747 _removeScrollBar: function() 1748 { 1749 if(this._verticalScrollBar) 1750 { 1751 this.removeProtectedChild(this._verticalScrollBar); 1752 this._verticalScrollBar = null; 1753 } 1754 if(this._horizontalScrollBar) 1755 { 1756 this.removeProtectedChild(this._horizontalScrollBar); 1757 this._horizontalScrollBar = null; 1758 } 1759 }, 1760 1761 /** 1762 * Returns a node by tag 1763 * @param {Number} tag 1764 * @returns {cc.Node} 1765 * @deprecated since v3.0, please use getChildByTag instead. 1766 */ 1767 getNodeByTag: function (tag) { 1768 return this._innerContainer.getNodeByTag(tag); 1769 }, 1770 1771 /** 1772 * Returns all nodes of inner container 1773 * @returns {Array} 1774 * @deprecated since v3.0, please use getChildren instead. 1775 */ 1776 getNodes: function () { 1777 return this._innerContainer.getNodes(); 1778 }, 1779 1780 /** 1781 * Removes a node from ccui.ScrollView. 1782 * @param {cc.Node} node 1783 * @deprecated since v3.0, please use removeChild instead. 1784 */ 1785 removeNode: function (node) { 1786 this._innerContainer.removeNode(node); 1787 }, 1788 1789 /** 1790 * Removes a node by tag 1791 * @param {Number} tag 1792 * @deprecated since v3.0, please use removeChildByTag instead. 1793 */ 1794 removeNodeByTag: function (tag) { 1795 this._innerContainer.removeNodeByTag(tag); 1796 }, 1797 1798 /** 1799 * Remove all node from ccui.ScrollView. 1800 * @deprecated since v3.0, please use removeAllChildren instead. 1801 */ 1802 removeAllNodes: function () { 1803 this._innerContainer.removeAllNodes(); 1804 }, 1805 1806 /** 1807 * Add node for scrollView 1808 * @param {cc.Node} node 1809 * @param {Number} zOrder 1810 * @param {Number} tag 1811 * @deprecated since v3.0, please use addChild instead. 1812 */ 1813 addNode: function (node, zOrder, tag) { 1814 this._innerContainer.addNode(node, zOrder, tag); 1815 } 1816 }); 1817 1818 var _p = ccui.ScrollView.prototype; 1819 1820 // Extended properties 1821 /** @expose */ 1822 _p.innerWidth; 1823 cc.defineGetterSetter(_p, "innerWidth", _p._getInnerWidth, _p._setInnerWidth); 1824 /** @expose */ 1825 _p.innerHeight; 1826 cc.defineGetterSetter(_p, "innerHeight", _p._getInnerHeight, _p._setInnerHeight); 1827 /** @expose */ 1828 _p.direction; 1829 cc.defineGetterSetter(_p, "direction", _p.getDirection, _p.setDirection); 1830 /** @expose */ 1831 _p.touchTotalTimeThreshold; 1832 cc.defineGetterSetter(_p, "touchTotalTimeThreshold", _p.getTouchTotalTimeThreshold, _p.setTouchTotalTimeThreshold); 1833 _p = null; 1834 /** 1835 * allocates and initializes a UIScrollView. 1836 * @deprecated since v3.0, please use new ccui.ScrollView() instead. 1837 * @return {ccui.ScrollView} 1838 */ 1839 ccui.ScrollView.create = function () { 1840 return new ccui.ScrollView(); 1841 }; 1842 1843 // Constants 1844 //ScrollView direction 1845 /** 1846 * The none flag of ccui.ScrollView's direction. 1847 * @constant 1848 * @type {number} 1849 */ 1850 ccui.ScrollView.DIR_NONE = 0; 1851 /** 1852 * The vertical flag of ccui.ScrollView's direction. 1853 * @constant 1854 * @type {number} 1855 */ 1856 ccui.ScrollView.DIR_VERTICAL = 1; 1857 /** 1858 * The horizontal flag of ccui.ScrollView's direction. 1859 * @constant 1860 * @type {number} 1861 */ 1862 ccui.ScrollView.DIR_HORIZONTAL = 2; 1863 /** 1864 * The both flag of ccui.ScrollView's direction. 1865 * @constant 1866 * @type {number} 1867 */ 1868 ccui.ScrollView.DIR_BOTH = 3; 1869 1870 //ScrollView event 1871 /** 1872 * The flag scroll to top of ccui.ScrollView's event. 1873 * @constant 1874 * @type {number} 1875 */ 1876 ccui.ScrollView.EVENT_SCROLL_TO_TOP = 0; 1877 /** 1878 * The flag scroll to bottom of ccui.ScrollView's event. 1879 * @constant 1880 * @type {number} 1881 */ 1882 ccui.ScrollView.EVENT_SCROLL_TO_BOTTOM = 1; 1883 /** 1884 * The flag scroll to left of ccui.ScrollView's event. 1885 * @constant 1886 * @type {number} 1887 */ 1888 ccui.ScrollView.EVENT_SCROLL_TO_LEFT = 2; 1889 /** 1890 * The flag scroll to right of ccui.ScrollView's event. 1891 * @constant 1892 * @type {number} 1893 */ 1894 ccui.ScrollView.EVENT_SCROLL_TO_RIGHT = 3; 1895 /** 1896 * The scrolling flag of ccui.ScrollView's event. 1897 * @constant 1898 * @type {number} 1899 */ 1900 ccui.ScrollView.EVENT_SCROLLING = 4; 1901 /** 1902 * The flag bounce top of ccui.ScrollView's event. 1903 * @constant 1904 * @type {number} 1905 */ 1906 ccui.ScrollView.EVENT_BOUNCE_TOP = 5; 1907 /** 1908 * The flag bounce bottom of ccui.ScrollView's event. 1909 * @constant 1910 * @type {number} 1911 */ 1912 ccui.ScrollView.EVENT_BOUNCE_BOTTOM = 6; 1913 /** 1914 * The flag bounce left of ccui.ScrollView's event. 1915 * @constant 1916 * @type {number} 1917 */ 1918 ccui.ScrollView.EVENT_BOUNCE_LEFT = 7; 1919 /** 1920 * The flag bounce right of ccui.ScrollView's event. 1921 * @constant 1922 * @type {number} 1923 */ 1924 ccui.ScrollView.EVENT_BOUNCE_RIGHT = 8; 1925 /** 1926 * The flag container moved of ccui.ScrollView's event. 1927 * @constant 1928 * @type {number} 1929 */ 1930 ccui.ScrollView.EVENT_CONTAINER_MOVED = 9; 1931 /** 1932 * The flag autoscroll ended of ccui.ScrollView's event. 1933 * @constant 1934 * @type {number} 1935 */ 1936 ccui.ScrollView.EVENT_AUTOSCROLL_ENDED = 10; 1937 1938 /** 1939 * @ignore 1940 */ 1941 1942 ccui.ScrollView.MOVEDIR_TOP = 0; 1943 ccui.ScrollView.MOVEDIR_BOTTOM = 1; 1944 ccui.ScrollView.MOVEDIR_LEFT = 2; 1945 ccui.ScrollView.MOVEDIR_RIGHT = 3; 1946