JSFiddle - React, Tailwind, and code Playground

by joecritch

HTML

<div class="container">

<section class="top">
        <div class="shards">
                        <span data-props='{"x": 58, "y": 1500, "inertia": 0.2}'></span>
                        <span data-props='{"x": 372, "y": 1500, "inertia": 0.6}'></span>
                        <span data-props='{"x": 986, "y": 1500, "inertia": 0.8}'></span>
                    </div>
</section>

<section class="portfolio">
        <div class="shards">
                        <span data-props='{"x": 58, "y": 1500, "inertia": 0.2}'></span>
                        <span data-props='{"x": 372, "y": 1500, "inertia": 0.6}'></span>
                        <span data-props='{"x": 986, "y": 1500, "inertia": 0.8}'></span>
                    </div>
</section>

<section class="about">
        <div class="shards">
                        <span data-props='{"x": 58, "y": 1500, "inertia": 0.2}'></span>
                        <span data-props='{"x": 372, "y": 1500, "inertia": 0.6}'></span>
                        <span data-props='{"x": 986, "y": 1500, "inertia": 0.8}'></span>
                    </div>
</section>
    
</div>

CSS

.container {
     width: 1700px;
    overflow: hidden;
}

section {
    min-height: 600px;
    background: yellow;
    position: relative;
   z-index: 3;
}

section.top {
    background: yellow;
}

section.portfolio {
    background: cyan;}

section.about {
    background: lime;}
.shards {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
}

.shards > span {
        position: absolute;
        display: block;
        width: 600px; /* Bigger than necessary, to save manually entering widths and heights */
        height: 500px;
        background: none no-repeat 0 0;
    }

section.top .shards > span { background-image: url('http://f.cl.ly/items/0y012M032H0h151I2K1T/home-1.png'); }
section.top .shards > span + span { background-image: url('http://f.cl.ly/items/0H192d3h2V2d262D250M/home-2.png'); }
section.top .shards > span + span + span { background-image: url('http://f.cl.ly/items/0q3I3x140b1x1Z3l2o0w/home-3.png'); }

section.portfolio .shards > span { background-image: url('http://f.cl.ly/items/0v2d062h1H2d371s1M1z/portfolio-1.png'); }
section.portfolio .shards > span + span { background-image: url('http://f.cl.ly/items/1y1y1E3d1C3S3q2h2Q0N/portfolio-2.png'); }
section.portfolio .shards > span + span + span { background-image: url('http://f.cl.ly/items/0d1O0Z0m3S2T3Q3t0l2v/portfolio-3.png'); }

section.about .shards > span { background-image: url('http://f.cl.ly/items/0y012M032H0h151I2K1T/home-1.png'); }
section.about .shards > span + span { background-image: url('http://f.cl.ly/items/0H192d3h2V2d262D250M/home-2.png'); }
section.about .shards > span + span + span { background-image: url('http://f.cl.ly/items/0q3I3x140b1x1Z3l2o0w/home-3.png'); }

JavaScript

// Track visibility 
$.fn.trackVisibility = function(options) {
    var defaults = {
        aboveClass: 'above',
        belowClass: 'below',
        visibleClass: 'visible'
    };
    var config = $.extend({}, defaults, options);
    var $window = $(window);
    return this.each(function() {
        var $this = $(this);
        var height = $this.height();
        var offset = $this.offset().top;
        var refresh = function(e) {
            var windowPos = $window.scrollTop();
            if ((height + offset) < windowPos) {
                $this.addClass(config.aboveClass).removeClass(config.visibleClass);
            }
            else if (offset > windowPos + window.innerHeight) {
                $this.addClass(config.belowClass).removeClass(config.visibleClass);
            }
            else {
                $this.removeClass(config.aboveClass + ' ' + config.belowClass).addClass(config.visibleClass);
            }
        };
        $(window).bind('scroll resize', refresh);
    });
};

// Track backgrounds (parallax w/ inertia)
$.fn.trackBackgrounds = function(options) {
    var defaults = {
        minHeight: 600,
        visibleClass: 'visible'
    };
    var config = $.extend({}, defaults, options);
    var $window = $(window);
    return this.each(function() {
        var $this = $(this);
        // var $shards = $(this).find('.shards');
        //console.log($shards);
        var moveBackground = function() {
            var windowPos = $window.scrollTop();
            if ($this.hasClass('visible')) {
                var positions = [];
                var $shards = $this.data('shards');
                $shards.each(function() {
                    var $span = $(this);
                    var props = $span.data('props');
                    var inertia = (props.inertia === 0) ? 1 : props.inertia;
                    var pos = {
                        x: props.x,
                        y: ~~ (-((config.minHeight + windowPos) - props.y) *...