Stand-alone Parallax* scrolling lib with zero dependencies (seriously, you don't need jQuery) in just over 7.7k minified (3.5k gzipped).
Designer friendly. No JavaScript skills needed. Just plain CSS.
*Actually, skrollr is much more. It's a full-fledged scrolling animation library. In fact, you can use it and still have no parallax scrolling at all. But calling it "parallax" is part of my ongoing effort to play buzzword bingo as often as possible. By the way, skrollr leverages HTML5 and CSS3 ;-)
Examples - read the source ;-)
- http://www.guardian.co.uk/world/interactive/2012/nov/06/america-elect-graphic-novel
- http://www.evanshalshaw.com/bondcars/
- https://squareup.com/careers/creative
- http://happy.is
- http://www.futuremylove.com/
- http://jonnyacevedo.com/
skrollr allows you to animate any CSS property of any element depending on the horizontal scrollbar position. All you need to do is define key frames for each element at certain points in top scroll offset.
Other libraries require you to write JavaScript in order to define your animations. This introduces two main problems:
- Animation and element are not at one place. In order to find out if any animations are defined for a given element, you have to scroll through many (sometimes thousands) of lines of JavaScript.
- You have to learn a new syntax which is often very verbose and limited at the same time.
With skrollr, you put the definition of your key frames right where they belong (to the element) using a syntax you already know (plain CSS).
Simple animation of one property
<div data-0="padding:0px;" data-1000="padding:300px;">WOOOT</div>
That was easy, right?
We are using the HTML5 data attributes to attach key frames to DOM elements. The numbers represent the key frame position (the top scroll offset in pixel). The highest key frame found in the document will be used to set the the max top scroll offset.
You can set multiple properties, just like with the style
attribute.
<div data-0="padding:0px;color:hsl(0,50%,50%);" data-1000="padding:300px;color:hsl(360,50%,50%);">WOOOT</div>
And you can specify easing functions for each property using square brackets. That is an extension to the default CSS syntax.
<div data-0="padding[bounce]:0px;color[cubic]:hsl(0,50%,50%);" data-1000="padding:300px;color:hsl(360,50%,50%);">WOOOT</div>
skrollr automatically sets the prefixed properties for you. You not just don't have to use prefixed properties, it's even wrong to do so. The following will rotate an element in every browser that supports transform, no matter if they call it "-moz-transform" or "-webkit-transform".
<div data-0="transform:rotate(0deg);" data-1000="transform:rotate(180deg);">Look ma, I'm rotating!</div>
skrollr will add a skrollr
class to the HTML
element when calling init
and will remove a no-skrollr
class if present. This allows fallback CSS rules to create a good user experience on unsupported devices or when JavaScript or skrollr are disabled.
All elements under skrollr's control (elements with appropriate data-attributes) will get the skrollable
class.
In addition we add the rendered
or unrendered
class, depending on whether an element is currently being styled by skrollr, that means the current scroll offset is in between the key frames of that element, or not.
Now it gets really exciting. Being only able to define key frames in absolute values is simply insufficient for some cases. For example if you don't know where an element will exactly be in the document. That's why there are two modes for key frames, namely absolute
and relative
move.
Absolute mode is what you already know about. The key frames are in absolute values, so how much the document has been scrolled down.
The syntax is data-[offset]-[anchor]
, where offset
can be any integer (0 is default) and anchor
can be either start
(default) or end
. Either offset
or anchor
can be ommited in some situations. Here are some examples of key frames and their meaning.
data-0
=data-start
=data-0-start
: When the scroll top is 0.data-100
=data-100-start
: When the scroll top is 100.data--100
=data--100-start
: When the scroll top is -100 (sounds like nonsense, but keep in mind that interpolation will be relative to this point).data-end
=data-0-end
: When offset is 0, but counting from the bottom of the document instead of from the top. In short: when you reach the bottom of the page.data-100-end
: 100px before we reach the bottom.data--100-end
: 100px after we reach the bottom (again, it's up to you whether you need it).
Relative mode is something which has not been mentioned yet, even though it's very powerful. Instead of defining key frames relative to the document, we are able to define them depending on the position of any element in relation to the viewport.
The syntax is data-[offset]-(viewport-anchor)-[element-anchor]
, where offset
can again be any integer and defaults to 0. Both viewport-anchor
(mandatory) and element-anchor
(optional) can be one of top
, center
or bottom
. If element-anchor
is ommitted, the value of viewport-anchor
will be taken (just like with background-position). Here are some examples of key frames and their meaning.
data-top
=data-0-top
=data-top-top
=data-0-top-top
: When the element's top is aligned with the top of the viewport.data-100-top
=data-100-top-top
: When the element's top is 100px above the top of the viewport.data--100-top
=data--100-top-top
: When the element's top is 100px below the top of the viewport.data-top-bottom
=data-0-top-bottom
: When the bottom of the element is at the top of the viewport (it's just not visible).data-center-center
=data-0-center-center
: When the element is at the center of the viewport.data-bottom-center
=data-0-bottom-center
: When the element's center is at the bottom of the viewport, thuss the upper half of the element is visible.
By default the element is the element where the key frames are defined on (self), but can be any element on the page. You can optionally specify which element you want by using the data-anchor-target
and any CSS selector. The first element on the page matching the selector will be used. data-anchor-target
requires IE 8 or greater.
Examples: data-anchor-target="#foo"
or data-anchor-target=".bar:not(.bacon) ~ span > a[href]"
Here's an infographic for better understanding of anchors (click to open PDF):
Important: All those values will be calculated up-front and transformed to absolute
mode. So if either the element's box height changes (height, padding, border) or the elements position within the document, you probably need to call refresh()
(see documentation in JavaScript section below). Window resizing is handled by skrollr.
I was lying to you. The syntax for absolute mode is not data-[offset]-[anchor]
and for relative mode it's not data-[offset]-(viewport-anchor)-[element-anchor]
. In both cases offset
can be preceeded by a constant which can be passed to the ìnit
method. The name of the constant needs to be preceeded with an underscore.
Example:
skrollr.init({
constants: {
foobar: 1337
}
});
<div data-_foobar="left:0%;" data-_foobar--100="left:50%;" data-_foobar-100="left:100%;"></div>
<!--Equal to-->
<div data-1337="left:0%;" data-1237="left:50%;" data-1437="left:100%;"></div>
Valid characters for a constant are [a-z0-9_]
.
Imagine the following animation
<div data-100="left:0%;" data-200="top:0%;" data-300="left:50%;" data-400="top:50%;"></div>
One could expect left
to have a value of 25%
at keyframe 200
. That is not the case. By design skrollr only interpolates values between key frames which are direct neighbors. What actually happens is that skrollr internally fills out all holes once from left and then from right. So the above is equivalent to
<div data-100="left:0%;top:0%;" data-200="left:0%;top:0%;" data-300="left:50%;top:0%;" data-400="left:50%;top:50%;"></div>
The reason why skrollr is so lightweight and powerfull is because it literally interpolates any numbers it can find. If you want to prevent some side effect, you can supress interpolation for a specific value by prepending an exclamation point.
Example:
<!-- This will get your image url f***** up because there's no "kitten0.4561799.jpg" and the like -->
<div data-0="background-image:url(kitten1.jpg);" data-100="background-image:url(kitten2.jpg)"></div>
<!-- Better -->
<div data-0="background-image:!url(kitten1.jpg);" data-100="background-image:!url(kitten2.jpg)"></div>
Note: The values for both keyframes (at least the if they contain a number) need to be prefixed if you want to avoid skrollr throwing an exception at you!
Now that we just talked about CSS transforms, there are some limitations of skrollr you should be aware of.
- All numeric values have to have the same unit. It's not possible to animate from
0%
to100px
. skrollr won't complain, but results are undefined. - Animations between values which are composed of multiple numeric values like
margin:0 0 0 0;
are only possible for the same number of values.margin:0px 0px 0px 0px;
tomargin:0px 100px 50px 3px;
is fine, but notmargin:10px;
tomargin:5px 10px;
. - Animations between CSS transforms only work when they use the same functions in same order. From
rotate(0deg) scale(1)
torotate(1000deg) scale(5)
is fine. - Color animations don't support named values like "red" or hex values like "#ff0000". Instead, you have to use
rgb()
,rgba()
,hsl()
andhsla
. Don't worry, there's a skrollr plugin for IE < 9 to supporthsl()
(without "a"!) and to fall rgba back to rgb. - Color animations only work for same color functions.
hsl()
tohsl()
orhsla()
is fine, but notrgb()
tohsl()
. Which makes sense, because animating from the same colors in rgb space and in hsl space results in different animations (hsl gives you the nice rainbow stuff).
But feel free to send in a pull request to fix any of them. Just keep in mind that keeping skrollr as lightweight as possible has high priority.
On the JavaScript part there's not much to do (you can, if you want to!). So if you only know CSS and HTML, perfect.
All there is to do is to call skrollr.init([options]);
. Subsequent calls to init()
will just return the same skrollr instance again.
Possible options include
Smooth scrolling smoothens your animations. When you scroll down 50 pixel the animations will transition instead of jumping to the new position.
The global setting can be overridden per element by setting data-smooth-scrolling
to on
or off
.
An object containing integers as values. The keys can contain [a-z0-9_]
. They do not need a leading underscore.
Example: data-_myconst-200
and skrollr.init({constants: {myconst: 300}})
result in data-500
.
By default skrollr uses the largest key frame and makes document height + viewport height this high, thus the max possible scroll top offset. If your animation runs too fast or too slow, just adjust the scale value.
When forceHeight
is set to false, scale
is ignored.
scale
affects constants
as well.
scale
does only affect key frames in absolute mode, e.g. data-500
but not data-top
.
####forceHeight=true
true
: Make sure the document is high enough that all key frames fit inside. Example: You use data-1000
, but the content only makes the document 500px high. skrollr will ensure that you can scroll down the whole 1000px. Or if you use relative mode, e.g. data-top-bottom
, skrollr will make sure the bottom of the element can actually reach the top of the viewport.
false
: Don't manipulate the document and just keep the natural scrollbar.
A listener function getting called each time right before we render everything. The function will be passed an object with the following properties:
{
curTop: 10, //the current scroll top offset
lastTop: 0, //the top value of last time
maxTop: 100, //the max value you can scroll to. curTop/maxTop will give you the current progress.
direction: 'down' //either up or down
}
Returning false
will prevent rendering.
A listener function getting called right after we finished rendering everything. The function will be passed the same parameters as beforerender
An object defining new easing functions or overwriting existing ones. Easing functions get just one argument, which is a value between 0 and 1 (the percentage of how much of the animation is done). The function should return a value between 0 and 1 as well, but for some easings a value less than 0 or greater than 1 is just fine.
An easing function basically transforms the timeline for an animation. When the animation should be 50% done, you can transform it to be 90% done or whatever your function does.
Example:
skrollr.init({
easing: {
//This easing will sure drive you crazy
wtf: Math.random,
inverted: function(p) {
return 1 - p;
}
}
});
You can now use the easing functions like any other.
skrollr ships with some built in functions:
- linear: The default. Doesn't need to be specified.
- quadratic: To the power of two. So 50% looks like 25%.
- cubic: To the power of three. So 50% looks like 12.5%
- begin/end: They always return 0 or 1 respectively. No animation.
- swing: Slow at the beginning and accelerates at the end. So 25% -> 14.6%, 50% -> 50%, 75% -> 85.3%
- sqrt: Square root. Starts fast, slows down at the end.
- bounce: Bounces like a ball. See https://www.desmos.com/calculator/tbr20s8vd2 for a graphical representation.
Note: Your easing functions should return 1 for input of 1. After the keyframe is passed, skrollr sets the values to the values of this keyframe. So if you function returns .8 for input of 1, your elements will jump at the end. But you can also use this on purpose, like the "inverted" function in the above example. The element will do everything in reverse, but at end the jumps to the end position.
Calling init()
returns an instance of skrollr which exposes a public api.
Reparses all given elements. Useful when
- elements in
relative
mode change and need to be updated - data-attributes are manipulated dynamically
- new elements are added to the DOM and should be controlled by skrollr
When no elements are given, all elements in the document will be parsed again. In fact, when calling skrollr.init()
skrollr uses refresh()
without parameters internally.
Time consuming operation, should not be called on every rendering.
returns an integer which represents the absolute scroll position which correlates to the relative anchor.
element
must be a DOM element.
viewportAnchor
and elementAnchor
must be one of top
, center
or bottom
Example:
var offset = s.relativeToAbsolute(document.getElementById('foo'), 'top', 'bottom');
//offset contains the scroll position at which #foo's top is at the bottom of the viewport.
//if you now use window.scroll or similar to scroll to offset, #foo's top is aligned with the bottom of the viewport.
Sets the top offset using window.scroll(0, top)
Animates the scroll position from current position to top
. Possible Options include
How long the animation should run in milliseconds. The default is 1000
or one second.
The name of an easing function. The same functions can be used as for property animations. Default is linear
.
A function to be called after the animation finished. When you pass a top
value, which is the same as the current, then the function will be called immediately. The function get a boolean argument interrupted
which indicates if the animation was iterrupted by stopAnimateTo or finished to the end.
Stops the animation and calls the done
callback passing true
as interrupted
arguments.
Returns if an animation caused by animateTo is running.
Set a listener function for one of the events described in the options section (beforerender, render). Only one listener can be attached at a given time. This method overwrites the current listener, if any.
Removes the listener for the given event.
- #78: Fixed that new parser didn't allowed omitting the last semicolon in a keyframe property list.
- Fixed
setScrollTop
andanimateTo
not working because iScroll uses negative offset.
- breaking the
plugin
api has been removed (the IE plugin has been updated to a new, hidden api). - Full mobile support using iscroll.
- #73: Fixed parser to not mess up colons inside URLs
- #74: Fixed parser to not treat single periods as decimal numbers
- #76: Fixed dummy element overlaping the content, even though it should be unobtrusive
- #58:
forceHeight
now handles relative mode like a boss. - #59: Make
scale
option only affect absolute mode.
- #64: Setting
float
property using JavaScript didn't work across browser. Now usingstyleFloat
andcssFloat
properties.
- The
scale
option does not affectconstants
.
- Allow smooth scrolling on element level using
data-smooth-scrolling
- Added experimental smooth scrolling (no more CSS transitions. WORKS IN IE.).
- Added
stopAnimateTo
method.
- Updated the requestAnimationFrame polyfill for smoother animations
- Updated the way requestAnimationFrame is used for even smoother animations
- New method
relativeToAbsolute
which was formerly private - New method
isAnimatingTo
to check if an animation caused byanimateTo
is running - Added
sqrt
easing function
- Experimental mobile support using https://github.com/zynga/scroller
- A
skrollr
class is added to the HTML element and ano-skrollr
class is removed wheninit
is called. Useful for fallback styling.
- Added new feature "constants".
- Added new feature "anchor-target" which allows elements to react to other elements leaving/entering the viewport.
- Fixed a bug which broke skrollr in IE caused by wrong regular expression behavior
- breaking the
data-end-[offset]
syntax changed. It's nowdata-[offset]-end
. - Fixed a bug where white spaces between style declarations were not ignored.
- Added support for anchors. Animations can now be specified relative to the elements position within the viewport.
- Added support for SVG elements.
- Added new method
refresh()
.
- Alexander Prinzhorn, repo owner, main contributor
- Ali Karbassi
- Tri Nguyen
- Charles J Hardy
- Oscar Otero
- Jim Osborn
- Everyone giving feedback on Twitter and through other channels.