dagda1
dagda1

Reputation: 28940

constructing a 3d pyramid with css

Here is a jsbin of what I have so far and below is an image of what I am trying to construct:

enter image description here

Here is the html, I have only 2 sides of the pyramid added so far:

<div class="pyramid-container">
  <div id="pyramid">
    <div class="base"></div>
    <div></div>
    <div></div>
  </div>  
</div>

And here is the css:

.pyramid-container {
  perspective: 800px;
}

#pyramid {
  position: relative;
  transform-style: preserve-3d;
  transform-origin: 116px 200px 116px;
   padding-left: 100px;
  margin-top: 50px
}


#pyramid div:not(.base) {
  position: absolute;
    width: 0;
    height: 0;
    border-left: 100px solid transparent;  /* left arrow slant */
    border-right: 100px solid transparent; /* right arrow slant */
    border-bottom: 100px solid; /* bottom, add background color here */
    font-size: 12px;
    line-height: 0;
  opacity: .5;
}

.base {
  position: absolute;
  width: 200px;
  height: 200px;
  background-color: #ff0000;
  transform: rotateX(80deg) translate3d(0px, 10px, 0px);
}

#pyramid div:nth-child(2) {
  border-bottom-color: blue;//#e04545;
  transform: rotateX(-30deg) rotateY(0deg) rotateZ(0deg) translate3d(0px, 0px, 100px);
}

#pyramid div:nth-child(3) {
  border-bottom-color: yellow;//#ccaf5a;
  transform: rotateX(30deg) rotateY(0deg) rotateZ(0deg) translate3d(0px, 25px, 0px);
}

I am using css3 to create 1 base rectangular div and 4 triangular divs.

I would like some help with the maths involved as well as how to position the elements.

I would like all 4 of the triangle sides to meet at an apex and have the four triangle bottom sides positioned on the rectangular div.

I am struggling with how to get the points of the different triangles to meet in at an apex.

Can anyone help me out with the maths or what logic to use to achieve this.

I'm not looking for code but more the logic or maths I should use.

Upvotes: 2

Views: 2205

Answers (1)

Cimbali
Cimbali

Reputation: 11415

So first, a couple fundamental points on CSS transforms

  • the order of transforms is important
  • the perspective will change with the size of container (#pyramid for you)
  • the origin of the transform is not inherited, and if not set with transform-origin, it is by default the center of the object (borders et al. included, afaict)
  • x is the horizontal axis, y the vertical one and z the one orthogonal to the screen's surface (when you don't have perspective)

Then on pyramids:

pyramid section

Let us look at the Base angle (let's call it α). Expressing the triangle sizes based on the angle α, you get:

  • width = Base
  • apothem (height of triangle) = (1/2 Base) / cos(α)
  • height of the pyramid = (1/2 Base) * tan(α)

In the pyramid of your attempt, Base = 200px thus 1/2 Base = 100px and Apothem = triangle height = 100px. This forces cos(α) = 1 thus α = 0° -- you will have a flat pyramid.

If you want α = 60°, you want apothem = 200px, and you'll get pyramid height = 173.2
If you want α = 45°, you want apothem = 141px, and you'll get pyramid height = 100
etc.

The main secret now you know the parameters, is to reason at each step, about where the axis are pointed, and at which point of the object you are applying the transforms. Paper & pencil or trial & error, whatever works is good.

So here's how I would do to place triangles:

  • put css-transform at the middle of the base of all triangles
  • rotate by a multiple of 90° around axis Y to orient each triangle for a different face of the pyramid (respectively 0, 90, 180, 270)
  • translate along Z by 1/2 Base to get the right position
  • rotate along X axis by the desired angle (90°-α) to make them meet at apex.

Thanks to the first rotation, the two identical last steps will do what you want on all triangles.

For the base, it's easier, you can keep the default css-transform

  • rotate by 90° along X (will make it "flat" on the plane perpendicular to the screen)
  • shift it along Z by (Apothem - 1/2 Base) to align with the base of the triangles

Below is what it would look like for α = 45°, feel free to not open the snippet if you don't want spoilers.

#pyramid {
  perspective: 400px;
  padding: 50px 0 0 200px;
}
#pyramid div:not(.base) {
  position: absolute;
  border-left: 100px solid transparent;   /* 1/2 Base */
  border-right: 100px solid transparent;   /* 1/2 Base */
  border-bottom: 141px solid;  /* Apothem */
  transform-origin: 100px 141px 0; /* bottom of trangle (1/2 Base, Apothem) */
  opacity: .5;
}
.base {
  position: absolute;
  width: 200px;
  height: 200px;
  background-color: #2f2f2f;
  /* transform by default from middle (100px, 100px)
     move by Apothem - 1/2 Base = 41px */
  transform: rotateX(90deg) translate3d(0px, 0px, -41px);
  opacity: .5;
}
#pyramid div:nth-child(2) {
  border-bottom-color: #e04545;
  transform: rotateY(0deg) translate3d(0px, 0px, 100px) rotateX(45deg);
}
#pyramid div:nth-child(3) {
  border-bottom-color: #ccaf5a;
  transform: rotateY(90deg) translate3d(0px, 0px, 100px) rotateX(45deg);
}
#pyramid div:nth-child(4) {
  transform: rotateY(180deg) translate3d(0px, 0px, 100px) rotateX(45deg);
}
#pyramid div:nth-child(5) {
  border-bottom-color: #4ccfc7;
  transform: rotateY(270deg) translate3d(0px, 0px, 100px) rotateX(45deg);
}
<!doctype html>
<html>
<head><title>Pyramid</title></head>
<body>
<div class="pyramid-container">
  <div id="pyramid">
    <div class="base"></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
  </div>  
  </div>
</body>
</html>

Upvotes: 2

Related Questions