Developed around visibility
CSS property and HTMLElement#offsetHeight
and HTMLElement#offsetWidth
properties places HTMLElement
s generated by an HTMLElement
source in columns layout attempting to achieve the minimum height of the columns by placing each element on the less lengthy column at the time of placing the element.
Main components are sprinkler
with a sieve
, that is a collection of column
objects, supporting two modes (puddle
and sprite
) and a source
of HTMLElement
s. The elements to be placed are one at the time retrieved from the source
then passed to the sieve
that pass it to the less lengthy column
that stores it.
<html>
<head>
<style>
body { background-color: #cdcecd; }
.centered {
bottom: 0;
left: 0;
margin: auto;
position: absolute;
right: 0;
top: 0;
}
.drop {
position: absolute;
background-blend-mode: overlay;
background-position: center;
background-repeat: no-repeat;
background-size: contain;
border-radius: 50%;
height: auto;
opacity: .5;
width: 5%;
}
.airly {
background-color: #fefefe;
height: 25px;
width: 25px;
}
.vapourly {
background-color: #dcdcdc;
height: 50px;
width: 50px;
}
.dense {
background-color: #c0c0c0;
height: 100px;
width: 100px;
}
.drop.loading { visibility: hidden; }
.sprinkled {
background-color: #eeeeee;
height: 90%;
width: 90%;
}
</style>
<script type="text/javascript">
var sprinkler = function(sprinkled) {
var coordinates = (function() {
var toNumber = (string) => {
var digits = Array.from(string || "")
.filter( (character) => { return !isNaN(character) || "." == character; } )
.join("");
return ( digits.length == 0 ) ? 0 : parseInt(digits);
};
return { of : (htmlElement) => {
var element = htmlElement || { style : { top : "", left : "" } };
return { x : toNumber(element.style.left)
, y : toNumber(element.style.top)
};}};})();
var peeker = function(enabler) {
var peeked = void(0);
var availabilities = { content : (toPeek) => {
if ( enabler(peeked, toPeek) ) {
peeked = toPeek;
}
return availabilities;
}
, result : () => { return peeked; }
};
return availabilities;
};
var vainer = (function() {
return { supply: (purpose) => {
var vainers = [() => { return false; }];
if ( purpose ) { vainers.unshift(() => { vainers.shift(); return purpose(); }); }
return { shift: function() { return vainers[0](); }
, schedule: function(schedulee) {
vainers.unshift( () => { vainers.shift(); return schedulee(); } );
return this;
}
};
}};
})();
var sieve = (function() {
var mode = (function() {
var heightOffset = (element) => { return (element || {offsetHeight: 0}).offsetHeight };
var plainOffset = (element) => { return 0; };
var offset = heightOffset;
var column = () => {
var content = [];
var abacus = (function() {
var sum = 0;
return { count : (something) => { sum += something; }
, counted : () => { return sum; }
, reset : () => { sum = 0; } };
})();
var height = () => {
abacus.reset();
content.forEach( (element) => { abacus.count(element.offsetHeight || 0); } );
return abacus.counted();
};
return { add : (element) => { content.push(element); }
, height : () => { return height(); }
, coordinates : () => { return { x : coordinates.of(content[0]).x
, y : height() + offset(content[0]) } }
};
};
return { puddle : () => { offset = heightOffset; return column; }
, sprite : () => { offset = plainOffset; return column; }
};
})();
var puddleColumn = mode.puddle();
var column = puddleColumn;
var maxify = peeker( (previous, column) => { return column.height() >= (previous || column).height(); } );
var columns = [];
var minimumHeight = 0;
var shell = { add : { column : () => { columns.push(column()); return shell; }
, content: (toAdd) => {
if ( columns.length == 0 ) { columns.push(column()); }
var column = columns[0];
var minify = peeker( (previous, column) => { return column.height() <= (previous || column).height(); } );
columns.forEach( (column) => { minify.content(column); } );
minify.result().add(toAdd);
return { x : minify.result().coordinates().x
, y : minify.result().coordinates().y - toAdd.offsetHeight};
}
}
, length : () => { return columns.length; } };
return { puddle: () => { column = puddleColumn; return shell; }
, sprite: () => { column = mode.sprite(); return shell; }
};})().puddle();
var enabler = (function(purpose) {
var enablers = [ purpose, () => {} ];
return { enable : () => { return enablers[0]; }
, shift : () => { if ( enablers.length > 1 ) { enablers.shift(); } }
};
})( (after) => { sieve.add.column(); after(); } );
var load = (function() {
var loadingDelayMillis = 200;
return { progressive: (multiplier, toLoad) => { setTimeout( () => { toLoad.classList.remove("loading"); }, multiplier * loadingDelayMillis); }
, flashback: (multiplier, toLoad) => { toLoad.classList.remove("loading"); }
};
})();
return function(source) {
var sprinkledCoordinates = sprinkled.getBoundingClientRect() || { x : 0, y : 0, left : 0, top : 0 };
var origin = (function(originX, originY) {
var x = originY;
var y = originX;
return { widthed : (gap) => { x += parseFloat(gap); return x; }
, heighted : (gap) => { y+= parseFloat(gap); return y; }
, recorded : () => { return { x: x, y: y}; }
, reset : { height: () => { y = originY; }
, width: () => { x = originX; }
, both: () => { x = originX; y = originY; } }
};})(sprinkledCoordinates.x, sprinkledCoordinates.y);
var viewport = { height: sprinkled.offsetHeight - sprinkled.offsetHeight * 0.1
, width: sprinkled.offsetWidth - sprinkled.offsetWidth * 0.1 };
var i = 0;
while ( source.callworthy() ) {
var drop = source.next();
drop.classList.add("drop");
drop.classList.add("loading");
drop.style.left = origin.recorded().x;
drop.style.top = origin.recorded().y;
sprinkled.appendChild(drop);
if ( origin.recorded().x > viewport.width ) { enabler.shift(); }
enabler.enable()( () => { origin.widthed(drop.offsetWidth); } );
var position = sieve.add.content(drop);
drop.style.left = position.x + "px";
drop.style.top = position.y + "px";
load.progressive(i++, drop);
}
}
};
</script>
</head>
<body>
<div id="sprinkled" class="sprinkled centered"></div>
<script type="text/javascript">
var spread = (function() {
var source = (function() {
var counter = (function(from) {
var tapCounter = from;
return { incremented : () => { return ++tapCounter; }
, decremented : () => { return --tapCounter; }
, counted : () => { return tapCounter; }
, reset : () => { tapCounter = from; }
};
})(0);
var drop = (className) => {
var div = document.createElement("div");
div.className = className;
return div;
};
var condense = (className) => {
var drops = [];
for (var i = 0; i < 53; i++) {
drops.push(className);
}
return drops;
};
var shuffle = function(array) {
var shuffledArray = [];
while ( array.length > 0 ) {
var index = Math.floor(Math.random() * array.length);
shuffledArray = shuffledArray.concat( array.splice(index, 1) );
}
return shuffledArray;
};
var drops = shuffle(condense("airly").concat(condense("vapourly")).concat(condense("dense")));
return { callworthy: () => { return counter.counted() < drops.length; }
, next: () => { return drop(drops[counter.incremented()]); }
};
})();
sprinkler(document.getElementById("sprinkled"))(source);
})();
</script>
</body>
</html>
Among interests: improvements of javascript code, alternatives to implement sprinkler behaviour, existing alternatives already available or anything else that could be an improvement.