JSFiddle - React, Tailwind, and code Playground

by philippkuehn

HTML

<div class="viewport">
	<div class="wall-wrapper">
		<div class="wall"></div>
	</div>
</div>

CSS

* {
	margin: 0;
	padding: 0;
	border: none;
	-webkit-box-sizing : border-box;
	-moz-box-sizing : border-box;
	box-sizing : border-box;
	-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
	-webkit-text-size-adjust: none;
	-webkit-touch-callout: none;
	outline: none;
}

article,
aside,
details,
figcaption,
figure, 
footer,
header,
hgroup,
menu,
nav,
section {
	display: block;
}

html,
body {
	width: 100%;
	height: 100%;
}

body {
	
}

.viewport{
    position: relative;
    width: 100%;
    height: 100%;
    overflow: scroll;
    background: #090909;
}

.wall-wrapper {
	position: relative;
}

.wall-wrapper:after {
	content: '';
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	border: 100px solid #090909;
	pointer-events: none;
}


.wall {
	position: relative;
}


.chunk {
	position: absolute;
	visibility: hidden;
	opacity: 0;
	-webkit-transition: opacity 1s ease-out, visibility 1s ease-out;
}

.chunk.is--loaded {
	visibility: visible;
	opacity: 1;
}

.star {
	position: absolute;
	background: #fff;
	-webkit-border-radius: 50%;
	-webkit-box-shadow: 0 0 4px #fff;
    display: none;
}

.star__size--1 {
	width: 1px;
	height: 1px;
}

.star__size--2 {
	width: 2px;
	height: 2px;
}

.star__size--3 {
	width: 3px;
	height: 3px;
	-webkit-box-shadow: 0 0 30px 3px rgba(255,255,255,0.4), 0 0 10px 1px rgba(255,255,255,0.4);
}

.star__opacity--1 {
	opacity: 1;
}

.star__opacity--2 {
	opacity: 0.5;
}

.star__opacity--3 {
	opacity: 0.2;
}

JavaScript

jQuery(document).ready(function($) {

	var $doc = $(document),
		$win = $(window),
		$viewport = $('.viewport'),
		$wallWrapper = $('.wall-wrapper'),
		$wall = $('.wall'),
		chunkWidth = 500;
	
	var universe = {
		
		init: function($element) {
			
			$viewport.on('scroll', universe.helper.debounce(function() {

				var needToGenerate = [];
				
				if ($viewport.scrollTop() <= 100) {
					needToGenerate.push('top');
				}
				
				if ($viewport.scrollTop() + $viewport.height() > $wallWrapper.height() - 100) {
					needToGenerate.push('bottom');
				}
				
				if ($viewport.scrollLeft() <= 100) {
					needToGenerate.push('left');
				}
				
				if ($viewport.scrollLeft() + $viewport.width() > $wallWrapper.width() - 100) {
					needToGenerate.push('right');
				}
				
				universe.generate.chunks(needToGenerate);
				universe.showAroundCenter(universe.getPosition());
				universe.generate.stars();
				
			}, 100));
			
			universe.generate.chunks(false, true);
			
		},
		
		getPosition: function() {
			
			var centerPosX = $viewport.scrollLeft() + ($viewport.width() / 2),
				centerPosY = $viewport.scrollTop() + ($viewport.height() / 2),
				overX = parseFloat($wall.attr('data-from-x')) + Math.floor(centerPosX / chunkWidth),
				overY = parseFloat($wall.attr('data-from-y')) + Math.floor(centerPosY / chunkWidth);
			
			return [overX, overY];
				
		},
		
		showAroundCenter: function(center) {
			
			var newFromX = center[0] - 1,
				newToX = center[0] + 1,
				newFromY = center[1] - 1,
				newToY = center[1] + 1,
				coordinates = universe.helper.getCoordinatesBetween(newFromX, newToX, newFromY, newToY);
			
			$('.chunk').removeClass('is--in-viewport');
			
			for (i = 0, l = coordinates.length; i < l; i++) {
				
				$('.chunk[data-x="' + coordinates[i]['x'] + '"][data-y="' + coordinates[i]['y'] + '"]').addClass('is--in-viewport');
				
			}
			
			$('.chunk').not('.is--in-viewport').empty().removeClass('is--loaded');
			
		},
		
		generate: {
		
			chunks:...