HTML5 3D Cube Slideshow

Tutorials

Our new tutorial tells us about creation of animated 3D Cube slideshow (pictures are located within the walls of the cube). The cube itself rotates continuously.

Here are our demo and downloadable package:

Live Demo
download in package

Ok, download the source files and lets start coding !


Step 1. HTML

This is markup of our result slideshow page. Here it is.

index.html

01 <!DOCTYPE html>
02 <html lang="en" >
03     <head>
04         <meta charset="utf-8" />
05         <title>HTML5 3D Cube Slideshow | Script Tutorials</title>
06         <link href="css/main.css" rel="stylesheet" type="text/css" />
07         <script src="js/script.js"></script>
08     </head>
09     <body>
10         <header>
11             <h2>HTML5 3D Cube Slideshow</h2>
12             <a href="https://www.script-tutorials.com/html5-3d-cube-slideshow/" class="stuts">Back to original tutorial on <span>Script Tutorials</span></a>
13         </header>
14         <canvas id="slideshow" width="1280" height="800"></canvas>
15     </body>
16 </html>

Step 2. CSS

css/main.css

That file available in package (because it just contains styles of page layout)

Step 3. JS

js/script.js

001 var canvas, ctx;
002 var aImages = [];
003 var points = [];
004 var triangles = [];
005 var textureWidth, textureHeight;
006 var lev = 3;
007 var angle = 0;
008 // scene vertices
009 var vertices = [
010     new Point3D(-2,-1,2),
011     new Point3D(2,-1,2),
012     new Point3D(2,1,2),
013     new Point3D(-2,1,2),
014     new Point3D(-2,-1,-2),
015     new Point3D(2,-1,-2),
016     new Point3D(2,1,-2),
017     new Point3D(-2,1,-2)
018 ];
019 // scene faces (6 faces)
020 var faces  = [[0,1,2,3],[1,5,6,2],[5,4,7,6],[4,0,3,7],[0,4,5,1],[3,2,6,7]];
021 function Point3D(x,y,z) {
022     this.x = x;
023     this.y = y;
024     this.z = z;
025     this.rotateX = function(angle) {
026         var rad, cosa, sina, y, z
027         rad = angle * Math.PI / 180
028         cosa = Math.cos(rad)
029         sina = Math.sin(rad)
030         y = this.y * cosa - this.z * sina
031         z = this.y * sina + this.z * cosa
032         return new Point3D(this.x, y, z)
033     }
034     this.rotateY = function(angle) {
035         var rad, cosa, sina, x, z
036         rad = angle * Math.PI / 180
037         cosa = Math.cos(rad)
038         sina = Math.sin(rad)
039         z = this.z * cosa - this.x * sina
040         x = this.z * sina + this.x * cosa
041         return new Point3D(x,this.y, z)
042     }
043     this.rotateZ = function(angle) {
044         var rad, cosa, sina, x, y
045         rad = angle * Math.PI / 180
046         cosa = Math.cos(rad)
047         sina = Math.sin(rad)
048         x = this.x * cosa - this.y * sina
049         y = this.x * sina + this.y * cosa
050         return new Point3D(x, y, this.z)
051     }
052     this.projection = function(viewWidth, viewHeight, fov, viewDistance) {
053         var factor, x, y
054         factor = fov / (viewDistance + this.z)
055         x = this.x * factor + viewWidth / 2
056         y = this.y * factor + viewHeight / 2
057         return new Point3D(x, y, this.z)
058     }
059 }
060 // array of photos
061 var aImgs = [
062     'images/pic1.jpg',
063     'images/pic2.jpg',
064     'images/pic3.jpg',
065     'images/pic4.jpg'
066 ];
067 for (var i = 0; i < aImgs.length; i++) {
068     var oImg = new Image();
069     oImg.src = aImgs[i];
070     aImages.push(oImg);
071     oImg.onload = function () {
072         textureWidth = oImg.width;
073         textureHeight = oImg.height;
074     }
075 }
076 window.onload = function(){
077     // creating canvas objects
078     canvas = document.getElementById('slideshow');
079     ctx = canvas.getContext('2d');
080     // prepare points
081     for (var i = 0; i <= lev; i++) {
082         for (var j = 0; j <= lev; j++) {
083             var tx = (i * (textureWidth / lev));
084             var ty = (j * (textureHeight / lev));
085             points.push({
086                 tx: tx,
087                 ty: ty,
088                 nx: tx / textureWidth,
089                 ny: ty / textureHeight,
090                 ox: i,
091                 oy: j
092             });
093         }
094     }
095     // prepare triangles ----
096     var levT = lev + 1;
097     for (var i = 0; i < lev; i++) {
098         for (var j = 0; j < lev; j++) {
099             triangles.push({
100                 p0: points[j + i * levT],
101                 p1: points[j + i * levT + 1],
102                 p2: points[j + (i + 1) * levT],
103                 up: true
104             });
105             triangles.push({
106                 p0: points[j + (i + 1) * levT + 1],
107                 p1: points[j + (i + 1) * levT],
108                 p2: points[j + i * levT + 1],
109                 up: false
110             });
111         }
112     }
113     drawScene();
114 };
115 function drawScene() {
116     // clear context
117     ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
118     // rotate scene
119     var t = new Array();
120     for (var iv = 0; iv < vertices.length; iv++) {
121         var v = vertices[iv];
122         var r = v.rotateY(angle);
123         //var r = v.rotateX(angle).rotateY(angle);
124         var prj = r.projection(ctx.canvas.width, ctx.canvas.height, 1000, 3);
125         t.push(prj)
126     }
127     var avg_z = new Array();
128     for (var i = 0; i < faces.length; i++) {
129         var f = faces[i];
130         avg_z[i] = {"ind":i, "z":(t[f[0]].z + t[f[1]].z + t[f[2]].z + t[f[3]].z) / 4.0};
131     }
132     // get around through all faces
133     for (var i = 0; i < faces.length; i++) {
134         var f = faces[avg_z[i].ind];
135         if (t[f[3]].z+t[f[2]].z+t[f[1]].z+t[f[0]].z > -3) {
136             ctx.save();
137             // draw surfaces
138             ctx.fillStyle = "rgb(160,180,160)";
139             ctx.beginPath();
140             ctx.moveTo(t[f[0]].x,t[f[0]].y);
141             ctx.lineTo(t[f[1]].x,t[f[1]].y);
142             ctx.lineTo(t[f[2]].x,t[f[2]].y);
143             ctx.lineTo(t[f[3]].x,t[f[3]].y);
144             ctx.closePath();
145             ctx.fill();
146             // draw stretched images
147             if (i < 4) {
148                 var ip = points.length;
149                 while (--ip > -1) {
150                     var p = points[ip];
151                     var mx = t[f[0]].x + p.ny * (t[f[3]].x - t[f[0]].x);
152                     var my = t[f[0]].y + p.ny * (t[f[3]].y - t[f[0]].y);
153                     p.px = (mx + p.nx * (t[f[1]].x + p.ny * (t[f[2]].x - t[f[1]].x) - mx)) + p.ox;
154                     p.py = (my + p.nx * (t[f[1]].y + p.ny * (t[f[2]].y - t[f[1]].y) - my)) + p.oy;
155                 }
156                 var n = triangles.length;
157                 while (--n > -1) {
158                     var tri = triangles[n];
159                     var p0 = tri.p0;
160                     var p1 = tri.p1;
161                     var p2 = tri.p2;
162                     var xc = (p0.px + p1.px + p2.px) / 3;
163                     var yc = (p0.py + p1.py + p2.py) / 3;
164                     ctx.save();
165                     ctx.beginPath();
166                     ctx.moveTo((1.05 * p0.px - xc * 0.05), (1.05 * p0.py - yc * 0.05));
167                     ctx.lineTo((1.05 * p1.px - xc * 0.05), (1.05 * p1.py - yc * 0.05));
168                     ctx.lineTo((1.05 * p2.px - xc * 0.05), (1.05 * p2.py - yc * 0.05));
169                     ctx.closePath();
170                     ctx.clip();
171                     // transformation
172                     var d = p0.tx * (p2.ty - p1.ty) - p1.tx * p2.ty + p2.tx * p1.ty + (p1.tx - p2.tx) * p0.ty;
173                     ctx.transform(
174                         -(p0.ty * (p2.px - p1.px) -  p1.ty * p2.px  + p2.ty *  p1.px + (p1.ty - p2.ty) * p0.px) / d, // m11
175                          (p1.ty *  p2.py + p0.ty  * (p1.py - p2.py) - p2.ty *  p1.py + (p2.ty - p1.ty) * p0.py) / d, // m12
176                          (p0.tx * (p2.px - p1.px) -  p1.tx * p2.px  + p2.tx *  p1.px + (p1.tx - p2.tx) * p0.px) / d, // m21
177                         -(p1.tx *  p2.py + p0.tx  * (p1.py - p2.py) - p2.tx *  p1.py + (p2.tx - p1.tx) * p0.py) / d, // m22
178                          (p0.tx * (p2.ty * p1.px  -  p1.ty * p2.px) + p0.ty * (p1.tx *  p2.px - p2.tx  * p1.px) + (p2.tx * p1.ty - p1.tx * p2.ty) * p0.px) / d, // dx
179                          (p0.tx * (p2.ty * p1.py  -  p1.ty * p2.py) + p0.ty * (p1.tx *  p2.py - p2.tx  * p1.py) + (p2.tx * p1.ty - p1.tx * p2.ty) * p0.py) / d  // dy
180                     );
181                     ctx.drawImage(aImages[i], 0, 0);
182                     ctx.restore();
183                 }
184             }
185         }
186     }
187     // shift angle and redraw scene
188     angle += 0.3;
189     setTimeout(drawScene, 40);
190 }

At the first, I have defined all vertices and faces (walls) of our virtual cube. Then I have defined rules of rotating. After – the most difficult thing – transformation of images with using ‘clip’ and ‘transform’.


Live Demo
download in package

Conclusion

I hope that today’s 3D html5 cube lesson has been interesting for you. We have done another one nice html5 example. I will be glad to see your thanks and comments. Good luck!

Rate article