Desafío front-end aceptado: CSS 3D Cube
Publicado: 2022-03-10¿Te gustan los retos? ¿Está dispuesto a asumir una tarea con la que nunca antes se ha enfrentado y hacerlo dentro de un plazo? ¿Qué pasa si, al realizar la tarea, se encuentra con un problema que parece irresoluble? Quiero compartir mi experiencia de usar los efectos 3D de CSS por primera vez en un proyecto real y para inspirarte a enfrentar desafíos.
Era un día cualquiera cuando Eugene, gerente de CreativePeople, me escribió. Me envió un video y me explicó que estaba desarrollando un concepto para un nuevo proyecto y se preguntaba si sería posible para mí desarrollar algo como lo que estaba en el video.
Lectura adicional en SmashingMag:
- Beercamp: un experimento con CSS 3D
- Crear formas receptivas con Clip-Path y salir de la caja
- Juguemos con CSS acelerado por hardware
Era un objeto 3D (un paralelepípedo, para ser precisos) que giraba alrededor de uno de los ejes. Ya tenía algo de experiencia trabajando con CSS 3D, y una solución comenzó a formarse en mi mente. Busqué en Google palabras clave como "CSS 3D cube" para confirmar mis ideas y le respondí a Eugene que era posible.
La siguiente pregunta de Eugene fue si aceptaría el proyecto. Me gustan las tareas complicadas, así que no pude negarme. En ese momento, no me di cuenta en lo que me estaba metiendo, pero estaba más allá de la determinación.
Afila tus hachas
Recordemos los ejes, no los ejes de guerra, sino las rectas numéricas, los mismos ejes que en el sistema de coordenadas cartesiano tridimensional que estudiamos en la escuela. Como nos dice Wikipedia:
El sistema de coordenadas cartesianas para un espacio tridimensional es un triplete ordenado de líneas (ejes) que son perpendiculares por pares, tienen una sola unidad de longitud para los tres ejes y tienen una orientación para cada eje.
La siguiente imagen muestra cómo se orientan los ejes en un navegador web.

El eje x es horizontal, el eje y es vertical y el eje z parece salir de la pantalla hacia usted. El valor cero del eje z es el plano de la pantalla. Recuerda esto.
Aclarando la perspectiva
Para crear un objeto 3D, necesitaba un elemento (llamémoslo una "escena") con una perspectiva. La perspectiva es la profundidad de la escena, y depende del tamaño de los objetos que contiene.
.scene { perspective: 800px; }
Si la perspectiva es demasiado pequeña, los objetos podrían distorsionarse. Si es demasiado grande, el efecto 3D se reducirá a nada.
Vea el Pen jqgMvL de Anna Selezniova (@askd) en CodePen.
Además, solo hay un ángulo de visión para todos los objetos en la escena. Y el efecto 3D depende de la posición del punto de vista.
Vea el Pen oxKzKv de Anna Selezniova (@askd) en CodePen.
Entonces, ¿cómo calculamos la perspectiva? Descubrí que depende del eje de rotación. Para el eje x, el valor de altura multiplicado por 4 encajaría. Para el eje y, sería el valor del ancho multiplicado por 4. Esta es mi fórmula mágica:
const perspective = dimension * 4;
Considerado desde todos los lados
Después de determinar la perspectiva, comencé a crear un objeto 3D. Elegí un cubo porque es sencillo y predecible. Un elemento de cubo se crea como un div regular, relativamente posicionado, con el ancho y alto definidos (digamos, 200px
). Se transforma en un objeto 3D a través de la propiedad transform-style
con un valor de preserve-3d
. Le dice al navegador que renderice todos los elementos anidados de acuerdo con las reglas del mundo 3D.
En mi caso, el cubo tiene seis divs (o “lados”), absolutamente posicionados. Los nombres de las clases corresponden a las posiciones iniciales de los lados ( back
, left
, right
, top
, bottom
, front
). Aquí está el marcado:
<div class="scene"> <div class="cube"> <div class="side back"></div> <div class="side left"></div> <div class="side right"></div> <div class="side top"></div> <div class="side bottom"></div> <div class="side front"></div> </div> </div>
Por defecto, todos los lados estarán en un plano. Entonces, necesitaba reorganizarlos. Así es como se ve:
Vea el Pen mPNwPx de Anna Selezniova (@askd) en CodePen.
Y aquí está el CSS resultante:
.cube { position:relative; width: 200px; height: 200px; transform-style: preserve-3d; } .side { position: absolute; width: 200px; height: 200px; } .back { transform: translateZ(-100px); } .left { transform: translateX(-100px) rotateY(90deg); } .right { transform: translateX(100px) rotateY(90deg); } .top { transform: translateY(-100px) rotateX(90deg); } .bottom { transform: translateY(100px) rotateX(90deg); } .front { transform: translateZ(100px); }
Para rotar el cubo, configuro la propiedad de transform
en el elemento del cubo en un ángulo de rotación arbitrario a lo largo del eje x:
.cube { transform: rotateX(42deg); }
Superando las deficiencias
De acuerdo con la tarea, debía rotar el cubo solo a lo largo del eje x, por lo que no necesitaba el lado izquierdo o derecho. Agregué leyendas para alinearlas con las posiciones iniciales de los lados restantes.
Empecé a rotar el cubo y descubrí que las leyendas en la parte inferior y posterior se mostraban al revés:
Vea el Pen GZVvMR de Anna Selezniova (@askd) en CodePen.
Para resolver este problema, giré cada uno de estos lados a lo largo del eje x 180 grados:
.back { transform: translateZ(-100px) rotateX(180deg); } .bottom { transform: translateY(100px) rotateX(270deg); }
Ir más allá de la pantalla
Empecé a llenar los lados con contenido real e inmediatamente me encontré con otro problema. Necesitaba mostrar líneas de puntos de 1 píxel, pero estaban borrosas y se veían mal.

Vea el Pen VjeBPg de Anna Selezniova (@askd) en CodePen.
Pronto me di cuenta de cuál era el problema. ¿Recuerdas aquel anuncio de televisión en 3D en el que la imagen se extiende más allá de la pantalla? Fue algo así con mi cubo.
Si pudiera mirar el cubo desde el lado izquierdo o derecho, vería que su centro estaba en el plano de la pantalla (cero en el eje z) y que el lado frontal estaba más allá de la pantalla. Por lo tanto, aumentó visualmente y borrosa.
Vea el Pen WwVEMR de Anna Selezniova (@askd) en CodePen.
Para resolver este problema, moví el cubo a lo largo del eje z para alinear el lado frontal con el plano de la pantalla:
.cube { transform:translateZ(-100px); }
Aquí está el cubo ahora, casi listo:
Vea el Pen Xdvery de Anna Selezniova (@askd) en CodePen.
Usando los números mágicos
Supongo que habrás notado que estoy usando el número mágico 100
para cambiar los lados a lo largo del eje. El valor 100
es exactamente la mitad de la altura de mi cubo de prueba. ¿Por qué la mitad de la altura? Porque ese sería el radio de un círculo inscrito en un lado del cubo (que es un cuadrado, aparentemente).
const offset = dimension / 2;
Si tuviera que girar un prisma triangular, el círculo estaría inscrito en un triángulo. En este caso, la fórmula para la compensación sería la siguiente:
const offset = dimension / (2 * Math.sqrt(3));
Soplando el cubo
Para dar por finalizada la tarea, tuve que probar el resultado en diferentes navegadores.
La imagen que vi en Internet Explorer me sumió en la depresión. Para tener una idea de lo que estoy hablando, mire la demostración a continuación en su navegador favorito. Cambié una propiedad que provocó que el cubo se mostrara incorrectamente en Internet Explorer. Sin embargo, no mire el código fuente hasta que haya leído el párrafo debajo de la demostración a continuación.
Vea el Pen XKWMwV de Anna Selezniova (@askd) en CodePen.
El hecho es que Internet Explorer no admite la propiedad transform-style
con un valor de preserve-3d
. Aprendí sobre esto mirando mi recurso de confianza Can I Use (ver nota 1). En la demostración anterior, reemplacé preserve-3d
con flat
. ¿Ya lo sabías? ¡Oye, te dije que no miraras!
Estaba molesto, pero no me iba a rendir. Un problema es una oportunidad para aprender algo nuevo. Además, había aceptado el desafío.
Buscando el fulcro
Estaba buscando una manera de crear un objeto 3D sin usar transform-style: preserve-3d
, y finalmente descubrí una propiedad útil: transform-origin
. Determina el punto central de la transformación de un elemento. He creado una demostración interactiva a continuación, que lo ayudará a comprender cómo funciona:
Vea el Pen rLNmBp de Anna Selezniova (@askd) en CodePen.
La rotación 3D del elemento en la demostración es muy similar a la parte frontal de un cubo, ¿no es así? Eso es lo que usé.
(Por cierto, ¿trató de seleccionar la casilla de verificación backface-visibility: hidden
durante la rotación 3D? Esta propiedad se usa para ocultar la parte posterior del elemento durante la transformación 3D).
Empezar todo de nuevo
Empecé a rehacer el cubo. No tuve que interactuar con la escena como un todo, así que eliminé la propiedad de perspective
del elemento de la scene
y la agregué a cada transformación 3D, de modo que ahora cada elemento se transforma de forma independiente. Además, establecí nuevas propiedades para cada lado: transform-origin
con un valor igual a la posición del centro del cubo y backface-visibility: hidden
. Así es como los estilos han cambiado:
.scene { } .cube { position: relative; width: 200px; height: 200px; transform: perspective(800px) translateZ(-100px); } .side { position: absolute; transform-origin: 50% 50% -100px; backface-visibility: hidden; }
Tuve que poner los lados en los lugares correctos. Debido a la propiedad transform-origin
, no necesitaba cambiarlos, solo rotarlos alrededor de los ejes. ¡Es como magia! Veamos cómo se ve:
Vea el Pen zBYwEm de Anna Selezniova (@askd) en CodePen.
Aquí está el CSS para la colocación de los lados:
.back { transform: perspective(800px) rotateY(180deg); } .top { transform: perspective(800px) rotateX(90deg); } .bottom { transform: perspective(800px) rotateX(-90deg); } .front { transform: perspective(800px); }
Y aquí puedes ver el nuevo cubo en acción:
Vea el Pen wWvdXd de Anna Selezniova (@askd) en CodePen.
Devolviendo al César lo que es del César
El segundo cubo se ve y gira igual que el primero. Pero en este caso, debe transformar cada lado individualmente. Esto puede no ser muy fácil, especialmente si desea controlar el ángulo de rotación intermedio.
Además, si abre la demostración en Chrome, verá que los lados parpadean durante la rotación, muy frustrante.
Al final, apliqué ambos enfoques usando la prueba simple de transform-style: preserve-3d
. El primer cubo es el predeterminado. El segundo cubo es para Internet Explorer y los navegadores que no son compatibles preserve-3d
.
Usando el poder de las matemáticas
Finalmente, tuve que implementar un efecto de paralaje. Por lo general, este efecto responde a la acción del usuario, ya sea la posición del cursor del mouse o la barra de desplazamiento. En este caso, el efecto depende del ángulo de rotación.
Vea el Pen QENyqm de Anna Selezniova (@askd) en CodePen.
Entonces, ¿qué datos tengo? Primero, tenía los puntos inicial y final de la posición de la leyenda o, para decirlo simplemente, su offset
hacia arriba y hacia abajo desde el centro de un lado. En segundo lugar, tenía el angle
de rotación del cubo.
Pasé horas tratando de desarrollar una fórmula. Entonces, me di cuenta. Esto es lo que me vino a la mente:

Con la ayuda de senos y cosenos, calculé fácilmente el desplazamiento de cada subtítulo según el ángulo. Aquí están las fórmulas que se me ocurrieron:
const front_offset = offset * sin(angle) * -1; const bottom_offset = offset * cos(angle); const back_offset = offset * sin(angle); const top_offset = offset * cos(angle) * -1;
Resumiendo
La tarea ya está completa y puedo disfrutar del resultado y compartirlo con usted. Vea usted mismo cómo funciona. Utilice las teclas de desplazamiento o de flecha para rotar el bloque de promoción. Además, intente tirar del triángulo negro de la derecha hacia arriba y hacia abajo para controlar el ángulo de rotación manualmente (lamentablemente, esta función no funciona en Internet Explorer). Se ve bastante bien, ¿no? Y el rendimiento es bastante alto (alrededor de 60 fotogramas por segundo).
Estoy muy contento de haber participado en el desarrollo de este sitio web. Obtuve una experiencia útil trabajando con CSS 3D y descubrí muchas propiedades interesantes. Más importante aún, he aprendido que uno nunca debe darse por vencido; lo más probable es que encuentre una manera de realizar la tarea.
Espero que hayas disfrutado de mi historia y que ahora estés listo para asumir nuevos desafíos.