3D Cubes for AliceJS, Part 2

Introduction

I get a lot of questions regarding how a new AliceJS effect is born. Most people are interested particularly about how you make code reusable. Lots of people know how to write an app. Few people know how to write a reusable library or framework. I have seen many examples about how to set-up a 3D Cube, but pretty much all of them take a pure content approach: you code your content, your settings, your CSS all in one big ball of code. In this 3-part article series, I’ll explore how you generalize the concept of a 3D Cube and how you approach it from an API point of view.

In the previous installment, we established the basic pieces of HTML and CSS to create a Cube effect and discovered a few tricks. In this installment, we’ll look at ways to significantly reduce the amount of HTML and CSS an application developer needs to put together to get a cube. Especially, we’ll remove the need to have specialized knowledge around how to achieve this and tuck it away under a simple API. There are many roads to Rome and I don’t profess that this way is the best: I am simply sharing the path I took, and the decisions I made along the way.


Basic API Goals


What is the simplest way to set up a cube? The best way is to always start from the point of view of a developer. If I were to use a framework, I’d want something such as this.

<div id="MyCube1">
  <span> 1 </span>
  <span> 2 </span>
  <span> 3 </span>
  <span> 4 </span>
  <span> 5 </span>
  <span> 6 </span>
</div>
<SCRIPT>
var MyCube1 = new Cube3D("MyCube1");
</SCRIPT>

So, what did we do here compared to the code samples from Part 1?

  • I don’t have to know anything about CSS and what needs to be done to lay out a cube

  • I don’t have to really know about how CSS 3D transforms work and create a new DIV for the container

  • I don’t have to artificially add IDs to my SPANs in order to do the CSS layout

All in all, we now have 11 lines Vs the last sample from Part 1 which had 53 lines. That becomes even more important if I want to have a dozen cubes around and each have slightly different attributes and rotations etc… So let’s get to work.

Modifying the DOM


The first thing we want to do is make sure the DOM is properly setup, and the faces are accounted for. Remember that container layer? We need to insert it. We need to move things around to do this, and it’s pretty simple.

function Cube3D(Id)
 {
   this._id = Id;
   this._e = document.getElementById(Id);

   // Create the container DIV, style it appropriately, and insert it before the Cube DIV
   var containerDiv = document.createElement("div");
   containerDiv.id=Id+"_container";
   this._e.parentNode.insertBefore(containerDiv, this._e);
	 
   // Get the Cube element and move it inside the container DIV 
   this._e.parentNode.removeChild(this._e);
   containerDiv.appendChild(this._e);

Astute readers may ask why I didn’t use DOM fragments… The issue is that we have to remove the Cube DIV from the DOM and then reinsert it back. Those 2 operations are the expensive ones requiring re-layouts. Using DOM fragments, although a best practice when manipulating DOM contents, in this case doesn’t yield much improvement in terms of performance imho.

The next step is to take an inventory of the cube’s Faces. We iterate through the element’s children, and collect all those that are Elements (nodeType==1, see here for more details), and which have a tagName. We can handle SPANs, DIVs, or even FORMs for example. Any proper HTML element will do as a face, but generally, you’ll want DIVs so you can put additional contents in there.

   // Get all the faces
   this._faces = [];
   for (var i = 0; i &lt; this._e.childNodes.length; ++i)
    {
      var e = this._e.childNodes[i];
      if (e.nodeType==1 && e.tagName)
       { 
    	  this._faces.push(e);
       }
    }
 }

Setting up the CSS


At this point, we have handled all the extra work needed for the DOM. What we have to do now is handle the CSS. The new container DIV, the cube DIV, and all the faces have to be styled. We have covered in the previous installment what the CSS needed was, but here, we’ll do it programmatically through JavaScript. Styling the new container DIV is simple

   containerDiv.style.webkitPerspective="1000px";

Styling the cube DIV is also simple.

   var s = this._e.style
   s.width="100%";
   s.height="100%";
   s.position="absolute";
   s.webkitTransformStyle="preserve-3d";
   s.webkitTransition="-webkit-transform 1s";
   s.webkitTransform="rotateX(-25deg) rotateY(25deg)";	 

Finally, in the loop where we collect the faces, we’ll do the styling there

   var s = e.style;
   s.webkitTransform=Cube3D_Rotations[++j];
   s.position="absolute";
   s.width="198px";
   s.height="198px";	
   s.border="2px solid black";
   s.backgroundColor="#EEEEEE";
   s.opacity="0.6";

I hate repeating things, so I have captured the proper CSS for each face in an array, and you’ll recognize the values from our examples in the first installment:

var Cube3D_Rotations =
  ["rotate3d(0, 0, 0,   0deg) translate3d(0px, 0px, 100px)",
   "rotate3d(0, 1, 0,  90deg) translate3d(0px, 0px, 100px)",
   "rotate3d(0, 1, 0, 180deg) translate3d(0px, 0px, 100px)",
   "rotate3d(0, 1, 0, -90deg) translate3d(0px, 0px, 100px)",
   "rotate3d(1, 0, 0,  90deg) translate3d(0px, 0px, 100px)",
   "rotate3d(1, 0, 0, -90deg) translate3d(0px, 0px, 100px)"
  ];

This will be useful for the future. Load up the file toto2a.html in your browser (BlackBerry PlayBook 2 or BlackBerry 10, but Chrome or Safari will do too) and you’ll see the same result as the final example in the previous installment. The differences are stark though and we have achieved most of our goals. The application code is much smaller and simpler, and all the core logic is captured directly inside a reusable JavaScript object.

Tying things up


All is not as good as it could be though. In particular, the size of the faces, and additional styling (such as opacity or borders) really don’t belong into framework code. That is up to the application to decide what they want to do there. In this section, we’ll put a few finishing touches so that we have a nicer, more configurable and more reusable bit.

There are a number of obvious things. For example, the perspective should be driven by the app. Same with things such as transition timing, and initial rotation. We can simply add a few new parameters to the function:

function Cube3D(Id, Perspective, TimingMS, HRot, VRot)

And we change the code in various places to plug those in

   containerDiv.style.webkitPerspective=Perspective;
   …
   s.webkitTransition="-webkit-transform "+TimingMS;
   …
   s.webkitTransform="rotateX("+VRot+"deg) rotateY("+HRot+"deg)";	 

and we now call the constructor differently:

var MyCube1 = new Cube3D("MyCube1", "1000px", "1s", 25, -25);

Then we’d want to extract the face styling out of the framework (only position:absolute is generically relevant).

#MyCube1 span {
   width:  175px;
   height: 175px;
   border: 4px solid black;
   -webkit-border-radius: 30px 30px 30px 30px;   
   background-color: #EEFFEE;
   opacity: 0.8;
}

Here, we have made it a bit different from previously. That’s the whole point: the application should be in control and be able to style the cube in various ways as desired. The framework cannot get in the way of application flexibility. You can check the file toto2b.html to see the result.

Size Does Matter


There are a few things that remain to be done. One of them is size. The astute among you may have noticed that my Face styling hard-codes 100px (the strings in the Cube3D_Rotations array), which is half of the Faces’ app-level styling (in the app’s CSS) which sets their width and height (including border) to 200px. What if I want to make the faces bigger?

In the previous code, at the end of the constructor, we had the following loop body:

    	this._faces.push(e);
    	// Style the face
    	var s = e.style;
        s.webkitTransform=Cube3D_Rotations[++j];
        s.position="absolute";

What we’d want to do is replace translation element in the Cube3D_Rotations array. So we’ll use a simple marker “XXXpx”:

var Cube3D_Rotations =
  ["rotate3d(0, 0, 0,   0deg) translate3d(0px, 0px, XXXpx)",
…
   "rotate3d(1, 0, 0, -90deg) translate3d(0px, 0px, XXXpx)"
  ];

And in the loop, we’d change to

         s.webkitTransform=Cube3D_Rotations[i].replace("XXXpx", Size);

But how do we compute the size? We have some dependencies here, and one way to solve that is to break the loop in two. First, we’ll accumulate the faces and figure out the “envelope” for the cube, and then we’ll reloop through the faces and style them. First we’ll set a new Envelope variable.

   // We need to find the largest width/height of each faces to 
   // calculate the max envelope of the cube.
   var Envelope = 0;

Then in the first loop’s body, we’ll find out the envelope value

   // Query the element's dimensions to figure out the envelope. 
   // We'll take the largest of width or height across all faces.
   if (e.clientWidth &gt; Envelope)
    Envelope = e.clientWidth;
   if (e.clientHeight &gt; Envelope)
    Envelope = e.clientHeight;

Then post loop, we’ll make sure we re-adjust Envelope:

   // If a max size is 200px for example, the envelope is really
   // 100px (half) from the center.
   Envelope = (Envelope/2)+"px";

Finally, in the second loop, we can then use the Envelope’s value to update the translation value in the style:

   // Get all the faces
   for (var i = 0; i &lt; this._faces.length; ++i)
    {
      var s = this._faces[i].style;
      // Style the face with the pre-computed rotations, 
      // translated by the sizes provided
      s.position="absolute";
      s.webkitTransform=Cube3D_Rotations[i].replace("XXXpx", Envelope);
    }

There is a hiccup here however, and that got me thinking… If you run this code, the Envelope will always be 0px! What’s happening? My first thought was that by the time the JavaScript is called, the DOM is not fully in place yet and the elements’ dimensions are not initialized by the CSS styling. I tried to defer the JavaScript code with a setTimeout, or a body.onLoad, all common tactics in most frameworks. But that still didn’t make a difference. I scratched my head and realized (way too late in the night) that in fact, the culprit was positioning! As I unwound the code and put things back in the CSS, it suddenly started working after I put back the position:absolute in the CSS. Back to the JavaScript, I moved the positioning in the first loop, and it just worked.

         // It is __CRITICAL__ to do this here so that the elements 
         // are initialized DOM- and CSS-wise.
         e.style.position="absolute";

         // Query the element's dimensions to figure out the envelope. 
         // We'll take the largest of width or height across all faces.
         if (e.clientWidth > Envelope)
          Envelope = e.clientWidth;
         if (e.clientHeight > Envelope)
          Envelope = e.clientHeight;

And the second loop later in the constructor is now (we no longer set the positioning there):

   // Get all the faces
   for (var i = 0; i < this._faces.length; ++i)
    {
      // Style the face with the pre-computed rotations, 
      // translated by the sizes provided
      this._faces[i].style.webkitTransform=
      Cube3D_Rotations[i].replace("XXXpx", Envelope);
    }

You can see the results in the file toto2c.html. I have added a couple of buttons at the bottom so you can rotate the cubes around… If you do, you’ll notice a pretty severe problem: the center of the 3D animation is all messed up.

Geometry Origin


One CSS 3D concept I am still struggling with is that of Origin, and I am still creating bugs today on stuff related to origins. In the previous code, as you rotate the cubes, they seem to all rotate off some origin that is counter-intuitive: I would like the cubes to rotate each around their centers. Additionally, you can see that the perspective for each cube seems to be different, as if they all keyed off again from some magical point on the page.

For the first issue, it’s simple. There is a CSS property to set up a transform’s origin, and since we just computed the Envelope of the cube, it’s very simple to set that origin to the center of that cube.

  // Make sure that the axis for the cube remains its center, 
  // as pre-computed previously with Envelope
  s.webkitTransformOrigin=Envelope+" "+Envelope+" 0px";

You can check the file toto2d.html for the final results. The cubes now all rotate onto themselves and behave more predictably.

For the second issue, it’s a little bit trickier to explain, and I sent myself on a wild goose chase for that one (thanks to Eli @efidler, our WebKit guru, for pointing me in the right direction). When I started this work, and as shown in the previous installment, I had a single cube which I centered on the page. When I then updated the code, and took advantage of the library to do 3 cubes, something happened that I didn’t notice. To highlight it, check the file toto2e.html. All I did was style the container DIVs to see where they were positioned on the page:

#MyCube1_container {
 background-color: #EEEEEE;
 border: 1px solid #000000;
 width: 50px;
 height: 50px;
}
#MyCube2_container {
 background-color: #EEEEEE;
 border: 1px solid #000000;
 width: 50px;
 height: 50px;
}
#MyCube3_container {
 background-color: #EEEEEE;
 border: 1px solid #ffffff;
 width: 50px;
 height: 50px;
}

And the result looks like this:

Do you see the container DIVs in the upper left corner? These are in fact the perspective origins. So two things happened:


  • Because the DIVs were empty, and un-styled, they occupied no space on the page, and it looked like all three cubes were “perspectived” from a single point on the page. In fact they were each done from three different points, which happened to overlap, causing the illusion of one. With the styling now in place, not only do you see the container DIVs, but they also occupy space (50px here), you can see how this truly worked and how the 3 cubes have always been in fact independent.

  • In the styling in previous examples, I positioned the cube DIVs, not the container DIVs, thus compounding the illusion of a single perspective. The layout was set for each, but their perspective origin still originated from the container DIVs.

The lesson? It’s almost always a good idea to style invisible positional elements at some point to help your debugging, and making clear how your stuff gets laid out. So, fixing is simple: position the containers, not the cubes. The application-level styling now becomes:

#MyCube1_container {
 position: absolute;
 left: 50px;
 top: 100px;
}
#MyCube1 span {
   width:  150px;
   height: 150px;
   border: 2px solid black;
   background-color: #FFDDDD;   
   opacity: 0.8;
}
#MyCube2_container {
 position: absolute;
 left: 450px;
 top: 100px;
}
#MyCube2 span {
   width:  150px;
   height: 150px;
   border: 2px solid black;
   -webkit-border-radius: 30px 30px 30px 30px;   
   background-color: #DDFFDD;
   opacity: 0.8;
}
#MyCube3_container {
 position: absolute;
 left: 800px;
 top: 100px;
}
#MyCube3 span {
   width:  150px;
   height: 150px;
   border: 2px solid black;
   -webkit-border-radius: 10px 10px 10px 10px;
   background-color: #DDDDFF;
   opacity: 0.8;
}

Here, we use absolute positioning, but any positioning as per the application’s requirements, will do. You can see the results in file toto2f.html: all cubes now have a uniform perspective no matter where they are laid out on the screen.

And the beauty of this is that if a developer needs all cubes to have a single point of perspective, they can do that too, and the framework code doesn’t really care. I could style all the containers to be a centered on the page horizontally and vertically and draw multiple cubes on all 4 corners of the page, and they’d look nice. I’ll leave this as an exercise for the reader.

Conclusion


In this installment, we have formalized how a Cube is constructed inside a reusable piece of code, and made it easier for the developer to build them out. In the process, we have figured out a few gotchas and truly separated the application space (where the CSS and visual aspects of the cubes should be specified), and the framework space (where things are now quite generic). In the next installment, we’ll see how we can start adding a few cool features and make this at last an AliceJS effect.

Resources


Toto2a.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Cube Tutorial</title>
</head>
<body>

<SCRIPT>
var Cube3D_Rotations =
  ["rotate3d(0, 0, 0,   0deg) translate3d(0px, 0px, 100px)",
	 "rotate3d(0, 1, 0,  90deg) translate3d(0px, 0px, 100px)",
	 "rotate3d(0, 1, 0, 180deg) translate3d(0px, 0px, 100px)",
	 "rotate3d(0, 1, 0, -90deg) translate3d(0px, 0px, 100px)",
   "rotate3d(1, 0, 0,  90deg) translate3d(0px, 0px, 100px)",
   "rotate3d(1, 0, 0, -90deg) translate3d(0px, 0px, 100px)"
  ];
function Cube3D(Id)
 {
	 this._id = Id;
   this._e = document.getElementById(Id);

   // Create the container DIV, style it appropriately, and insert it before the Cube DIV
	 var containerDiv = document.createElement("div");
	 containerDiv.id=Id+"_container";
	 containerDiv.style.webkitPerspective="1000px";
   this._e.parentNode.insertBefore(containerDiv, this._e);
	 
	 // Get the Cube element and move it inside the container DIV 
	 this._e.parentNode.removeChild(this._e);
	 containerDiv.appendChild(this._e);
	 
	 // Style the Cube element
   var s = this._e.style
   s.width="100%";
   s.height="100%";
   s.position="absolute";
   s.webkitTransformStyle="preserve-3d";
   s.webkitTransition="-webkit-transform 1s";
   s.webkitTransform="rotateX(-25deg) rotateY(25deg)";	 

	 // Get all the faces
   this._faces = [];
   var j = -1;
   for (var i = 0; i < this._e.childNodes.length; ++i)
	  {
	    var e = this._e.childNodes[i];
	    if (e.nodeType==1 && e.tagName)
       { 
    	   this._faces.push(e);
    	   // Style the face
    	   var s = e.style;
         s.webkitTransform=Cube3D_Rotations[++j];
         s.position="absolute";
         s.width="198px";
         s.height="198px";
         s.border="2px solid black";
         s.backgroundColor="#EEEEEE";
         s.opacity="0.6";
       }
	  }

 }
</SCRIPT>

<BR><BR><BR><BR><BR><BR><BR><BR>
<center>
<div id="MyCube1">
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
</div>
</center>
<BR>
<BR>

<SCRIPT>
var MyCube1 = new Cube3D("MyCube1");
</SCRIPT>

<SCRIPT>
//document.getElementById("CUBE1").style.webkitTransform="rotateX(-25deg) rotateY(25deg)";
</SCRIPT>
</body>
</html>

Toto2b.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Cube Tutorial</title>
</head>
<body>

<SCRIPT>
var Cube3D_Rotations =
  ["rotate3d(0, 0, 0,   0deg) translate3d(0px, 0px, 100px)",
	 "rotate3d(0, 1, 0,  90deg) translate3d(0px, 0px, 100px)",
	 "rotate3d(0, 1, 0, 180deg) translate3d(0px, 0px, 100px)",
	 "rotate3d(0, 1, 0, -90deg) translate3d(0px, 0px, 100px)",
   "rotate3d(1, 0, 0,  90deg) translate3d(0px, 0px, 100px)",
   "rotate3d(1, 0, 0, -90deg) translate3d(0px, 0px, 100px)"
  ];
function Cube3D(Id, Perspective, TimingMS, HRot, VRot)
 {
	 this._id = Id;
   this._e = document.getElementById(Id);

   // Create the container DIV, style it appropriately, and insert it before the Cube DIV
	 var containerDiv = document.createElement("div");
	 containerDiv.id=Id+"_container";
	 containerDiv.style.webkitPerspective=Perspective;
   this._e.parentNode.insertBefore(containerDiv, this._e);
	 
	 // Get the Cube element and move it inside the container DIV 
	 this._e.parentNode.removeChild(this._e);
	 containerDiv.appendChild(this._e);
	 
	 // Style the Cube element
   var s = this._e.style
   s.width="100%";
   s.height="100%";
   s.position="absolute";
   s.webkitTransformStyle="preserve-3d";
   s.webkitTransition="-webkit-transform "+TimingMS;
   s.webkitTransform="rotateX("+VRot+"deg) rotateY("+HRot+"deg)";	 

	 // Get all the faces
   this._faces = [];
   var j = -1;
   for (var i = 0; i < this._e.childNodes.length; ++i)
	  {
	    var e = this._e.childNodes[i];
	    if (e.nodeType==1 && e.tagName)
       { 
    	   this._faces.push(e);
    	   // Style the face
    	   var s = e.style;
         s.webkitTransform=Cube3D_Rotations[++j];
         s.position="absolute";
       }
	  }

 }
</SCRIPT>

<STYLE>
#MyCube1 span {
   width:  175px;
   height: 175px;
   border: 4px solid black;
   -webkit-border-radius: 30px 30px 30px 30px;   
   background-color: #EEFFEE;
   opacity: 0.8;
}
</STYLE>

<BR><BR><BR><BR><BR><BR>
<center>
<div id="MyCube1">
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
</div>
</center>
<BR>
<BR>

<SCRIPT>
var MyCube1 = new Cube3D("MyCube1", "1000px", "1s", 25, -25);
</SCRIPT>

<SCRIPT>
//document.getElementById("CUBE1").style.webkitTransform="rotateX(-25deg) rotateY(25deg)";
</SCRIPT>
</body>
</html>

Toto2c.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Cube Tutorial</title>
</head>
<body>

<SCRIPT>
function PrintObject(Obj)
 {
   var Str = "";
   for (prop in Obj)
    Str+=prop + ": " + Obj[prop] + "\n";
   return Str;
 }

// static definitions for the geometry of each face in a cube
var Cube3D_Rotations =
  ["rotate3d(0, 0, 0,   0deg) translate3d(0px, 0px, XXXpx)",
	 "rotate3d(0, 1, 0,  90deg) translate3d(0px, 0px, XXXpx)",
	 "rotate3d(0, 1, 0, 180deg) translate3d(0px, 0px, XXXpx)",
	 "rotate3d(0, 1, 0, -90deg) translate3d(0px, 0px, XXXpx)",
   "rotate3d(1, 0, 0,  90deg) translate3d(0px, 0px, XXXpx)",
   "rotate3d(1, 0, 0, -90deg) translate3d(0px, 0px, XXXpx)"
  ];
function Cube3D(Id, Perspective, TimingMS, HRot, VRot)
 {
	 this._id = Id;
   this._e = document.getElementById(Id);
   this.HRot = HRot;
   this.VRot = VRot;

   // We need to find the largest width/height of each faces to calculate the max envelope of the cube.
   var Envelope = 0;
   // Let's accumulate the faces
   this._faces = [];
   for (var i = 0; i < this._e.childNodes.length; ++i)
    {
      var e = this._e.childNodes[i];
      if (e.nodeType==1 && e.tagName)
       {
         this._faces.push(e);
         // It is __CRITICAL__ to do this here so that the elements are initialized DOM- and CSS-wise.
         e.style.position="absolute";
         // Query the element's dimensions to figure out the envelope. We'll take the 
         // largest of width or height across all faces.
         if (e.clientWidth > Envelope)
          Envelope = e.clientWidth;
         if (e.clientHeight > Envelope)
          Envelope = e.clientHeight;
       }
    }
   // If a size is 200px for example, the envelope is really 100px (half) from the center.
   Envelope = (Envelope/2)+"px";

   // Create the container DIV
	 var containerDiv = document.createElement("div");
	 containerDiv.id=Id+"_container";
	 // Style the container DIV
	 containerDiv.style.webkitPerspective=Perspective;
	 // Insert the container DIV before the cube element
   this._e.parentNode.insertBefore(containerDiv, this._e);
   // Remove the cube element from DOM 
	 this._e.parentNode.removeChild(this._e);
   // Place it in the container DIV
	 containerDiv.appendChild(this._e);
	 
	 // Style the Cube element
   var s = this._e.style
   s.position="relative";
	 // Nice 3D effect
   s.webkitTransformStyle="preserve-3d";
	 // Transition timing
   s.webkitTransition="-webkit-transform "+TimingMS;
	 // Initial rotation
   s.webkitTransform="rotateX("+VRot+"deg) rotateY("+HRot+"deg)";

	 // Get all the faces
   for (var i = 0; i < this._faces.length; ++i)
	  {
      // Style the face with the pre-computed rotations, translated by the sizes provided
	    this._faces[i].style.webkitTransform=Cube3D_Rotations[i].replace("XXXpx", Envelope);
	  }
 }
</SCRIPT>

<STYLE>
#MyCube1 {
 left: 50px;
 top: 100px;
}
#MyCube1 span {
   width:  150px;
   height: 150px;
   border: 2px solid black;
   background-color: #FFDDDD;   
   opacity: 0.8;
}
#MyCube2 {
   left: 450px;
   top: 100px;
}
#MyCube2 span {
   width:  150px;
   height: 150px;
   border: 2px solid black;
   -webkit-border-radius: 30px 30px 30px 30px;   
   background-color: #DDFFDD;
   opacity: 0.8;
}
#MyCube3 {
 left: 800px;
 top: 100px;
}
#MyCube3 span {
   width:  150px;
   height: 150px;
   border: 2px solid black;
   -webkit-border-radius: 10px 10px 10px 10px;
   background-color: #DDDDFF;
   opacity: 0.8;
}
</STYLE>

<div id="MyCube1">
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
</div>

<div id="MyCube2">
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
</div>

<div id="MyCube3">
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
</div>

<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR>
<button onClick="Rotate(-15, 0)">Rotate Vertical</button>
<button onClick="Rotate( 0, 15)">Rotate Horizontal</button>

<SCRIPT>
var MyCube1 = new Cube3D("MyCube1", "1000px", "1s", 20, -20);
var MyCube2 = new Cube3D("MyCube2", "1000px", "1s", 20, -20);
var MyCube3 = new Cube3D("MyCube3", "1000px", "1s", 20, -20);
var vr=-20;
var hr=20;
function Rotate(VRot, HRot)
 {
   vr+=VRot;
   hr+=HRot;
   document.getElementById("MyCube1").style.webkitTransform="rotateX("+vr+"deg) rotateY("+hr+"deg)";
   document.getElementById("MyCube2").style.webkitTransform="rotateX("+vr+"deg) rotateY("+hr+"deg)";
   document.getElementById("MyCube3").style.webkitTransform="rotateX("+vr+"deg) rotateY("+hr+"deg)";
 }
</SCRIPT>

</body>
</html>

Toto2d.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Cube Tutorial</title>
</head>
<body>

<SCRIPT>
function PrintObject(Obj)
 {
   var Str = "";
   for (prop in Obj)
    Str+=prop + ": " + Obj[prop] + "\n";
   return Str;
 }

// static definitions for the geometry of each face in a cube
var Cube3D_Rotations =
  ["rotate3d(0, 0, 0,   0deg) translate3d(0px, 0px, XXXpx)",
	 "rotate3d(0, 1, 0,  90deg) translate3d(0px, 0px, XXXpx)",
	 "rotate3d(0, 1, 0, 180deg) translate3d(0px, 0px, XXXpx)",
	 "rotate3d(0, 1, 0, -90deg) translate3d(0px, 0px, XXXpx)",
   "rotate3d(1, 0, 0,  90deg) translate3d(0px, 0px, XXXpx)",
   "rotate3d(1, 0, 0, -90deg) translate3d(0px, 0px, XXXpx)"
  ];
function Cube3D(Id, Perspective, TimingMS, HRot, VRot)
 {
	 this._id = Id;
   this._e = document.getElementById(Id);
   this.HRot = HRot;
   this.VRot = VRot;

   // We need to find the largest width/height of each faces to calculate the max envelope of the cube.
   var Envelope = 0;
   // Let's accumulate the faces
   this._faces = [];
   for (var i = 0; i < this._e.childNodes.length; ++i)
    {
      var e = this._e.childNodes[i];
      if (e.nodeType==1 && e.tagName)
       {
         this._faces.push(e);
         // It is __CRITICAL__ to do this here so that the elements are initialized DOM- and CSS-wise.
         e.style.position="absolute";
         // Query the element's dimensions to figure out the envelope. We'll take the 
         // largest of width or height across all faces.
         if (e.clientWidth > Envelope)
          Envelope = e.clientWidth;
         if (e.clientHeight > Envelope)
          Envelope = e.clientHeight;
       }
    }
   // If a size is 200px for example, the envelope is really 100px (half) from the center.
   Envelope = (Envelope/2)+"px";

   // Create the container DIV
	 var containerDiv = document.createElement("div");
	 containerDiv.id=Id+"_container";
	 // Style the container DIV
	 containerDiv.style.webkitPerspective=Perspective;
	 // Insert the container DIV before the cube element
   this._e.parentNode.insertBefore(containerDiv, this._e);
   // Remove the cube element from DOM 
	 this._e.parentNode.removeChild(this._e);
   // Place it in the container DIV
	 containerDiv.appendChild(this._e);
	 
	 // Style the Cube element
   var s = this._e.style
   s.position="relative";
	 // Nice 3D effect
   s.webkitTransformStyle="preserve-3d";
	 // Make sure that the axis for the cube remains its center, as 
	 // pre-computed previously with Envelope
   s.webkitTransformOrigin=Envelope+" "+Envelope+" 0px";
	 // Transition timing
   s.webkitTransition="-webkit-transform "+TimingMS;
	 // Initial rotation
   s.webkitTransform="rotateX("+VRot+"deg) rotateY("+HRot+"deg)";

	 // Get all the faces
   for (var i = 0; i < this._faces.length; ++i)
	  {
      // Style the face with the pre-computed rotations, translated by the sizes provided
	    this._faces[i].style.webkitTransform=Cube3D_Rotations[i].replace("XXXpx", Envelope);
	  }
 }
</SCRIPT>

<STYLE>
#MyCube1 {
 left: 50px;
 top: 100px;
}
#MyCube1 span {
   width:  150px;
   height: 150px;
   border: 2px solid black;
   background-color: #FFDDDD;   
   opacity: 0.8;
}
#MyCube2 {
   left: 450px;
   top: 100px;
}
#MyCube2 span {
   width:  150px;
   height: 150px;
   border: 2px solid black;
   -webkit-border-radius: 30px 30px 30px 30px;   
   background-color: #DDFFDD;
   opacity: 0.8;
}
#MyCube3 {
 left: 800px;
 top: 100px;
}
#MyCube3 span {
   width:  150px;
   height: 150px;
   border: 2px solid black;
   -webkit-border-radius: 10px 10px 10px 10px;
   background-color: #DDDDFF;
   opacity: 0.8;
}
</STYLE>

<div id="MyCube1">
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
</div>

<div id="MyCube2">
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
</div>

<div id="MyCube3">
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
</div>

<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR>
<button onClick="Rotate(-15, 0)">Rotate Vertical</button>
<button onClick="Rotate( 0, 15)">Rotate Horizontal</button>

<SCRIPT>
var MyCube1 = new Cube3D("MyCube1", "1000px", "1s", 20, -20);
var MyCube2 = new Cube3D("MyCube2", "1000px", "1s", 20, -20);
var MyCube3 = new Cube3D("MyCube3", "1000px", "1s", 20, -20);
var vr=-20;
var hr=20;
function Rotate(VRot, HRot)
 {
   vr+=VRot;
   hr+=HRot;
   document.getElementById("MyCube1").style.webkitTransform="rotateX("+vr+"deg) rotateY("+hr+"deg)";
   document.getElementById("MyCube2").style.webkitTransform="rotateX("+vr+"deg) rotateY("+hr+"deg)";
   document.getElementById("MyCube3").style.webkitTransform="rotateX("+vr+"deg) rotateY("+hr+"deg)";
 }
</SCRIPT>

</body>
</html>

Toto2e.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Cube Tutorial</title>
</head>
<body>

<SCRIPT>
function PrintObject(Obj)
 {
   var Str = "";
   for (prop in Obj)
    Str+=prop + ": " + Obj[prop] + "\n";
   return Str;
 }

// static definitions for the geometry of each face in a cube
var Cube3D_Rotations =
  ["rotate3d(0, 0, 0,   0deg) translate3d(0px, 0px, XXXpx)",
	 "rotate3d(0, 1, 0,  90deg) translate3d(0px, 0px, XXXpx)",
	 "rotate3d(0, 1, 0, 180deg) translate3d(0px, 0px, XXXpx)",
	 "rotate3d(0, 1, 0, -90deg) translate3d(0px, 0px, XXXpx)",
   "rotate3d(1, 0, 0,  90deg) translate3d(0px, 0px, XXXpx)",
   "rotate3d(1, 0, 0, -90deg) translate3d(0px, 0px, XXXpx)"
  ];
function Cube3D(Id, Perspective, TimingMS, HRot, VRot)
 {
	 this._id = Id;
   this._e = document.getElementById(Id);
   this.HRot = HRot;
   this.VRot = VRot;

   // We need to find the largest width/height of each faces to calculate the max envelope of the cube.
   var Envelope = 0;
   // Let's accumulate the faces
   this._faces = [];
   for (var i = 0; i < this._e.childNodes.length; ++i)
    {
      var e = this._e.childNodes[i];
      if (e.nodeType==1 && e.tagName)
       {
         this._faces.push(e);
         // It is __CRITICAL__ to do this here so that the elements are initialized DOM- and CSS-wise.
         e.style.position="absolute";
         // Query the element's dimensions to figure out the envelope. We'll take the 
         // largest of width or height across all faces.
         if (e.clientWidth > Envelope)
          Envelope = e.clientWidth;
         if (e.clientHeight > Envelope)
          Envelope = e.clientHeight;
       }
    }
   // If a size is 200px for example, the envelope is really 100px (half) from the center.
   Envelope = (Envelope/2)+"px";

   // Create the container DIV
	 var containerDiv = document.createElement("div");
	 containerDiv.id=Id+"_container";
	 // Style the container DIV
	 containerDiv.style.webkitPerspective=Perspective;
	 // Insert the container DIV before the cube element
   this._e.parentNode.insertBefore(containerDiv, this._e);
   // Remove the cube element from DOM 
	 this._e.parentNode.removeChild(this._e);
   // Place it in the container DIV
	 containerDiv.appendChild(this._e);
	 
	 // Style the Cube element
   var s = this._e.style
   s.position="relative";
	 // Nice 3D effect
   s.webkitTransformStyle="preserve-3d";
	 // Make sure that the axis for the cube remains its center, as 
	 // pre-computed previously with Envelope
   s.webkitTransformOrigin=Envelope+" "+Envelope+" 0px";
	 // Transition timing
   s.webkitTransition="-webkit-transform "+TimingMS;
	 // Initial rotation
   s.webkitTransform="rotateX("+VRot+"deg) rotateY("+HRot+"deg)";

	 // Get all the faces
   for (var i = 0; i < this._faces.length; ++i)
	  {
      // Style the face with the pre-computed rotations, translated by the sizes provided
	    this._faces[i].style.webkitTransform=Cube3D_Rotations[i].replace("XXXpx", Envelope);
	  }
 }
</SCRIPT>

<STYLE>
#MyCube1 {
 left: 50px;
 top: 100px;
}
#MyCube1 span {
   width:  150px;
   height: 150px;
   border: 2px solid black;
   background-color: #FFDDDD;   
   opacity: 0.8;
}
#MyCube2 {
   left: 450px;
   top: 100px;
}
#MyCube2 span {
   width:  150px;
   height: 150px;
   border: 2px solid black;
   -webkit-border-radius: 30px 30px 30px 30px;   
   background-color: #DDFFDD;
   opacity: 0.8;
}
#MyCube3 {
 left: 800px;
 top: 100px;
}
#MyCube3 span {
   width:  150px;
   height: 150px;
   border: 2px solid black;
   -webkit-border-radius: 10px 10px 10px 10px;
   background-color: #DDDDFF;
   opacity: 0.8;
}

#MyCube1_container {
 background-color: #EEEEEE;
 border: 1px solid #000000;
 width: 50px;
 height: 50px;
}
#MyCube2_container {
 background-color: #EEEEEE;
 border: 1px solid #000000;
 width: 50px;
 height: 50px;
}
#MyCube3_container {
 background-color: #EEEEEE;
 border: 1px solid #000000;
 width: 50px;
 height: 50px;
}



</STYLE>

<div id="MyCube1">
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
</div>

<div id="MyCube2">
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
</div>

<div id="MyCube3">
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
</div>

<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR>
<button onClick="Rotate(-15, 0)">Rotate Vertical</button>
<button onClick="Rotate( 0, 15)">Rotate Horizontal</button>

<SCRIPT>
var MyCube1 = new Cube3D("MyCube1", "1000px", "1s", 20, -20);
var MyCube2 = new Cube3D("MyCube2", "1000px", "1s", 20, -20);
var MyCube3 = new Cube3D("MyCube3", "1000px", "1s", 20, -20);
var vr=-20;
var hr=20;
function Rotate(VRot, HRot)
 {
   vr+=VRot;
   hr+=HRot;
   document.getElementById("MyCube1").style.webkitTransform="rotateX("+vr+"deg) rotateY("+hr+"deg)";
   document.getElementById("MyCube2").style.webkitTransform="rotateX("+vr+"deg) rotateY("+hr+"deg)";
   document.getElementById("MyCube3").style.webkitTransform="rotateX("+vr+"deg) rotateY("+hr+"deg)";
 }
</SCRIPT>

</body>
</html>

Toto2f.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Cube Tutorial</title>
</head>
<body>

<SCRIPT>
function PrintObject(Obj)
 {
   var Str = "";
   for (prop in Obj)
    Str+=prop + ": " + Obj[prop] + "\n";
   return Str;
 }

// static definitions for the geometry of each face in a cube
var Cube3D_Rotations =
  ["rotate3d(0, 0, 0,   0deg) translate3d(0px, 0px, XXXpx)",
	 "rotate3d(0, 1, 0,  90deg) translate3d(0px, 0px, XXXpx)",
	 "rotate3d(0, 1, 0, 180deg) translate3d(0px, 0px, XXXpx)",
	 "rotate3d(0, 1, 0, -90deg) translate3d(0px, 0px, XXXpx)",
   "rotate3d(1, 0, 0,  90deg) translate3d(0px, 0px, XXXpx)",
   "rotate3d(1, 0, 0, -90deg) translate3d(0px, 0px, XXXpx)"
  ];
function Cube3D(Id, Perspective, TimingMS, HRot, VRot)
 {
	 this._id = Id;
   this._e = document.getElementById(Id);
   this.HRot = HRot;
   this.VRot = VRot;

   // We need to find the largest width/height of each faces to calculate the max envelope of the cube.
   var Envelope = 0;
   // Let's accumulate the faces
   this._faces = [];
   for (var i = 0; i < this._e.childNodes.length; ++i)
    {
      var e = this._e.childNodes[i];
      if (e.nodeType==1 && e.tagName)
       {
         this._faces.push(e);
         // It is __CRITICAL__ to do this here so that the elements are initialized DOM- and CSS-wise.
         e.style.position="absolute";
         // Query the element's dimensions to figure out the envelope. We'll take the 
         // largest of width or height across all faces.
         if (e.clientWidth > Envelope)
          Envelope = e.clientWidth;
         if (e.clientHeight > Envelope)
          Envelope = e.clientHeight;
       }
    }
   // If a size is 200px for example, the envelope is really 100px (half) from the center.
   Envelope = (Envelope/2)+"px";

   // Create the container DIV
	 var containerDiv = document.createElement("div");
	 containerDiv.id=Id+"_container";
	 // Style the container DIV
	 containerDiv.style.webkitPerspective=Perspective;
	 // Insert the container DIV before the cube element
   this._e.parentNode.insertBefore(containerDiv, this._e);
   // Remove the cube element from DOM 
	 this._e.parentNode.removeChild(this._e);
   // Place it in the container DIV
	 containerDiv.appendChild(this._e);
	 
	 // Style the Cube element
   var s = this._e.style
   s.position="relative";
	 // Nice 3D effect
   s.webkitTransformStyle="preserve-3d";
	 // Make sure that the axis for the cube remains its center, as 
	 // pre-computed previously with Envelope
   s.webkitTransformOrigin=Envelope+" "+Envelope+" 0px";
	 // Transition timing
   s.webkitTransition="-webkit-transform "+TimingMS;
	 // Initial rotation
   s.webkitTransform="rotateX("+VRot+"deg) rotateY("+HRot+"deg)";

	 // Get all the faces
   for (var i = 0; i < this._faces.length; ++i)
	  {
      // Style the face with the pre-computed rotations, translated by the sizes provided
	    this._faces[i].style.webkitTransform=Cube3D_Rotations[i].replace("XXXpx", Envelope);
	  }
 }
</SCRIPT>

<STYLE>
#MyCube1_container {
 position: absolute;
 left: 50px;
 top: 100px;
}
#MyCube1 span {
   width:  150px;
   height: 150px;
   border: 2px solid black;
   background-color: #FFDDDD;   
   opacity: 0.8;
}
#MyCube2_container {
 position: absolute;
 left: 450px;
 top: 100px;
}
#MyCube2 span {
   width:  150px;
   height: 150px;
   border: 2px solid black;
   -webkit-border-radius: 30px 30px 30px 30px;   
   background-color: #DDFFDD;
   opacity: 0.8;
}
#MyCube3_container {
 position: absolute;
 left: 800px;
 top: 100px;
}
#MyCube3 span {
   width:  150px;
   height: 150px;
   border: 2px solid black;
   -webkit-border-radius: 10px 10px 10px 10px;
   background-color: #DDDDFF;
   opacity: 0.8;
}

</STYLE>

<div id="MyCube1">
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
</div>

<div id="MyCube2">
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
</div>

<div id="MyCube3">
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
  <span> </span>
</div>

<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR>
<button onClick="Rotate(-15, 0)">Rotate Vertical</button>
<button onClick="Rotate( 0, 15)">Rotate Horizontal</button>

<SCRIPT>
var MyCube1 = new Cube3D("MyCube1", "1000px", "1s", 20, -20);
var MyCube2 = new Cube3D("MyCube2", "1000px", "1s", 20, -20);
var MyCube3 = new Cube3D("MyCube3", "1000px", "1s", 20, -20);
var vr=-20;
var hr=20;
function Rotate(VRot, HRot)
 {
   vr+=VRot;
   hr+=HRot;
   document.getElementById("MyCube1").style.webkitTransform="rotateX("+vr+"deg) rotateY("+hr+"deg)";
   document.getElementById("MyCube2").style.webkitTransform="rotateX("+vr+"deg) rotateY("+hr+"deg)";
   document.getElementById("MyCube3").style.webkitTransform="rotateX("+vr+"deg) rotateY("+hr+"deg)";
 }
</SCRIPT>

</body>
</html>
Advertisements

About ldhasson

Software guy, a film and music nut, blue hair, NYC. All opinions and ramblings are my own and do not necessarily reflect my employer's views.
This entry was posted in HTML5. Bookmark the permalink.

2 Responses to 3D Cubes for AliceJS, Part 2

  1. Pingback: 3D Cubes for AliceJS, Part 1 | Open BlackBerry News

  2. Pingback: 3D Cubes for AliceJS, Part 1 « All i know is that i know nothing

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s