goog.provide('lime.ui.Scroller');
goog.require('lime.Sprite');
goog.require('lime.animation.MoveTo');
/**
* @constructor
* @extends lime.Sprite
*/
lime.ui.Scroller = function() {
goog.base(this);
//need default size for autoresize
this.setSize(100, 100);
this.clipper = new lime.Sprite().setFill('#c00').setSize(100, 100).
setAutoResize(lime.AutoResize.ALL);
this.appendChild(this.clipper);
this.setMask(this.clipper);
this.moving_ = new lime.Layer();
lime.Node.prototype.appendChild.call(this, this.moving_);
goog.events.listen(this, ['mousedown', 'touchstart'],
this.downHandler_, false, this);
this.setDirection(lime.ui.Scroller.Direction.HORIZONTAL);
};
goog.inherits(lime.ui.Scroller, lime.Sprite);
/**
* Offset that can be dragged over the edge
* @const
* @type number
*/
lime.ui.Scroller.OFFSET = 250;
/**
* Factor to slow down if over the edge
* @const
* @type number
*/
lime.ui.Scroller.OFFSET_LAG = .4;
/**
* How fast to slow down
* @const
* @type number
*/
lime.ui.Scroller.FRICTION = .95;
/**
* Directions of the scroller.
* @enum number
*/
lime.ui.Scroller.Direction = {
HORIZONTAL: 0,
VERTICAL: 1
};
/**
* Cleans up event listeners and references
*/
lime.ui.Scroller.prototype.dispose = function() {
if (this.event) {
this.event.disposeInternal();
this.event = null;
}
if (this.clipper) {
goog.events.removeAll(this.clipper);
this.clipper.maskTarget_ = null;
this.clipper.targetNode = null;
this.clipper = null;
}
if (this.moving_) {
goog.events.removeAll(this.moving_);
this.moving_.removeAllChildren();
this.moving_ = null;
}
this.mask_ = null;
this.activeMask_ = null;
goog.base(this, 'dispose');
};
/**
* Returns the direction of the scroller (horizontal/vertical)
* @return {lime.ui.Scroller.Direction} Scroll direction.
*/
lime.ui.Scroller.prototype.getDirection = function() {
return this.direction_;
};
/**
* Set the direction of the scroller (horizontal/vertical)
* @param {lime.ui.Scroller.Direction} direction Direction.
* @return {lime.ui.Scroller} object itself.
*/
lime.ui.Scroller.prototype.setDirection = function(direction) {
this.direction_ = direction;
return this;
};
/**
* @inheritDoc
* @see lime.Node#setAnchorPoint
*/
lime.ui.Scroller.prototype.setAnchorPoint = function() {
if (this.clipper) {
this.clipper.setAnchorPoint.apply(this.clipper, arguments);
}
return lime.Node.prototype.setAnchorPoint.apply(this, arguments);
};
/**
* @inheritDoc
* @see lime.Node#appendChild
*/
lime.ui.Scroller.prototype.appendChild = function() {
if (this.moving_) return this.moving_.appendChild.apply(
this.moving_, arguments);
return lime.Node.prototype.appendChild.apply(this, arguments);
};
/**
* @inheritDoc
* @see lime.Node#removeChild
*/
lime.ui.Scroller.prototype.removeChild = function() {
if (this.moving_) return this.moving_.removeChild.apply(
this.moving_, arguments);
return lime.Node.prototype.removeChild.apply(this, arguments);
};
/**
* Measure the contents of the scroller and set
* up high and low limits.
*/
lime.ui.Scroller.prototype.measureLimits = function() {
var measure = this.moving_.measureContents();
if (this.getDirection() == lime.ui.Scroller.Direction.HORIZONTAL) {
this.downy = this.moving_.getPosition().y;
var max = this.getSize().width;
this.LOW = -measure.left;
this.HIGH = Math.min(max - measure.right, -measure.left);
var diff = (measure.right - measure.left) - max;
} else {
this.downx = this.moving_.getPosition().x;
max = this.getSize().height;
this.LOW = -measure.top;
this.HIGH = Math.min(max - measure.bottom, -measure.top);
diff = (measure.bottom - measure.top) - max;
}
if (diff > 0) {
this.LOW -= diff;
this.HIGH += diff;
}
};
/**
* Scroll to specific offset in pixels
* @param {number} offset Offset in pixels.
* @param {number=} opt_duration Animation duration.
*/
lime.ui.Scroller.prototype.scrollTo = function(offset, opt_duration) {
var pos = this.moving_.getPosition().clone();
var duration = opt_duration || 0;
this.measureLimits();
if (offset < 0) offset = 0;
if (this.getDirection() == lime.ui.Scroller.Direction.HORIZONTAL) {
pos.x = this.HIGH - offset;
if (pos.x < this.LOW) pos.x = this.LOW;
} else {
pos.y = this.HIGH - offset;
if (pos.y < this.LOW) pos.y = this.LOW;
}
if (duration) {
this.moving_.runAction(new lime.animation.MoveTo(pos.x, pos.y).setDuration(duration).enableOptimizations().setEasing(lime.animation.getEasingFunction(.19, .6, .35, .97)));
} else this.moving_.setPosition(pos);
};
/**
* Handle down events
* @private
* @param {lime.events.Event} e Event.
*/
lime.ui.Scroller.prototype.downHandler_ = function(e) {
if (this.ismove) return;
e.position = this.localToNode(e.position, this.moving_);
this.downx = e.position.x;
this.downy = e.position.y;
this.v = 0;
this.ismove = 1;
if (this.getDirection() == lime.ui.Scroller.Direction.HORIZONTAL) {
this.oldvalue = this.posvalue = this.moving_.getPosition().x;
} else {
this.oldvalue = this.posvalue = this.moving_.getPosition().y;
}
this.measureLimits();
lime.animation.actionManager.stopAll(this.moving_);
//this.moving_.setPosition(p);
lime.scheduleManager.schedule(this.captureVelocity_, this);
e.swallow(['touchmove', 'mousemove'], this.moveHandler_);
e.swallow(['touchend', 'mouseup', 'touchcancel'], this.upHandler_);
this.event = e.clone();
};
/**
* Capture the scrolling velocity to get some throwing
* motion after release.
* @private
*/
lime.ui.Scroller.prototype.captureVelocity_ = function() {
if (this.ismove) {
this.v = (this.posvalue - this.oldvalue) * 1.05;
this.oldvalue = this.posvalue;
}
this.v *= lime.ui.Scroller.FRICTION;
};
/**
* Cancel all current movement events.
*/
lime.ui.Scroller.prototype.cancelEvents = function() {
if (this.event) {
this.event.release();
}
this.ismove = 0;
};
/**
* Handle move events.
* @private
* @param {lime.events.Event} e Event.
*/
lime.ui.Scroller.prototype.moveHandler_ = function(e) {
var pos = e.position.clone(),
dir = this.getDirection(),
activeval;
if (dir == lime.ui.Scroller.Direction.HORIZONTAL) {
pos.x -= this.downx;
pos.y = this.downy;
activeval = pos.x;
} else {
pos.x = this.downx;
pos.y -= this.downy;
activeval = pos.y;
}
if (activeval < this.LOW) {
var diff = this.LOW - activeval;
if (diff > lime.ui.Scroller.OFFSET) diff = lime.ui.Scroller.OFFSET;
activeval = this.LOW - diff * lime.ui.Scroller.OFFSET_LAG;
}
if (activeval > this.HIGH) {
diff = activeval - this.HIGH;
if (diff > lime.ui.Scroller.OFFSET) diff = lime.ui.Scroller.OFFSET;
activeval = this.HIGH + diff * lime.ui.Scroller.OFFSET_LAG;
}
this.posvalue = activeval;
if (dir == lime.ui.Scroller.Direction.HORIZONTAL) {
pos.x = activeval;
} else {
pos.y = activeval;
}
this.moving_.setPosition(pos);
};
/**
* Handle release(up) events.
* @private
* @param {lime.events.Event} e Event.
*/
lime.ui.Scroller.prototype.upHandler_ = function(e) {
var pos = e.position.clone(),
dir = this.getDirection(),
activeval;
if (dir == lime.ui.Scroller.Direction.HORIZONTAL) {
pos.x -= this.downx;
pos.y = this.downy;
activeval = pos.x;
} else {
pos.x = this.downx;
pos.y -= this.downy;
activeval = pos.y;
}
lime.scheduleManager.unschedule(this.captureVelocity_, this);
var k = Math.log(0.5 / Math.abs(this.v)) /
Math.log(lime.ui.Scroller.FRICTION),
duration = k / 30,
endpos = (Math.abs(this.v) *
(Math.pow(lime.ui.Scroller.FRICTION, k) - 1)) /
(lime.ui.Scroller.FRICTION - 1) * (this.v > 0 ? 1 : -1);
activeval += endpos;
this.ismove = 0;
if (this.v != 0) {
var diff = endpos;
if (activeval < this.LOW) {
diff = this.LOW - (activeval - endpos);
activeval = this.LOW;
}
if (activeval > this.HIGH) {
diff = this.HIGH - (activeval - endpos);
activeval = this.HIGH;
}
//console.log(diff,endpos);
duration *= (diff / endpos);
}
if (this.oldvalue < this.LOW) {
activeval = this.LOW;
duration = .3;
}
if (this.oldvalue > this.HIGH) {
activeval = this.HIGH;
duration = .3;
}
if (dir == lime.ui.Scroller.Direction.HORIZONTAL) {
pos.x = activeval;
} else {
pos.y = activeval;
}
if (Math.abs(duration) < 10) {
this.moving_.runAction(new lime.animation.MoveTo(pos.x, pos.y).setDuration(duration).enableOptimizations().setEasing(lime.animation.getEasingFunction(.19, .6, .35, .97)));
}
};