Swiftiti
Swiftiti

Reputation: 61

How to move elements in a perspective view with CSS?

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

Answers (1)

Swiftiti
Swiftiti

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

Related Questions