Skip to content

Instantly share code, notes, and snippets.

@jimkang
Last active August 29, 2015 14:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jimkang/e318dfad9c798a456ded to your computer and use it in GitHub Desktop.
Save jimkang/e318dfad9c798a456ded to your computer and use it in GitHub Desktop.
Simple inertial scrolling with d3.timer and d3.ease.

Here's an example of inertial scrolling without a lot of elaborate plugin business going all over the place. (If you're viewing this on bl.ocks, it's a good idea to open the example in a new window so that you can scroll around freely.)

The scroll position on web pages can be controlled by simply setting document.body.scrollTop. Other than that there's only a couple of things you need for inertial scrolling:

  • A function that tells you how far to move the scroll position based on the time. We want one that gives the impression of inertia, so it'd dictate relatively slow movement at first, then ramp up, then slow down. d3.ease provides a cubic function like the easeInOutCubic here.

  • A timer that updates at intervals so that you can call the easing function to figure out where the scroll position should be and update the scroll position.

That's what the example does. If you want to keep your code as small as possible, you can use D3 Smash to make yourself a little build of D3 that includes only the transition module, which is where d3.timer and d3.ease live.

If you want to keep it really small, you can just grab a cubic ease-in-ease-out function from somewhere and use plain JavaScript's setInterval..

<html>
<head>
<title>Simple inertial scrolling with D3</title>
</head>
<style>
body {
display: flex;
flex-direction: column;
align-items: center;
font-size: 24px;
}
#container {
display: flex;
flex-direction: column;
max-width: 20em;
align-contents: center;
}
.item {
text-align: center;
padding: 1em;
}
.button {
background-color: rgb(234, 106, 68);
text-align: center;
padding: 0.5em;
border-radius: 0.5em;
box-shadow: hsla(240, 30%, 30%, 0.15) 2px 2px;
color: #eee;
margin: 0.2em;
cursor: pointer;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<div id="container">
<div id="top-element" class="scroll-to-middle button">Scroll to a middle element</div>
<div class="scroll-to-bottom button">Scroll to the bottom element</div>
<div class="item">Hooligan</div>
<div class="item">Muscleman</div>
<div class="item">Tough Guy</div>
<div class="item">Bullyboy</div>
<div class="item">Tearaway</div>
<div class="item">Plug-Ugly</div>
<div class="item">Chav</div>
<div class="item">Muscle</div>
<div class="item">Skinhead</div>
<div class="item">Skinhead (10th level)</div>
<div class="item">Yob</div>
<div class="item">Yob of the Body</div>
<div class="item">Yob of Feelings</div>
<div class="item">Yob of the Mind</div>
<div class="item">Yob of the Mind (15th level)</div>
<div class="item">Yob of the Mind (16th level)</div>
<hr></hr>
<div class="item">Technologist</div>
<div class="item">Hacker</div>
<div class="item">Cyberpunk</div>
<div class="item">Applied Scientist</div>
<div class="item">Cyber-Terrorist</div>
<div class="item">Engineer</div>
<div class="item">Computer User</div>
<div class="scroll-to-top button">Scroll to the top element</div>
<div class="item" id="middle-element">Middle Element</div>
<div class="scroll-to-bottom button">Scroll to the bottom element</div>
<div class="item">Cracker</div>
<div class="item">Programmer</div>
<div class="item">Programmer (10th level)</div>
<div class="item">Chief Programmer</div>
<div class="item">Master Programmer</div>
<div class="item">Grandmaster Programmer</div>
<div class="item">Grandmaster Programmer (14th level)</div>
<hr></hr>
<div class="item">Bun</div>
<div class="item">Bunny</div>
<div class="item">Sciurine</div>
<div class="item">Con</div>
<div class="item">Rodent</div>
<div class="item">Rodent (6th level)</div>
<div class="item">Rodent (7th level)</div>
<div class="item">Rodent (8th level)</div>
<div class="item">Rodent (9th level)</div>
<div class="item">Squirrel</div>
<div class="item">Squirrel (11th level)</div>
<div class="item">Squirrel (12th level)</div>
<div class="item">Expert Squirrel</div>
<div class="item">Prime Squirrel</div>
<div class="scroll-to-middle button">Scroll to a middle element</div>
<div id="bottom-element" class="scroll-to-top button">Scroll to the top element</div>
</div>
<script>
var easingFn = d3.ease('cubic');
function scrollTo(toTop, time) {
var fromTop = document.body.scrollTop;
var scrollDistance = toTop - fromTop;
var scrolledTop = 0;
d3.timer(updateScrollTop);
function updateScrollTop(elapsed) {
var portion = easingFn(elapsed * 1.0 /time);
var scrollChange = scrollDistance * portion;
document.body.scrollTop = fromTop + scrollChange;
// Stop the timer by returning true if we've scrolled as far as requested.
if (scrollDistance < 0 && document.body.scrollTop <= toTop) {
return true;
}
if (scrollDistance >= 0 && document.body.scrollTop >= toTop) {
return true;
}
if (elapsed > time) {
// This is as far as we're going to get.
return true;
}
}
}
function scrollToElement(el, time) {
scrollTo(el.offsetTop, time);
}
d3.selectAll('.scroll-to-middle').on('click', scrollToMiddle);
d3.selectAll('.scroll-to-top').on('click', scrollToTop);
d3.selectAll('.scroll-to-bottom').on('click', scrollToBottom);
function scrollToMiddle() {
scrollToElement(d3.select('#middle-element').node(), 1500);
}
function scrollToTop() {
scrollToElement(d3.select('#top-element').node(), 1500);
}
function scrollToBottom() {
scrollToElement(d3.select('#bottom-element').node(), 1500);
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment