Reputation: 61
As you can see under codepen.io, I’ve built a perspective view of a 3D cube (orange) in a 3D structure with CSS.
:root {
--side: 40px;
--rowCols: 3;
--wh: 121px;
--mside: -40px;
--twoSides: -80px;
}
body {
background-color: blue;
}
.canvas {
position: absolute;
left: 0px;
top: 10px;
transform-style: preserve3d;
perspective: 850px;
}
.grid {
position: absolute;
left: 200px;
top: 75px;
transform-style: preserve-3d;
animation: spin 15s infinite linear;
}
.grid .face {
position: absolute;
height: var(--wh);
width: var(--wh);
margin: 0;
background-image: repeating-linear-gradient(#ccc 0 1px, transparent 1px 100%),
repeating-linear-gradient(90deg, #ccc 0 1px, transparent 1px 100%);
background-size: var(--side) var(--side);
}
.cube {
left: 200px;
top: 75px;
position: absolute;
width: calc(var(--side) + 1);
height: calc(var(--side) + 1);
transform-style: preserve-3d;
/*transform: translateZ(var(--side));*/
animation: spin 15s infinite linear;
}
.cube .cface {
width: var(--side);
height: var(--side);
position: absolute;
border: 1px solid black;
display: flex;
background-color: rgb(238, 141, 14);
}
.cube .cface:nth-child(1) {
transform: translateZ(var(--side));
}
.cube .cface:nth-child(2) {
transform-origin: center right;
transform: rotateY(90deg);
}
.cube .cface:nth-child(3) {
transform-origin: center left;
transform: rotateY(-90deg);
}
.cube .cface:nth-child(4) {
transform-origin: top center;
transform: rotateX(90deg);
}
.cube .cface:nth-child(5) {
transform-origin: top center;
transform: rotateX(90deg) translateZ(var(--mSides));
}
@keyframes spin {
from {
transform: rotate3d(1, 1, 0, 0deg);
}
to {
transform: rotate3d(1, 1, 0, 360deg);
}
}
I managed to build it dynamically using the JS function setValues() where « side » is the side width (in px units) of the orange cube and « rowsCols » being the number of cubes per side. I would like to place the orange cube in the defined structure but I didn’t find how to move in all 3D axes by one unit. Does someone have an idea how to do it?
Code demo:
setValues(40, 3)
function setValues(side, rowsCols) {
let r = document.querySelector(':root')
r.style.setProperty('--side', `${side}px`)
r.style.setProperty('--rowCols', `${rowsCols}`)
r.style.setProperty('--wh', `${side * rowsCols + 1}px`)
r.style.setProperty('--mside', `-${side}px`)
r.style.setProperty('--twoSides', `-${side * 2}px`)
let str = ""
let max = (rowsCols + 1) * 2
for (let n = 0; n < max; n++) {
str = `${str}<div class="face"></div>\n`
}
//console.log(str)
document.getElementById("grid").innerHTML = str
str = ""
let pix = side
let f = 1
for (; f <= rowsCols; f++) {
str = `${str}.grid .face:nth-child(${f}){transform: translateZ(${pix}px);}\n`
pix += side
}
pix = 0
for (; f < max; f++) {
str = `${str}.grid .face:nth-child(${f}){transform-origin: center right; transform: rotateY(90deg) translateZ(${pix}px);}\n`
pix -= side
}
//console.log(str)
document.getElementById("style").innerHTML = str
}
:root {
--side: 40px;
--rowCols: 3;
--wh: 121px;
--mside: -40px;
--twoSides: -80px;
}
body {
background-color: blue;
}
.canvas {
position: absolute;
left: 0px;
top: 10px;
transform-style: preserve3d;
perspective: 850px;
}
.grid {
position: absolute;
left: 200px;
top: 75px;
transform-style: preserve-3d;
animation: spin 15s infinite linear;
}
.grid .face {
position: absolute;
height: var(--wh);
width: var(--wh);
margin: 0;
background-image: repeating-linear-gradient(#ccc 0 1px, transparent 1px 100%), repeating-linear-gradient(90deg, #ccc 0 1px, transparent 1px 100%);
background-size: var(--side) var(--side);
}
.cube {
left: 200px;
top: 75px;
position: absolute;
width: calc(var(--side) + 1);
height: calc(var(--side) + 1);
transform-style: preserve-3d;
/*transform: translateZ(var(--side));*/
animation: spin 15s infinite linear;
}
.cube .cface {
width: var(--side);
height: var(--side);
position: absolute;
border: 1px solid black;
display: flex;
background-color: rgb(238, 141, 14);
}
.cube .cface:nth-child(1) {
transform: translateZ(var(--side));
}
.cube .cface:nth-child(2) {
transform-origin: center right;
transform: rotateY(90deg);
}
.cube .cface:nth-child(3) {
transform-origin: center left;
transform: rotateY(-90deg);
}
.cube .cface:nth-child(4) {
transform-origin: top center;
transform: rotateX(90deg);
}
.cube .cface:nth-child(5) {
transform-origin: top center;
transform: rotateX(90deg) translateZ(var(--mSides));
}
@keyframes spin {
from {
transform: rotate3d(1, 1, 0, 0deg);
}
to {
transform: rotate3d(1, 1, 0, 360deg);
}
}
<style id="style"></style>
<div class="canvas">
<div class="grid" id="grid">
<div class="face"></div>
</div>
<div class="cube">
<div class="cface"></div>
<div class="cface"></div>
<div class="cface"></div>
<div class="cface"></div>
<div class="cface"></div>
<div class="cface"></div>
</div>
</div>
Upvotes: 1
Views: 81
Reputation: 61
After a while, I found the way to have the orange cube being part of the grid structure or better said class. I modified a bit HTML, CSS and JS. The new version is available in codepen.io V2.
To finalize what I wanted to build, I added a new (JS) function which updates the CSS for displaying the small (orange) cube of size [pix] pixels at a given [x, y, z] location. I did all this in order to show the current location in a 3D square maze of side [side].
const info = [ // origin, transform X, Y, Z, rotate, angle
[],
[ "", 0, 0, 0, "", 0],
[ "", 0, 0, 1, "", 0],
[ "center right", 0, 0, 0, "rotateY", 90],
[ "center left", 0, 0, 0, "rotateY", -90],
[ "top center", 0, 0, 0, "rotateX", 90],
[ "bottom center", 0, 0, 0, "rotateX", -90]
]
const xyz = ["X", "Y", "Z"]
const cubePos = [2, 2, 2] // x, y, z
const pix = 30
const side = 5
setValues(pix, side, cubePos)
function setValues(side, rowsCols, cubePos){
let r = document.querySelector(':root')
r.style.setProperty('--side', `${side}px`)
r.style.setProperty('--rowCols', `${rowsCols}`)
r.style.setProperty('--wh', `${side * rowsCols + 1}px`)
r.style.setProperty('--mside', `-${side}px`)
r.style.setProperty('--twoSides', `-${side * 2}px`)
let str = ""
let max = (rowsCols + 1) * 2
for (let n = 0; n < max; n++){
str = `${str}<div class="face"></div>\n`
}
str = `${str}<div class="cube">\n`
for (let n = 0; n < 6; n++){
str = `${str}<div class="cface"></div>\n`
}
document.getElementById("grid").innerHTML = str + `</div>`
str = ""
let pix = side
let f = 1
for (; f <= rowsCols; f++){
str = `${str}.grid .face:nth-child(${f}){transform: translateZ(${pix}px);}\n`
pix += side
}
pix = 0
for (; f < max; f++){
str = `${str}.grid .face:nth-child(${f}){transform-origin: center right; transform: rotateY(90deg) translateZ(${pix}px);}\n`
pix -= side
}
document.getElementById("style").innerHTML = str + cubeCSS(cubePos)
}
function cubeCSS(cubePos){
let outStr = ""
for(let f = 1; f < 7; f++){
outStr = `${outStr}.cube .cface:nth-child(${f}){\n`
if(info[f][0] != ""){
outStr = `${outStr}\ttransform-origin: ${info[f][0]};\n`
}
let tra = ""
for(let t = 0; t < 3; t++){
let total = info[f][t + 1] + cubePos[t]
if(total != 0){
tra = `${tra} translate${xyz[t]}(${total * pix}px)`
}
}
if(info[f][4] != ""){
tra = `${tra} ${info[f][4]}(${info[f][5]}deg)`
}
if(tra != ""){
outStr = `${outStr}\ttransform:${tra};\n`
}
outStr = `${outStr}}\n`
}
return outStr
}
body {
background-color: blue;
}
.canvas {
position: absolute;
left: 60px;
top: 60px;
transform-style: preserve3d;
perspective: 800px;
}
.grid {
position: absolute;
transform-style: preserve-3d;
animation: spin 15s infinite linear;
}
.grid .face {
position: absolute;
height: var(--wh);
width: var(--wh);
background-image:
repeating-linear-gradient(#000 0 1px, transparent 1px 100%),
repeating-linear-gradient(90deg, #000 0 1px, transparent 1px 100%);
background-size: var(--side) var(--side);
}
.cube {
position: absolute;
width: calc(var(--side) + 1);
height: calc(var(--side) + 1);
transform-style: preserve-3d;
}
.cube .cface {
width: var(--side);
height: var(--side);
position: absolute;
border: 1px solid black;
display: flex;
background-color: rgb(238, 141, 14);
}
@keyframes spin {
from {
transform: rotate3d(1, 1, 1, 0deg);
}
to {
transform: rotate3d(1, 1, 1, 360deg);
}
}
<style id="style"></style>
<div class="canvas">
<div class="grid" id="grid"></div>
</div>
Upvotes: 1