Hi Jack.
I have a scrolling javascript library I'm working on. The gist of the project is to micromanage scroll and requestAnimFrame events to ensure that objects that control DOM elements that aren't currently in the viewport aren't executing.
The code goes something like this:
var scrollable = {
element: $('#foo_div'),
scroll: function(e){
console.log("the page was scrolled");
},
render: function(e){
console.log("render animation frame is active");
}
};
ScrollLibrary.watch(scrollable);
/*
If scrollable.element is on the screen, when the user scrolls the page, scrollable.scroll() fires.
If scrollable.element is on the screen, the scrollable.render() method fires
when scrollable.element is not on the screen, scrollable.scroll() and scrollable.render() are ignored
*/
This is made slightly more complex by a feature that I've implemented to help control what happens when you scroll.
As a DOM element makes its way from the bottom of the viewport to the top of the viewport, the scrollable.scroll() method is passed a number between -1 (bottom of the screen) and 1 (top of the screen).
var scrollable = {
element: $('#foo_div'),
scroll: function(e){
console.log("offset = " + e);
},
render: function(e){
console.log("render animation frame is active");
}
};
ScrollLibrary.watch(scrollable);
What that allows me to do is proportionally scale between two values. Given a map method like this
//mathutils for proportional scaling
var mathutils = {
normalize: function ($value, $min, $max) {
return ($value - $min) / ($max - $min);
},
interpolate: function ($normValue, $min, $max) {
return $min + ($max - $min) * $normValue;
},
map: function ($value, $min1, $max1, $min2, $max2) {
if ($value < $min1){
$value = $min1;
}
if($value > $max1){
$value = $max1;
}
var res = this.interpolate(this.normalize($value, $min1, $max1), $min2, $max2);
return res;
}
};
I can proportionally manage where on the screen elements land while scrolling
var scrollable = {
element: $('#foo_div'),
scroll: function(e){
var elem_left = mathutils.map(e, -1, 1, 0, 100);
this.element.css('left', elem_left);
},
render: function(e){
console.log("render animation frame is active");
}
};
ScrollLibrary.watch(scrollable);
So now I've set the element's left value to 0 when it has just come onto the bottom of the screen, and 100 when it has just left the top of the screen.
Taking this a bit further, and to finally get to the point, I can use this same mapping logic to seek to a percentage of a tween.
//create circle
var $circle = $('.circle');
//tween it
var tween = TweenLite.to($circle, 2, {
top: "200"
});
tween.pause();
controlTween(tween, 1);
function controlTween(tween, percentage){
var tDuration = tween.duration();
var timecodeByPercent = mathutils.map(percentage, 0, 1, 0, tDuration);
tween.seek(timecodeByPercent);
}
http://jsbin.com/edewem/3/edit
So by adding that along with the scrolling bit, I can tween any element as I scroll from the top to the bottom. Which really opens some doors to what the web can do, and makes rich scrolling sites pretty amazing.
So, rather than calling pause on n number of tweening elements in the system, I'd rather just not send the render event to them. When they are back in the viewport they would just pick back up where they left off like nothing happened.
If you've made it this far, Here's an example of it all working
http://digitalsurgeonsdev.com/aaron/
The bottom panel is seeking to a percentage of a TimelineMax animation sequence of the InfinityBlade guy chopping the head off of a monster and blood spraying out.