One of the most challenging aspects of using CSS 3D Transforms is getting your head around how all the different elements are positioned in relation to one another.
Creating something as straightforward as a cube, for example, can quickly become really complex when you attempt to move or rotate the six different faces that make up the box, while maintaining their relative positions.
Thankfully, with a bit of JavaScript many of these headaches can be resolved – leaving you to get creative with the potential of this fantastic addition to the CSS specification! Sprite3D abstracts the 3D Transforms offered by CSS3, and also makes it really simple to create basic three-dimensional elements such as cubes.
In this tutorial, we’re going to examine how to make the most of the Sprite3D JavaScript library, and once we’ve got a handle on the basics, we’ll go on to create a simple 3D scene complete with animations.
Set up the page
Our basic page contains nothing inside the body element other than two links to script files: Sprite3D.js (the library we’re using to make our scene easier to handle) and 3d.js, which is our own code we’ll use to generate the scene. We’ve also created and linked a simple stylesheet that defines some background colours for different faces of a cube.
001 <html lang=”en”>
002 <head>
003 <meta charset=”utf-8” />
004 <meta name=’HandheldFriendly’ content=’True’ />
005 <meta name=’viewport’ content=’width=device-width, initial-scale=0.7, minimum-scale=0.7, maximum-scale=0.7, user- scalable=0’ />
006 <title>Create Easy 3D scenes with Sprite3D</title>
007 <link rel=”stylesheet” type=”text/css” href=”styles/screen.css” />
008 <head> 009 <body>
010 <script src=”scripts/Sprite3D.js” type=”text/javascript”></script>
011 <body>
012 <html>
Begin with a box…
Something that’s pretty complicated to work out in vanilla CSS and HTML are the dimensions and positioning of a cube. Sprite3D makes this exceptionally simple, Indeed, using the box() method we can define the width, height and depth of a cube, and the library will create it for us – making sure that all the faces are positioned correctly, and naming them accordingly for us to hook our CSS into.
001 Create our 3D scene using Sprite3D
002 (function(){
003 // display 3D content
004 // Create the stage var stage = Sprite3D.stage();
005 // Create a box in 3D
006 var cube = Sprite3D.box( 200, 200, 200,”.box”);
007 stage.appendChild(cube); Sprite3D</title>
008 } else {
009 // display warning or alternative content
010 alert(“Sorry, your browser doesn’t support this content”);
011 }
Rotate the box
We can easily rotate the box randomly by using the rotation() method on our box object. To get this up and running let’s set up a timed interval that fires every three seconds and changes the rotation value. CSS3 transitions will handle the animation for us, so the only thing we need to worry about is creating the change in values every few seconds.
001 var i = setInterval(function(){
002 cube.css(“Transition”, “all “ + (.3+Math. random()*.3)+”s linear”, true )
003 .rotation( Math.random()*60-30, Math. random()*60-30, 0 )
004 .z( Math.random() * 300 - 150 )
005 .update();
006 },1000);
Create many boxes
We can use a for loop and an array to create many boxes instead of sticking with just one. This time, we’ll also position each box randomly using Sprite3D’s position method – this is an abstraction of translate3D, and makes it easy to get our boxes into different 3D locations. Add the code shown in place of the existing box creation code to see this in action.
001 (function(){
002 if ( Sprite3D.isSupported() ) {
003 // display 3D content
004 // Create the stage
005 var cube = new Array();
006 var stage = Sprite3D.stage();
007 var container = Sprite3D. create(“#container”);
008 stage.appendChild( container );
009 for (var i=0;i<10;i++) {
010 cube[i] = Sprite3D.box( 200, 200, 200,”. box”+i );
011 container.appendChild(cube[i]);
012 cube[i].move(parseInt(Math. random()*1000-500),parseInt(Math.random()*1000- 500),parseInt(Math.random()*1000-500)). update();
013 }
014 var i = setInterval(function(){
015 container.css(“Transition”, “all “ + (.3+Math.random()*.3)+”s linear”, true )
016 .rotation( Math.random()*60-30, Math.random()*60-30, 0 )
017 .z( Math.random() * 300 - 150 )
018 .update();
019 },1000);
020 } else {
021 // display warning or alternative content
022 alert(“Sorry, your browser doesn’t support this content”);
023 } }());
Create a container
Now we have got lots of boxes, it doesn’t work if we just rotate the cubes, as we would need to calculate the respective rotation individually. Instead, we can rotate the container. First we need to add a container to rotate, and then make our boxes children of that container. Note, we can’t rotate the stage itself – otherwise strange, undesirable things will happen!
001 var container = Sprite3D. create(“#container”);
002 stage.appendChild( container ); …
003 var i = setInterval(function(){
004 container.css(“Transition”, “all “ + (.3+Math.random()*.3)+”s linear”, true )
005 .rotation( Math.random()*60-30, Math. random()*60-30, 0 )
006 .z( Math.random() * 300 - 150 )
007 .update();
008 },1000);
Build up the scene
Now you’ve got the idea of how Sprite3D works, it’s time to create something a little more complex than the cubes. We’ll start off by removing all the boxes and placing a floor into our scene. We need to rotate it through 90 degrees on the x axis to position it as a floor, and we’ll also move it halfway to the left so it aligns with the centre of the page.
001 var trees = new Array();
002 var stage = Sprite3D.stage();
003 var container = Sprite3D. create(“#container”);
004 stage.appendChild( container );
005 var floor = Sprite3D.create(“#floor”). position(-500,0,0).rotation(90,0,0).update();
006 container.appendChild(floor);
Grow some trees
Using the same approach we employed to make multiple boxes, we’ll now create a series of div’s to represent trees in our scene. This time, we won’t position the trees randomly in all directions as we want them to stick to the floor. Add the following code, which is a variation of the code from step 4, to generate trees and scatter them across the ground.
001 for (var x=0;x<8;x++) {
002 trees[x] = Sprite3D.create(“.tree”). position(0-parseInt(Math.random()*800)+300,- 150,0-parseInt(Math.random()*300-150)). update();
003 container.appendChild(trees[x]); }
Make a car
Now we’ve got some trees in place, let’s build a car. Start by
creating a single element and see how it looks. Once you’re happy with the basic shape, introduce some depth by using the same array approach, but positioning each copy of the car a single pixel back in 3D space from the previous one.
001 // Create the car
002 for (var x=0;x<20;x++) {
003 // We create 20 copies stacked against each other to create a 3D look
004 car[x] = Sprite3D.create(“.car”). position(-500,130,200+x).update();
005 container.appendChild(car[x]);
Get the car moving
We can move our car randomly by using a repeating event that fires every few seconds. When this function is called, it will arbitrarily move each copy of the car to a new position, and CSS transitions will then make it slide into place. Using a cubic-bezier transition sees the car shudder into its final position rather than arrive smoothly for a more realistic finish.
001 // Move the car back and forth
002 var carinterval = setInterval(function(){
003 randomx = 0-parseInt(Math. random()*800)+300;
004 for (var x=0;x<20;x++) {
005 car[x].css(“Transition”, “all 2s cubic- bezier(0.745, 1.650, 0.480, 0.660)”, true ) 006 .position(randomx,130,200+x)
007 .update();
008 }
009 }, 3000);
Add other elements
Using the same techniques we’ve seen, add any other elements that you’d like to see in place at this point. We’ve added a ‘cuff’ to the front of our floor <div> to provide additional depth to the piece, and we have also dropped in a sky at the back to serve as a focal point and horizon line for the scene.
Bring in graphics
Now we’ve got the basics of our scene, it’s time to bring in some real graphics instead of relying on coloured boxes to understand how the final composition will look. Edit the stylesheet and add entries for .tree, #floor and .car to bring in graphics and set the <div> sizes. You’ll need to adjust for the new dimensions in your Sprite3D function calls.
Add some clouds
Let’s add some further depth and interest to our scene by putting in some cloud elements. Copy and paste the tree generation code, changing the references from ‘tree’ to ‘cloud’. Add an extra method call to the scale() function in the code chain and set the scale to be something random between 0.1 and 1.0.
001 // Create the clouds
002 for (var x=0;x<8;x++) {
003 randomscale = (parseInt(Math. random()*10)/10)+0.3;
004 cloud[x] = Sprite3D.create(“.cloud”). position(0-parseInt(Math.random()*1000)+500,0- parseInt(Math.random()*300)-100,- 230+(parseInt(Math.random()*250))). scale(randomscale).update();
005 container.appendChild(cloud[x]);
Move the clouds
We can use the same code we’ve got for our car to move the clouds across our scene. However, instead of using the cubic-bezier transition, we’ll stick with linear, and while we’re there we can also change the number of pixels we move the cloud according to its scale. In this way, we’ll get a nice feeling of depth and speed variance.
001 // Move the clouds across the scene
002 var cloudinterval = setInterval(function(){
003 for (var x=0;x<8;x++) {
004 cloud[x].css(“Transition”, “all 2s linear”, true )
005 .move(100*cloud[x].scaleX(),0,0)
006 .update();
007 }
008 }, 2000);
Repeat the clouds
As it stands, our clouds will move off the side of our scene and keep going for ever. We need to get them to move back to the left as they hit the right. We could just make them disappear and reappear, but instead by adding the code below we can push them back in 3D space behind the sky, and shoot them back to the beginning so they drift in a perpetual cycle.
001 if (cloud[x].x()>600) {
002 cloud[x].z(cloud[x].z()-320).update(); }
003 if (cloud[x].z()<=-550) {
004 cloud[x].x(-880).update(); }
005 if (cloud[x].x()<=-700 &&
006 cloud[x].z()<=-240) {
007 cloud[x].z(cloud[x].z()+320).update(); }
Final elements
Now we’ve got the clouds in place, add any final elements you’d like to see in your scene. We’ve created a sign on strings that drops down after an eight-second delay, and we’ve also included a rainbow for added depth and interest. Keep adding elements to your scene until you’re happy with the overall look.
001 var rainbow = Sprite3D. create(“#rainbow”).position(-500,-285,-109). update();
002 var sign = Sprite3D.create(“#sign”). position(-200,-2000,120).update();
003 var grass = Sprite3D.create(“#grass”). position(-500,139,251).update();
004 container.appendChild(base);
005 container.appendChild(floor);
006 container.appendChild(cuff);
007 container.appendChild(sky);
008 container.appendChild(rainbow);
009 container.appendChild(sign);
010 container.appendChild(grass);
Twist the trees
Once we test our scene, we decide that our trees need a little more depth, so we’ve added a rotated version of each tree to create an ‘X’ shape in 3D space, with the two copies of each tree intersecting each other. Add the code below to create the same effect. This is very similar to our car with 20 copies for depth.
001 // Create the trees
002 for (var x=0;x<5;x++) {
003 var rotationangle = parseInt(Math.random()*90);
004 var posx = 0-parseInt(Math. random()*800)+300;
005 var posz = 0-parseInt(Math. random()*200)+50;
006 treename = “.tree”+parseInt(Math. random()*3).toString();
007 trees[x] = Sprite3D.create(treename). position(posx,15,posz). rotate(0,rotationangle,0).update();
008 treename = “.tree”+parseInt(Math. random()*3).toString();
009 treesrot[x] = Sprite3D. create(treename).position(posx,15,posz). rotate(0,rotationangle+90,0).update();
010 container.appendChild(trees[x]);
011 container.appendChild(treesrot[x]); }
Last tweaks
Finally, now you’ve got a fully working scene, it’s time to adjust the settings for all your elements so they work together. We’ve amended the animation speed of the car, the easing for the movement and the delay for the sign dropping down. Tweak the design to taste and test across different browsers, replacing graphics and altering the position of elements accordingly.