How to grow an isometric rectangular box properly with HTML and CSS?
Asked Answered
P

3

6

It is convenient for isometric grids to start at the top like this: iso grid

I have the following code to draw a "3d" isometric box:

body {
  display: flex;
  justify-content: center;
  align-items: center;
}

.plane {
  position: relative;
  width: 600px;
  height: 600px;
}

.container {
  position: absolute;
  width: 0;
  height: 0;
}

.left {
  background-color: #3e8fe1;
  height: var(--width);
  width: var(--height);
  transform-origin: 0 0;
  transform: rotate(90deg) skewX(-30deg) scaleY(0.864);
}

.right {
  position: relative;
  background-color: #2870bd;
  height: var(--height);
  width: var(--length);
  transform-origin: 0 0;
  transform: rotate(-30deg) skewX(-30deg) scaleY(0.864);
  bottom: var(--width);
}

.top {
  position: relative;
  background-color: #80bdfe;
  height: var(--length);
  width: var(--width);
  transform-origin: 0 0;
  transform: rotate(210deg) skew(-30deg) scaleY(0.864);
  bottom: calc(var(--width) + var(--height));
}
<body>
  <div class="plane">
    <div class="container" style="--width: 50px; --length: 200px; --height: 100px; left: 150px; top: 150px;">
      <div class="left"></div>
      <div class="right"></div>
      <div class="top"></div>
    </div>
  </div>
</body>

This works, but is there any way to make it so that increasing the --width, --length, and --height CSS variables would grow the box in the opposite directions that they are currently growing in?

That is, --width is currently growing front to back, but should grow back to front (such that increasing it makes it extrude towards you instead of away from you)

Similarly, increasing the --height should make it grow up, not down.

And lastly, increasing the --length should make it grow towards the "camera", and not away from it, as it currently does.

Prussian answered 2/8 at 19:26 Comment(2)
In the initial diagram you show two axes, x, and y. To which axes do the names width, length, and height refer? I'd assume height would be the implicit z axis, but assumptions are often flawed.Instrumentalism
@DavidThomas Yeah, height would be the z axis. And let's see.. length would be the y axis and width would be the x axis I believePrussian
S
6

I think you can simply change the value of the rotate to value - 180 and add some tweak to the translate transformation, and you will have it grow in the other direction accordingly:

const container = document.getElementById('container')
const width = document.getElementById('width')
const length = document.getElementById('length')
const height = document.getElementById('height')

width.addEventListener("input", (event) => {
  container.style.setProperty("--width", `${event.target.value}px`)
})

length.addEventListener("input", (event) => {
  container.style.setProperty("--length", `${event.target.value}px`)
})

height.addEventListener("input", (event) => {
  container.style.setProperty("--height", `${event.target.value}px`)
})
body {
  display: flex;
  justify-content: center;
  align-items: center;
}

.plane {
  position: relative;
  width: 600px;
  height: 600px;
}

.container {
  position: absolute;
  width: 0;
  height: 0;
}

.left {
  background-color: #3e8fe1;
  height: var(--width);
  width: var(--height);
  transform-origin: 0 0;
  transform: translate(calc(var(--length) * -0.864), calc(var(--length) / 2)) rotate(270deg) skewX(-30deg) scaleY(0.864);
  position: relative;
}

.right {
  position: relative;
  background-color: #2870bd;
  height: var(--height);
  width: var(--length);
  transform-origin: 0 0;
  transform: translate(calc(var(--width) * 0.864), calc(var(--width) / -2)) rotate(150deg) skewX(-30deg) scaleY(0.864);
}

.top {
  position: relative;
  background-color: #80bdfe;
  height: var(--length);
  width: var(--width);
  transform-origin: 0 0;
  transform: rotate(30deg) skew(-30deg) scaleY(0.864);
  bottom: calc(var(--height) + var(--height) + var(--width));
}
<div class="plane">
  <div id="container" class="container" style="--width: 50px; --length: 200px; --height: 100px; left: 150px; top: 150px;">
    <div class="left"></div>
    <div class="right"></div>
    <div class="top"></div>
  </div>
</div>

<div style="position: fixed;
    top: 0;
    right: 0;
    z-index: 999999;">
  width: <input type="range" min="10" max="400" value="50" class="slider" id="width">
  <br/> length: <input type="range" min="10" max="400" value="200" class="slider" id="length">
  <br/> height: <input type="range" min="10" max="400" value="100" class="slider" id="height">
</div>

Note: the translate has 0.864 constant, you can adjust it according to the scale value (which is also 0.864).

Sangsanger answered 8/8 at 5:9 Comment(0)
D
2

I used translation in the transform line, which allowed me to create a counter-movement and realign the faces of the cube. I also noticed that you were using 3 separate divs in your example so I replaced 2 of them with the before and after pseudo-elements.

body {
  background-color: #000;
  box-shadow: inset 0 0 1em #000;
}

#plane {
  width: 10em;
  height: 10em;
  margin: 0 auto;
  position: relative;
  top: 5em;
  transform: rotateX(45deg) rotateZ(45deg) translateZ(-1em);
  transform-style: preserve-3d;
}

#plane div {
  float: left;
  transform-origin: 97% 97%;
  position: relative;
}

.tall {
  background: #fff;
  transform: translateZ(var(--height));
  transform-style: preserve-3d;
  width: var(--width);
  height: var(--length);
}

.tall:before, .tall:after {
  content: "";
  background: #2e6b8f;
  position: absolute;
  transform-origin: 100% 100%;
}

.tall:before {
  transform: translateY(calc(var(--length) - 1em - var(--height) + 1em)) rotateX(90deg);
  box-shadow: inset 0em -.125em .25em rgba(0, 0, 0, .1);
  width: var(--width);
  height: var(--height);
}

.tall:after {
  transform: translateX(calc(var(--width) - 1em - var(--length) + 1em)) translateY(calc(var(--length) - 1em)) rotateX(90deg) rotateY(90deg) translateX(calc(var(--height) * -1 + 1em));
  box-shadow: inset -.125em 0em .25em rgba(0, 0, 0, .1);
  width: var(--length);
  height: var(--height);
}
<div id="plane">
  <div class="tall" style="--width: 1em; --length: 1em; --height: 5em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 4em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 3em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 2em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
  <div class="tall" style="--width: 1em; --length: 1em; --height: 1em"></div>
</div>
Dulci answered 7/8 at 13:7 Comment(6)
You seem to have one square here but you added... like 50 divs?Galegalea
@Galegalea There are actually 100 isomeric cubes, they were just packed in a way that they looked like 1 square. I have edited my post above to make this more apparent.Dulci
Yeah, I came across this approach in my searching for a solution (putting rotateX(45deg) rotateZ(45deg) translateZ(-1em); in the parent div), but it makes other things tricky it seems.. Like calculating the click location, applying background images to the walls in a consistent direction, and placing non-rotated images. Hmm, this might be the best way, but it's unfortunate how the solution in my original post doesn't have most of those problemsPrussian
@RyanPeschel if there is anything that you would like me to attempt to implement, tell me. By calculating the click location, do you mean calculating if the cursor clicked the box? And I don't quite get what you mean by applying background images to the walls in a consistent direction and placing non-rotated images.Dulci
I am using this to try and make an isometric room. So instead of a bunch of cubes, there will be one div for one wall, another div for another wall, another div for the floor, etc. The issue is that my images are just drawn in the isometric style, but they shouldn't be rotated, you know? The issue with this approach is that when I place isometric images (furniture) in the house, these images have the parent's transformation applied. In addition, when applying a background image to the walls, the wallpaper flips directions depending on which wall it is applied toPrussian
@RyanPeschel If the wallpaper flips directions, would it not give a more convincing effect since it would project the illusion of faces going in different directions? And even if you wish the image to not be at an angle while on certain faces, could you not adjust the images themselves to appear to be at an angle? To my knowledge, there is no way to rotate an image back without using the transform property.Dulci
P
2

Is there a reason to limit yourself to using CSS+HTML? Would be super simple to recreate this using a library like Isomer.

Parsimonious answered 13/8 at 8:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.