The first thing to do is to find out the height and width on the screen of each word. That answer in the SOEN shows a possible path (create a div
with the word and measure their clientWidth
and clientHeight
).
var palavras = [];
var larguraTotal = 0;
var alturaMaxima = 0;
$('li').each(function() {
palavras.push({
elemento:this,
largura:this.clientWidth,
altura:this.clientHeight
});
larguraTotal += this.clientWidth;
alturaMaxima = Math.max(alturaMaxima, this.clientHeight);
});
Once this is done, you need to figure out the most "harmonious" way to distribute your words on the screen (i.e. don’t leave them "tight" horizontally and "loose" vertically, or vice versa). One way - not necessarily the best way - would be:
var linhas = 0;
do {
linhas++;
var horizontal = larguraTotal / linhas / larguraConteiner;
var vertical = linhas * alturaMaxima / alturaConteiner;
} while ( vertical < horizontal*0.8 ); // Esse 0.8 é uma "folga"
Now it’s backpack problem! Good, almost... You need to choose, for each line, a set of words that approaches the desired width (larguraConteiner * horizontal
). I suggest we start with the bigger ones, because it’s easier to fit the smaller ones later.
var distribuicao = [];
for ( var i = 0 ; i < linhas ; i++ )
distribuicao.push({ palavras:[], larguraTotal:0 });
function minima() {
var min = 0;
for ( var i = 1 ; i < distribuicao.length ; i++ )
if ( distribuicao[i].larguraTotal < distribuicao[min].larguraTotal )
min = i;
return distribuicao[min];
}
palavras.sort(function(a,b) { return b.largura - a.largura; });
for ( var i = 0 ; i < palavras.length ; i++ ) {
var min = minima();
min.palavras.push(palavras[i]);
min.larguraTotal += palavras[i].largura;
}
Finally, we will distribute the words across the screen. I will do this using absolute positioning, but you can think of another way too.
var alturaSobrando = alturaConteiner - linhas*alturaMaxima;
var alturaAntes = alturaSobrando / linhas / 2;
for ( var i = 0 ; i < distribuicao.length ; i++ ) {
var larguraSobrando = larguraConteiner - distribuicao[i].larguraTotal;
var larguraAntes = larguraSobrando / distribuicao[i].palavras.length / 2;
var top = alturaAntes + i*(2*alturaAntes + alturaMaxima);
var left = larguraAntes;
for ( var t = 0 ; t < distribuicao[i].palavras.length ; t++ ) {
var palavra = distribuicao[i].palavras[t];
$(palavra.elemento).css({
position: "absolute",
top: top,
left: left
});
left += 2*larguraAntes + palavra.largura;
}
}
Quite homogeneous, no? Now, we have a margin of manoeuvre to randomize each word. There is a space of larguraAntes
before and after each word. Iofc alturaAntes
. I will use half of this space (here you evaluate what is interesting, aesthetically speaking, in my opinion using the whole space has left the appearance kind bizarre).
top: top + Math.floor(Math.random()*alturaAntes - alturaAntes/2),
left: left + Math.floor(Math.random()*larguraAntes - larguraAntes/2)
Final result. Each of these steps can be improved, if desired, I did not spend much time with each of them not. In addition, some boundary conditions I think may be bugged (for example, when testing using the whole space, some words came out of the container) - but in the example above I believe this does not occur.
have tried with justified text? did not look good?
– SparK
@Spark It is still "right" too - http://jsfiddle.net/sRe9k/
– bfavaretto