1

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:

  1. 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.

  2. 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.

Screenshot of the <code>nav</code> with overlapping project <code>li</code>'s

<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 the nav--this solved the problem, but I'm not sure why/how that would be causing the observed jitter

Thanks!

Community
  • 1
  • 1
aresnick
  • 1,635
  • 12
  • 24

1 Answers1

0

Turns out the original jitter was because of the overhead of using the saturate transform in CSS. I found a far better solution using requestAnimationFrame.

aresnick
  • 1,635
  • 12
  • 24