En otra entrada presenté una pequeña página donde se mostraba un gráfico como este:

Por petición y por gusto voy a explicar cómo conseguir hacerlo.

Para hacerlo usé un poco de Javascript y mucho CSS para lograr el efecto.

El HTML

El código html inicial será este:


<div class="grafico-wrapper">
<div class="grafico-fuera">
<div id="grafico" class="grafico">
<div class="column" data-name="C++" data-value="40"></div>
<div class="column" data-name="Unix Shell" data-value="80"></div>
.....
</div>
</div>
</div>

data-name es el nombre de la característica, y data-value el de el porcentaje. Opcionalmente, podemos dejar vacío el gráfico y pasar los valores vía Javascript:


<div class="grafico-wrapper">
<div class="grafico-fuera">
<div id="grafico" class="grafico">
</div>
</div>
</div>

El CSS

El CSS es, de lejos, lo más difícil para generar éste efecto. Para mantener la compatibilidad con IE8 aprovechamos que los navegadores modernos soportan la notación ::before y ::after, mientras él sólo soporta :before y :after. De IE7 no habrá que preocuparse porque no soporta pseudoelementos, por lo que se verá igual que en IE8 (salvo las líneas del porcentaje).

El código está comentado si queréis ver los detalles. Recordad que el CSS va encima de </b:skin>


.grafico-wrapper {
/*Espacios con el gráfico*/
padding: 25px 5px 20px 55px;
/*Un pequeño borde*/
border: 1px solid #EEE;
/*Anchura máxima, se irá haciendo más pequeño con el contenedor*/
max-width: 800px;
/*Lo centramos*/
margin: 0 auto;
}
.grafico-fuera {
/*Separación desde el bisel derecho hasta la columna más a la izda*/
padding-right: 15px;
/*Lo ocultamos -> Lo mostraremos con el script*/
display: none;
position: relative;
}
.lt-ie9 .grafico-fuera {
padding-right: 0;
}
/*Fondo de abajo y lateral derecho*/
.grafico-fuera::before {
content: "";
/*Que sean tan altos como la parte externa del gráfico*/
height: 100%;
/*10px de alto*/
width: 10px;
/*Lo posicionamos a la derecha*/
position: absolute;
right: 0;
bottom: 0;
/*El fondo semitransparente*/
background: rgba(0, 0, 0, .15);
/*Jugamos con el z-index para la profundidad -> El gráfico 2, la parte externa y las líneas 1*/
z-index: 1;
}
.grafico-fuera::after {
content: "";

position: absolute;
/*Tan ancho como el gráfico*/
width: 100%;
/*10px de alto*/
height: 10px;
/*Lo posicionamos abajo*/
left: 0;
bottom: 0;
/*Fondo semitransparente*/
background: rgba(0, 0, 0, .1);
z-index: 1;
}
/*los biseles de la columna
Tened en cuenta que la anchura de los biseles de la izda y la altura de los superiores tiene que ser iguales (en este caso 10px) para que "encajen"
*/

/*Bisel de arriba*/
.column::before {
content: "";
position: absolute;
/*Lo posicionamos encima del todo*/
bottom: 100%;
left:0;
/*10px de alto, tan ancho como la columna*/
width:100%;
height: 10px;
/*Fondo azulado*/
background-color: #538CC6;
background-color: rgba(83,140,198,.8)
}
/*Bisel de la izda*/
.column::after {
content: "";
position: absolute;
/*tan alto como la columna*/
height: 100%;
width: 10px;
/*A la izda del todo*/
right: 100%;
bottom: 0;
/*El fondo*/
background-color: #2D5986;
background-color:rgba(45,89,134,.8);
}
/*
Aquí aplicamos las transformaciones para el efecto
*/
.column::before, .grafico-fuera::after {
-webkit-transform:skewX(45deg);
-moz-transform:skewX(45deg);
-ms-transform:skewX(45deg);
-o-transform:skewX(45deg);
transform:skewX(45deg);
/*Para evitar transladar*/
-webkit-transform-origin:0 100%;
-moz-transform-origin:0 100%;
-ms-transform-origin:0 100%;
-o-transform-origin:0 100%;
transform-origin:0 100%;
}
.column::after, .grafico-fuera::before {
-webkit-transform-origin:100% 0;
-moz-transform-origin:100% 0;
-ms-transform-origin:100% 0;
-o-transform-origin:100% 0;
transform-origin:100% 0;
-webkit-transform:skewY(45deg);
-moz-transform:skewY(45deg);
-ms-transform:skewY(45deg);
-o-transform:skewY(45deg);
transform:skewY(45deg);
}
/*La altura del gráfico*/
.grafico {
height: 200px;
z-index: 2;
}
/*Las líneas*/
.grafico-linea {
position: absolute;
left: 0;
width: 100%;
height: 1px;
background-color: #777;
z-index:1;
/*En los navegadores soportados movemos las líneas hacia arriba y hacia la izda para que coincidan con las de el efecto 3d*/
-webkit-transform:translate(-10px,-10px);
-moz-transform:translate(-10px,-10px);
-ms-transform:translate(-10px,-10px);
-o-transform:translate(-10px,-10px);
transform:translate(-10px,-10px);
}
/*Éste (el número de la línea) nos interesa mantenerlo*/
.grafico-linea:before {
content: attr(data-porcentaje)" %";
position: absolute;
top:-7.5px;
left: -37px;
}
/*Las columnas*/
.column {
background: #8CB3D9;
background:rgba(140,179,217,.9);
text-align: center;
z-index: 2;
}
/*El texto del valor, que nos interesa centrar aproximadamente*/
.value {
position: relative;
top: 40%;
padding: 4px;
}

El Javascript

El javascript es una función con determinadas opciones para generar el gráfico. Ahí es donde calculamos las posiciones teniendo en cuenta la cantidad de valores que hay, y damos la altura a las columnas.


(function(){
/* =Función de ayuda
--------------------- */
function forEach(arr, fn){
for(var i = 0, len = arr.length; i < len; i++){
fn.call(null, arr[i], i)
}
}
/* =Gráfico
--------------------- */
function setGraph(opciones){
var opciones = opciones || {},
// El gráfico
graph = opciones.idContenedor ?
(typeof opciones.idContenedor === "string" ?
document.getElementById(opciones.idContenedor) :
opciones.idContenedor) :
document.getElementById("grafico"),
// Se han pasado valores para generarlo ?
valores = opciones.valores,
// Qué porcentaje del espacio totañ deberíamos ocupar?
espacioOcupado = opciones.espacioOcupado || 80,
// Cada qué porcentaje queremos que se pongan las líneas
porcentajeLineas = opciones.porcentajeLineas || 25,
// El contenedor del gráfico
parent = graph.parentNode,
// Las columnas (si hay)
childs = graph.childNodes || graph.children,
columns = [],
collen, incremento, props, linea;

for (var i = 0; i <= 100; i+=porcentajeLineas){
var linea = document.createElement("div");
linea.className = "grafico-linea"
linea.style.top = i + "%"
linea.setAttribute("data-porcentaje", (100 - i))
parent.appendChild(linea)
}

// Si hemos pasado valores para generar el gráfico
if ( valores && typeof valores === "object" ){
graph.innerHTML = "";
for ( var valor in valores ){
(function(){
var val = valores[valor],
div = document.createElement("div");
div.className = "column";
graph.appendChild(div)
columns.push({
el: div,
name: valor,
value: val
})
})()
}
} else {
// Filtramos entre los elementos
forEach(childs, function(child){
if(child.nodeType === 3 || !child.getAttribute){return}
var value = child.getAttribute("data-value"),
name = child.getAttribute("data-name");
if( value === null || name === null){return}
columns.push({
el: child,
value: value,
name: name
})
});
}

collen = columns.length;
incremento = 100 / collen;
// El espacio un poco más pequeño que el incremento (que sería un 100%)
espacioOcupado = espacioOcupado / collen;// Ocupar un porcentaje del contenedor
// Incremento - espacioOcupado => espacio en blanco por cada columna hacia abajo
//Collen -1 => el número de columnas que tendrán espacio (la última no la tiene)
incremento += (incremento - espacioOcupado)/(collen - 1)

forEach(columns, function(col,i){
var style = col.el.style;
col.el.innerHTML = '<span class="value">' + col.name + ' ('+ col.value + '%)</span>';

style.position = "absolute";

style.width = espacioOcupado + "%";
style.height = col.value + "%";

style.left = (i * incremento) + "%";
style.bottom = 0;

})
graph.style.position = "relative";
parent.style.display = "block";
}
// Hacemos la función global
window.setGraph = setGraph;

})()

He hecho opciones para poder hacer varios gráficos, y cambiar un poco la apariencia, pero aquí haremos lo básico. Para usarlo habría que poner entre etiquetas <script></script> el código de arriba, y debajo de él y del HTML habrá que poner: <script>setGraph()</script> si usamos HTML para pasar valores, o si no:


<script>
setGraph({
valores:{
"PHP" : 60,
"Javascript": 95
//....
}
})
</script>

Ahora el ejemplo

Mostrar

6 pensamientos en “Gráfico en 3D con CSS3 y algo de Javascript

    • Imagen de Emilio Cobos Álvarez el dijo:

      No sería muy difícil. Si quieres hacerlo con este método es fácil (aunque sería hacerlo en vertical). Sería hacer algo como poner el HTML del gráfico vacío (la segunda opción de "HTML") en donde quieres que aparezca el gráfico (y el CSS en su lugar).

      Despúes, en la función "corregir" tendrías que cambiar la línea:

      form.porcentaje.value = puntuacion + "%";

      por

      setGraph({
      valores:{
      "Correcto" : puntuacion
      }
      })

      Así se mostraría en vertical, y lo tendrías solucionado, pero no es muy difícil hacerlo sin usar este código (sobre todo si se trata de un sólo valor), para que se muestre en horizontal.

      Sólo te haría falta algo como
      <div class="grafico"><div id="puntuacion"><span id="valor"></span></div></div<

      Un css tal que así:

      .gráfico {width: 200px; text-align:center;}
      #puntuacion {height: 20px;}
      #valor {line-height: 20px;}

      Y un javascript tal que así (en vez de la línea mencionada anteriormente):
      document.getElementById('puntuacion').style.width = document.getElementById('valor').innerHTML = puntuacion + "%";

      Sería mejor si cachearas los objetos en una variable, pero así funcionará perfectamente.

    • Imagen de daviddavid el dijo:

      Perdona Emilio, soy una patata con esto. Estoy probando la segunda opción pero hago algo mal porque no me funciona. Te envío al correo lo que tengo, a ver si ves dónde la estoy cagando…
      Muchas gracias por tu ayuda!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Puedes usar las siguientes etiquetas y atributos HTML:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre data-language=""> <ul> <ol> <li>
Para poner código usa <pre data-language="[lenguaje]"><code>[código]</code></pre>, y no olvides escapar el HTMl.