I'm playing with replicating the recent, 30-year Apple Mac retrospective in pure CSS and Javascript for a small timeline of projects. I have the basic layout of the first full screen hero and the scalloped, expand-on-hover working appropriately. But the smooth scrolling of the timeline in the second half of the screen isn't working, even very slow scrolling is obviously jittery in Google Chrome 32.0.1700.102 on Mac OS X. You can download a folder with a single index.html
and the necessary CSS and JS here.
Specifically, my two questions are:
What is a pure CSS/JavaScript solution to fixing this smooth scrolling? I'd appreciate something which debugged this example rather than pointed me to another, working one.
And related, what could/should I do to approach debugging this to isolate the problem? I naïvely tried collecting a JavaScript CPU Profile, but nothing jumped out as needing attention.
Basic Structure
The timeline is structured as a nav
containing an ordered list, each li
containing project, i.e.
<nav id='timeline'>
<ol>
<li class='project' id='zero'>
<div class='description'>
<h2> Project 0 </h2>
<span> The project that changed everything </span>
<div class='icon'></div>
</div> <!-- div.description -->
</li> <!-- li.project#zero -->
</ol>
</nav> <!-- nav#timeline -->
I have a simple event loop to detect global mouse position and handle scroll detection,
// 20ms event loop to update a global mouseX, mouseY position and handle scroll detection
var mouseX = null;
var mouseY = null;
var scrollTimeline = null;
var updateInterval = 10;
var scrolling = false;
window.onmousemove = function(event) {
mouseX = event.clientX;
mouseY = event.clientY;
if (!scrollTimeline) {
scrollTimeline = window.setInterval(scroll, updateInterval);
}
};
Which in turn calls a simple scroll handler every 10ms,
function scroll(event) {
var buffer = window.innerWidth/4;
var distanceToCenter = Math.abs(window.innerWidth/2-mouseX);
var speed = distanceToCenter/(window.innerWidth/2);
if (mouseX < buffer) {
scrolling = true;
scrollLeft(speed);
}
else if ((window.innerWidth - mouseX) < buffer) {
scrolling = true;
scrollRight(speed);
}
else {
scrolling = false;
window.clearInterval(scrollTimeline);
scrollTimeline = null;
}
}
All the actual scrolling is accomplished by adjusting the left
attribute of the containing nav
via two functions, scrollRight
and scrollLeft
, called with a speed
argument depending on the mouse position.
function scrollRight(speed) {
var leftPixels = parseInt(getStyleProp(timeline, 'left'), 10);
var toShift = Math.pow(speed,3)*updateInterval;
var newLeft = leftPixels - toShift;
if (newLeft >= -1400 && newLeft <= 0) {
timeline.style.left = newLeft + 'px';
}
}
(getStyleProp
is a simple utility function for getting the computed left
attribute if it hasn't been explicitly set which I copied from this answer):
// Utility function to grab style properties when unset
function getStyleProp(elem, prop){
if(window.getComputedStyle)
return window.getComputedStyle(elem, null).getPropertyValue(prop);
else if(elem.currentStyle) return elem.currentStyle[prop]; //IE
}
What I've tried
So, with all of those basics out of the way, I've tried:
- Removing some of the CSS transitions that create the scalloped effect
- Using one image instead of six
- Adjusting
left
in a loop, one pixel at a time, instead of in small jumps - Removing any of the contained text and their transitions.
- And removing any contained
li
's in thenav
--this solved the problem, but I'm not sure why/how that would be causing the observed jitter
Thanks!