Working scroll view, a little buggy still

This commit is contained in:
Max Lynch
2013-10-23 16:54:36 -05:00
parent edca023980
commit 967813ddd7
3 changed files with 256 additions and 36 deletions

123
dist/js/ionic.js vendored
View File

@ -2490,11 +2490,15 @@ window.ionic = {
// Extend the options with our defaults // Extend the options with our defaults
ionic.Utils.extend(opts, { ionic.Utils.extend(opts, {
decelerationRate: ionic.views.Scroll.prototype.DECEL_RATE_NORMAL decelerationRate: ionic.views.Scroll.prototype.DECEL_RATE_NORMAL,
dragThresholdY: 10,
resistance: 2
}); });
this.el = opts.el; this.el = opts.el;
this.decelerationRate = opts.decelerationRate this.decelerationRate = opts.decelerationRate;
this.dragThresholdY = opts.dragThresholdY;
this.resistance = opts.resistance;
// Listen for drag and release events // Listen for drag and release events
window.ionic.onGesture('drag', function(e) { window.ionic.onGesture('drag', function(e) {
@ -2509,9 +2513,33 @@ window.ionic = {
DECEL_RATE_NORMAL: 0.998, DECEL_RATE_NORMAL: 0.998,
DECEL_RATE_FAST: 0.99, DECEL_RATE_FAST: 0.99,
/**
* Scroll to the given X and Y point, taking
* the given amount of time, with the given
* easing function defined as a CSS3 timing function.
*
* @param {float} the x position to scroll to (CURRENTLY NOT SUPPORTED!)
* @param {float} the y position to scroll to
* @param {float} the time to take scrolling to the new position
* @param {easing} the animation function to use for easing
*/
scrollTo: function(x, y, time, easing) {
easing = easing || 'cubic-bezier(0.1, 0.57, 0.1, 1)';
var el = this.el;
el.style.webkitTransitionTimingFunction = easing;
el.style.webkitTransitionDuration = time;
el.style.webkitTransform = 'translate3d(0,' + y + 'px, 0)';
},
_initDrag: function() { _initDrag: function() {
this._isDragging = false; this._isDragging = false;
this._drag = null; this._drag = null;
this.el.classList.remove('scroll-scrolling');
this.el.style.webkitTransitionDuration = '0';
}, },
/** /**
@ -2523,12 +2551,12 @@ window.ionic = {
this._initDrag(); this._initDrag();
this.el.classList.remove('scroll-scrolling'); var scrollTop = parseFloat(this.el.style.webkitTransform.replace('translate3d(', '').split(',')[1]) || 0;
var scrollTop = parseFloat(this.el.scrollTop);
this._drag = { this._drag = {
startY: scrollTop startY: scrollTop,
resist: 1,
startTime: +(new Date)
}; };
}, },
@ -2545,25 +2573,100 @@ window.ionic = {
if(!_this._drag) { if(!_this._drag) {
_this._startDrag(e); _this._startDrag(e);
} }
console.log('At scroll top', _this.el.scrollTop, e.gesture.deltaY);
_this.el.style.webkitTransform = 'translate3d(0,' + e.gesture.deltaY + 'px, 0)'; // Stop any default events during the drag
e.preventDefault();
// Check if we should start dragging. Check if we've dragged past the threshold.
if(!_this._isDragging && (Math.abs(e.gesture.deltaY) > _this.dragThresholdY)) {
_this._isDragging = true;
}
if(_this._isDragging) {
var totalHeight = _this.el.offsetHeight;
var parentHeight = _this.el.parentNode.offsetHeight;
var newY = _this._drag.startY + e.gesture.deltaY;
// Check if the dragging is beyond the bottom or top
if(newY > 0 || (-newY + parentHeight) > totalHeight) {
// Rubber band
newY = newY + e.gesture.deltaY / (-_this.resistance);
}
// Update the new translated Y point of the container
_this.el.style.webkitTransform = 'translate3d(0,' + newY + 'px, 0)';
}
}); });
}, },
_handleEndDrag: function(e) { _handleEndDrag: function(e) {
var _this = this; var _this = this;
window.requestAnimationFrame(function() {
// We didn't have a drag, so just init and leave // We didn't have a drag, so just init and leave
if(!_this._drag) { if(!_this._drag) {
_this._initDrag(); _this._initDrag();
return; return;
} }
// Animate to the finishing point
_this._animateToStop(e);
// Cleanup
_this._initDrag(); _this._initDrag();
},
_animateToStop: function(e) {
var _this = this;
var totalHeight = this.el.offsetHeight;
var parentHeight = this.el.parentNode.offsetHeight;
var scrollTop = parseFloat(this.el.style.webkitTransform.replace('translate3d(', '').split(',')[1]) || 0;
var duration = +(new Date) - this._drag.startTime;
var newY = scrollTop;
if(e.gesture.velocityY) {
// Get the final resting point
var vy = e.gesture.velocityY;
var speed = Math.abs(e.gesture.deltaY) / duration;
//newY = newY + (vy * vy) / (0.05 * this.decelerationRate);
var destination = newY + ( speed * speed ) / ( 2 * (1-_this.decelerationRate)) * ( e.gesture.deltaY < 0 ? -1 : 1 );
var dur = speed / (1-_this.decelerationRate);
if((-destination + parentHeight) > totalHeight) {
destination = -(totalHeight - parentHeight);
} else if(destination > 0) {
destination = 0;
}
console.log('Ending at velocity and point', speed, vy, destination, dur);
var el = this.el;
window.requestAnimationFrame(function() {
_this.scrollTo(0, destination, dur);
}); });
} }
/*
// Check if the dragging is beyond the bottom or top
*/
/*
setTimeout(function() {
window.requestAnimationFrame(function() {
_this.el.style.webkitTransform = 'translate3d(0,' + newY + 'px, 0)';
});
}, 50);
*/
// Turn on animation
this.el.classList.add('scroll-scrolling');
}
}; };
})(ionic); })(ionic);

View File

@ -6,11 +6,15 @@
// Extend the options with our defaults // Extend the options with our defaults
ionic.Utils.extend(opts, { ionic.Utils.extend(opts, {
decelerationRate: ionic.views.Scroll.prototype.DECEL_RATE_NORMAL decelerationRate: ionic.views.Scroll.prototype.DECEL_RATE_NORMAL,
dragThresholdY: 10,
resistance: 2
}); });
this.el = opts.el; this.el = opts.el;
this.decelerationRate = opts.decelerationRate this.decelerationRate = opts.decelerationRate;
this.dragThresholdY = opts.dragThresholdY;
this.resistance = opts.resistance;
// Listen for drag and release events // Listen for drag and release events
window.ionic.onGesture('drag', function(e) { window.ionic.onGesture('drag', function(e) {
@ -25,9 +29,33 @@
DECEL_RATE_NORMAL: 0.998, DECEL_RATE_NORMAL: 0.998,
DECEL_RATE_FAST: 0.99, DECEL_RATE_FAST: 0.99,
/**
* Scroll to the given X and Y point, taking
* the given amount of time, with the given
* easing function defined as a CSS3 timing function.
*
* @param {float} the x position to scroll to (CURRENTLY NOT SUPPORTED!)
* @param {float} the y position to scroll to
* @param {float} the time to take scrolling to the new position
* @param {easing} the animation function to use for easing
*/
scrollTo: function(x, y, time, easing) {
easing = easing || 'cubic-bezier(0.1, 0.57, 0.1, 1)';
var el = this.el;
el.style.webkitTransitionTimingFunction = easing;
el.style.webkitTransitionDuration = time;
el.style.webkitTransform = 'translate3d(0,' + y + 'px, 0)';
},
_initDrag: function() { _initDrag: function() {
this._isDragging = false; this._isDragging = false;
this._drag = null; this._drag = null;
this.el.classList.remove('scroll-scrolling');
this.el.style.webkitTransitionDuration = '0';
}, },
/** /**
@ -39,12 +67,12 @@
this._initDrag(); this._initDrag();
this.el.classList.remove('scroll-scrolling'); var scrollTop = parseFloat(this.el.style.webkitTransform.replace('translate3d(', '').split(',')[1]) || 0;
var scrollTop = parseFloat(this.el.scrollTop);
this._drag = { this._drag = {
startY: scrollTop startY: scrollTop,
resist: 1,
startTime: +(new Date)
}; };
}, },
@ -61,25 +89,100 @@
if(!_this._drag) { if(!_this._drag) {
_this._startDrag(e); _this._startDrag(e);
} }
console.log('At scroll top', _this.el.scrollTop, e.gesture.deltaY);
_this.el.style.webkitTransform = 'translate3d(0,' + e.gesture.deltaY + 'px, 0)'; // Stop any default events during the drag
e.preventDefault();
// Check if we should start dragging. Check if we've dragged past the threshold.
if(!_this._isDragging && (Math.abs(e.gesture.deltaY) > _this.dragThresholdY)) {
_this._isDragging = true;
}
if(_this._isDragging) {
var totalHeight = _this.el.offsetHeight;
var parentHeight = _this.el.parentNode.offsetHeight;
var newY = _this._drag.startY + e.gesture.deltaY;
// Check if the dragging is beyond the bottom or top
if(newY > 0 || (-newY + parentHeight) > totalHeight) {
// Rubber band
newY = newY + e.gesture.deltaY / (-_this.resistance);
}
// Update the new translated Y point of the container
_this.el.style.webkitTransform = 'translate3d(0,' + newY + 'px, 0)';
}
}); });
}, },
_handleEndDrag: function(e) { _handleEndDrag: function(e) {
var _this = this; var _this = this;
window.requestAnimationFrame(function() {
// We didn't have a drag, so just init and leave // We didn't have a drag, so just init and leave
if(!_this._drag) { if(!_this._drag) {
_this._initDrag(); _this._initDrag();
return; return;
} }
// Animate to the finishing point
_this._animateToStop(e);
// Cleanup
_this._initDrag(); _this._initDrag();
},
_animateToStop: function(e) {
var _this = this;
var totalHeight = this.el.offsetHeight;
var parentHeight = this.el.parentNode.offsetHeight;
var scrollTop = parseFloat(this.el.style.webkitTransform.replace('translate3d(', '').split(',')[1]) || 0;
var duration = +(new Date) - this._drag.startTime;
var newY = scrollTop;
if(e.gesture.velocityY) {
// Get the final resting point
var vy = e.gesture.velocityY;
var speed = Math.abs(e.gesture.deltaY) / duration;
//newY = newY + (vy * vy) / (0.05 * this.decelerationRate);
var destination = newY + ( speed * speed ) / ( 2 * (1-_this.decelerationRate)) * ( e.gesture.deltaY < 0 ? -1 : 1 );
var dur = speed / (1-_this.decelerationRate);
if((-destination + parentHeight) > totalHeight) {
destination = -(totalHeight - parentHeight);
} else if(destination > 0) {
destination = 0;
}
console.log('Ending at velocity and point', speed, vy, destination, dur);
var el = this.el;
window.requestAnimationFrame(function() {
_this.scrollTo(0, destination, dur);
}); });
} }
/*
// Check if the dragging is beyond the bottom or top
*/
/*
setTimeout(function() {
window.requestAnimationFrame(function() {
_this.el.style.webkitTransform = 'translate3d(0,' + newY + 'px, 0)';
});
}, 50);
*/
// Turn on animation
this.el.classList.add('scroll-scrolling');
}
}; };
})(ionic); })(ionic);

View File

@ -6,23 +6,37 @@
<!-- Sets initial viewport load and disables zooming --> <!-- Sets initial viewport load and disables zooming -->
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
<link href="../dist/css/ionic.css" rel="stylesheet"> <link href="../dist/css/ionic.css" rel="stylesheet">
<style>
#filler {
border-top: 10px solid red;
border-bottom: 10px solid red;
}
</style>
</head> </head>
<body> <body>
<section class="view-full">
<section>
<div class="bar bar-header bar-secondary"> <div class="bar bar-header bar-secondary">
<a href="#" class="button button-danger button-clear">Edit</a> <a href="#" class="button button-danger button-clear">Edit</a>
<h1 class="title">Scroll Me</h1> <h1 class="title">Scroll Me</h1>
<a href="#" class="button button-danger button-clear">Delete</a> <a href="#" class="button button-danger button-clear">Delete</a>
</div> </div>
<div id="scroller" class="scroll"> <div id="scroller" class="scroll" style="margin-top:44px">
<div style="height: 4000px; background: url('tree_bark.png') repeat;"></div> <!--<div id="filler" style="height: 1400px; background: url('tree_bark.png') repeat;"></div>-->
<ul class="list">
</ul>
</div> </div>
</section> </section>
<script src="../dist/js/ionic.js"></script> <script src="../dist/js/ionic.js"></script>
<script> <script>
var s = document.getElementById('scroller'); var s = document.getElementById('scroller');
for(var i = 0; i < 100; i++) {
var li = document.createElement('li');
li.className = 'list-item';
li.innerHTML = 'Item ' + i;
s.firstElementChild.appendChild(li);
}
var scroll = new ionic.views.Scroll({ var scroll = new ionic.views.Scroll({
el: s el: s
}); });