Parallax Scroller (CSS)

by Eric Hynds

HTML

<div class="buttons">
    <button id="prev">Prev</button>
    <button id="next">Next</button>
</div>
<div id="hero-container">
    <div class="hero-image" style="background-image: url(http://placeimg.com/640/480/any?1)">
        <div class="hero-text">
            <div>HERO 1</div>
            <div>More info on Hero 1.</div>
        </div>
    </div>
    <div class="hero-image" style="background-image: url(http://placeimg.com/640/480/any?2);">
        <div class="hero-text">
            <div>HERO 2</div>
            <div>More info on Hero 2.</div>
        </div>
    </div>
    <div class="hero-image" style="background-image: url(http://placeimg.com/640/480/any?3);">
        <div class="hero-text">
            <div>HERO 3</div>
            <div>More info on Hero 3.</div>
        </div>
    </div>
</div>

CSS

html, body {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
}
.buttons {
    position: absolute;
    z-index: 10;
    top: 20px;
    left: 20px;
}
.hero-image:not(:first-child) {
    transform: translateY(100%);
}
.hero-image:not(:first-child) .hero-text {
    transform: translateY(1000%);
}
.hero-image.reverse {
    transform: translateY(-100%);
}
.hero-image.reverse .hero-text {
    transform: translateY(-1000%);
}
.slide-in {
    transition: all 2s ease;
    transform: translateY(0) !important;
}
.slide-in .hero-text {
    transition: all 2s ease;
    transform: translateY(0) !important;
}
.slide-out {
    transition: all 2s ease;
    transform: translateY(-40%) !important;
}
.slide-out .hero-text {
    transition: all 2s ease;
    transform: translateY(-1000%) !important;
}
.slide-out.reverse {
    transition: all 2s ease;
    transform: translateY(40%) !important;
}
.slide-out.reverse .hero-text {
    transition: all 2s ease;
    transform: translateY(1000%) !important;
}
#hero-container {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
}
.hero-image {
    transform: translate3d(0, 0, 0);
    background-size: cover;
    width: 100%;
    height: 100%;
    position: absolute;
}
.hero-text {
    position: absolute;
    left: 10%;
    top: 70%;
    color: #FFF;
    font-family: sans-serif;
    font-size: 30px;
    text-shadow: 0px 0px 8px black, 0px 0px 2px black;
}

JavaScript

(function () {
    var classes = 'slide-in slide-out reverse'
    var transitionEnd = 'webkitTransitionEnd transitionend';

    function Parallax(elem) {
        this.$container = $(elem);
        this.$slides = this.$container.children();
    }

    Parallax.prototype._transitionEnd = function (event) {
        this.$container[this.direction === 'next' ? 'append' : 'prepend'](event.target);
        this.$slides.removeClass(classes).off(transitionEnd)
        this.transitioning = false;
    };

    Parallax.prototype.next = function () {
        if (this.transitioning) return;
        var $slides = this.$container.children();
        this.direction = 'next';
        this.transitioning = true;
        $slides.eq(1).addClass('slide-in');
        $slides.eq(0).on(transitionEnd, this._transitionEnd.bind(this)).addClass('slide-out');
    };

    Parallax.prototype.prev = function () {
        if (this.transitioning) return;
        var $slides = this.$container.children();
        this.direction = 'prev';
        this.transitioning = true;
        $slides.last().addClass('reverse');

        setTimeout(function () {
            $slides.eq(0).addClass('slide-out reverse');
            $slides.eq(-1).on(transitionEnd, this._transitionEnd.bind(this)).addClass('slide-in');
        }.bind(this), 10)
    };

    $.fn.parallax = function () {
        return this.each(function () {
            $.data(this, 'parallax', new Parallax(this));
        });
    };
})();


$(document).ready(function () {
    var $container = $('#hero-container').parallax();
    var obj = $container.data('parallax');

    $('#prev').on('click', function () {
        obj.prev();
    });

    $('#next').on('click', function () {
        obj.next();
    });
});