2dcanvas.js
  1 /*{# Copyright (c) 2011-2012 Turbulenz Limited #}*/
  2 /*
  3 * @title: 2D Canvas
  4 * @description:
  5 * This sample demonstrates some of the capabilities of the Canvas 2D API.
  6 * It shows how to render lines, rectangles, circles, arcs, polygons, textures and linear and radial gradients using the Canvas 2D API.
  7 * It also shows a frames per second counter to get a measure of the API performance.
  8 */
  9 /*{# Import additional JS files here #}*/
 10 /*{{ javascript("scripts/htmlcontrols.js") }}*/
 11 /*{{ javascript("jslib/observer.js") }}*/
 12 /*{{ javascript("jslib/requesthandler.js") }}*/
 13 /*{{ javascript("jslib/utilities.js") }}*/
 14 /*{{ javascript("jslib/services/turbulenzservices.js") }}*/
 15 /*{{ javascript("jslib/services/turbulenzbridge.js") }}*/
 16 /*{{ javascript("jslib/services/gamesession.js") }}*/
 17 /*{{ javascript("jslib/services/mappingtable.js") }}*/
 18 /*{{ javascript("jslib/canvas.js") }}*/
 19 /*global TurbulenzEngine: true */
 20 /*global TurbulenzServices: false */
 21 /*global RequestHandler: false */
 22 /*global Canvas: false */
 23 TurbulenzEngine.onload = function onloadFn() {
 24     if (!TurbulenzEngine.canvas) {
 25         var graphicsDeviceParameters = {};
 26         var graphicsDevice = TurbulenzEngine.createGraphicsDevice(graphicsDeviceParameters);
 27     }
 28 
 29     var canvas, ctx, crateImage, stonesImage, stonesPattern;
 30 
 31     var requestHandlerParameters = {};
 32     var requestHandler = RequestHandler.create(requestHandlerParameters);
 33 
 34     // Create game session and load textures
 35     var mappingTableReceived = function mappingTableReceivedFn(mappingTable) {
 36         if (TurbulenzEngine.canvas) {
 37             var crateImageLoading = new Image();
 38             crateImageLoading.onload = function () {
 39                 crateImage = crateImageLoading;
 40             };
 41             crateImageLoading.src = mappingTable.getURL("textures/crate.jpg");
 42 
 43             var stonesImageLoading = new Image();
 44             stonesImageLoading.onload = function () {
 45                 stonesImage = stonesImageLoading;
 46             };
 47             stonesImageLoading.src = mappingTable.getURL("textures/stones.jpg");
 48         } else {
 49             var crateImageParameters = {
 50                 src: mappingTable.getURL("textures/crate.jpg"),
 51                 mipmaps: true,
 52                 onload: function (texture) {
 53                     crateImage = texture;
 54                 }
 55             };
 56             graphicsDevice.createTexture(crateImageParameters);
 57 
 58             var stonesImageParameters = {
 59                 src: mappingTable.getURL("textures/stones.jpg"),
 60                 mipmaps: true,
 61                 onload: function (texture) {
 62                     stonesImage = texture;
 63                 }
 64             };
 65             graphicsDevice.createTexture(stonesImageParameters);
 66         }
 67     };
 68 
 69     var gameSessionCreated = function gameSessionCreatedFn(gameSession) {
 70         TurbulenzServices.createMappingTable(requestHandler, gameSession, mappingTableReceived);
 71     };
 72     var gameSession = TurbulenzServices.createGameSession(requestHandler, gameSessionCreated);
 73 
 74     if (TurbulenzEngine.canvas) {
 75         canvas = TurbulenzEngine.canvas;
 76 
 77         var numFrames = 0;
 78         var lastSecond = 0;
 79     } else {
 80         canvas = Canvas.create(graphicsDevice);
 81     }
 82     var ctx = canvas.getContext('2d');
 83 
 84     // Set the initial previous frametime for the first loop
 85     var previousFrameTime = TurbulenzEngine.time;
 86     var fpsElement = document.getElementById("fpscounter");
 87     var currentFPS = "0", lastFPS = "0";
 88 
 89     /*
 90     var concavePoints = [];
 91     for (var a = 0; a < (Math.PI * 2); a += 0.05)
 92     {
 93     var r = (150 + (150 * Math.random()));
 94     concavePoints.push([(300 + (r * Math.cos(a))),
 95     (300 + (r * Math.sin(a)))]);
 96     }
 97     */
 98     var concavePoints = [[204, 49], [48, 233], [296, 328], [261, 244], [87, 228], [211, 206], [170, 178], [274, 132]];
 99 
100     function calculateSpirograph(R, r, O) {
101         var points = [];
102 
103         /*jshint bitwise: false*/
104         var dRO = ((R - O) | 0);
105         var Rr = (R + r);
106         var rO = (r + O);
107         var Rrr = (Rr / r);
108 
109         var pi72 = (Math.PI / 72);
110         var cos = Math.cos;
111         var sin = Math.sin;
112 
113         points[0] = [dRO, 0];
114 
115         var x1i = dRO, y1i = 0;
116         var x2, y2;
117         var i = 1;
118         do {
119             var ipi = (i * pi72);
120             var Rrripi = (Rrr * ipi);
121 
122             x2 = ((Rr * cos(ipi)) - (rO * cos(Rrripi)));
123             y2 = ((Rr * sin(ipi)) - (rO * sin(Rrripi)));
124 
125             var x2i = (x2 | 0);
126             var y2i = (y2 | 0);
127 
128             if (x1i !== x2i || y1i !== y2i) {
129                 x1i = x2i;
130                 y1i = y2i;
131 
132                 points.push([x2, y2]);
133 
134                 if (x2i === dRO && y2i === 0) {
135                     break;
136                 }
137             }
138 
139             i += 1;
140         } while(i < 2000);
141 
142         /*jshint bitwise: true*/
143         return points;
144     }
145 
146     var spirographPoints = [];
147 
148     for (var i = 0; i < 3; i += 1) {
149         for (var j = 0; j < 3; j += 1) {
150             spirographPoints[(i * 3) + j] = calculateSpirograph(20 * (j + 2) / (j + 1), -8 * (i + 3) / (i + 1), 10);
151         }
152     }
153 
154     var lingrad = ctx.createLinearGradient(0, 0, 0, 150);
155     lingrad.addColorStop(0, '#00ABEB');
156     lingrad.addColorStop(0.5, '#fff');
157     lingrad.addColorStop(0.5, '#26C000');
158     lingrad.addColorStop(1, '#fff');
159 
160     var lingrad2 = ctx.createLinearGradient(0, 50, 0, 95);
161     lingrad2.addColorStop(0.5, '#000');
162     lingrad2.addColorStop(1, 'rgba(0,0,0,0)');
163 
164     var radgrad = ctx.createRadialGradient(45, 45, 10, 52, 50, 30);
165     radgrad.addColorStop(0, '#A7D30C');
166     radgrad.addColorStop(0.9, '#019F62');
167     radgrad.addColorStop(0.95, 'rgba(1,159,98,0)');
168     radgrad.addColorStop(1, 'rgba(1,159,98,0)');
169 
170     var radgrad2 = ctx.createRadialGradient(105, 105, 20, 112, 120, 50);
171     radgrad2.addColorStop(0, '#FF5F98');
172     radgrad2.addColorStop(0.75, '#FF0188');
173     radgrad2.addColorStop(0.99, 'rgba(255,1,136,0)');
174     radgrad2.addColorStop(1, 'rgba(255,1,136,0)');
175 
176     var radgrad3 = ctx.createRadialGradient(95, 15, 15, 102, 20, 40);
177     radgrad3.addColorStop(0, '#00C9FF');
178     radgrad3.addColorStop(0.8, '#00B5E2');
179     radgrad3.addColorStop(0.99, 'rgba(0,201,255,0)');
180     radgrad3.addColorStop(1, 'rgba(0,201,255,0)');
181 
182     var radgrad4 = ctx.createRadialGradient(0, 150, 50, 0, 140, 90);
183     radgrad4.addColorStop(0, '#F4F201');
184     radgrad4.addColorStop(0.8, '#E4C700');
185     radgrad4.addColorStop(0.99, 'rgba(228,199,0,0)');
186     radgrad4.addColorStop(1, 'rgba(228,199,0,0)');
187 
188     // Update: Should update the input, physics, sound and other js libraries used.
189     //         The function should then trigger the rendering using the graphicsDevice
190     function update() {
191         // Gets the currentTime to be used in calculations for this frame
192         var currentTime = TurbulenzEngine.time;
193 
194         // Delta is calculated to be used by update functions that require the elapsed time since they were last called
195         var deltaTime = (currentTime - previousFrameTime);
196         if (deltaTime > 0.1) {
197             deltaTime = 0.1;
198         }
199 
200         var deviceWidth, deviceHeight, i, j, points, numPoints, point;
201 
202         // Render the color
203         var ok = false;
204         if (TurbulenzEngine.canvas) {
205             deviceWidth = canvas.width;
206             deviceHeight = canvas.height;
207             ok = true;
208         } else {
209             if (graphicsDevice.beginFrame()) {
210                 deviceWidth = graphicsDevice.width;
211                 deviceHeight = graphicsDevice.height;
212 
213                 if (canvas.width !== deviceWidth) {
214                     canvas.width = deviceWidth;
215                 }
216                 if (canvas.height !== deviceHeight) {
217                     canvas.height = deviceHeight;
218                 }
219 
220                 ctx.beginFrame();
221                 ok = true;
222             }
223         }
224 
225         if (ok) {
226             ctx.fillStyle = '#000';
227             ctx.fillRect(0, 0, deviceWidth, deviceHeight);
228 
229             if (stonesImage) {
230                 ctx.save();
231                 if (!stonesPattern) {
232                     stonesPattern = ctx.createPattern(stonesImage, 'repeat');
233                 }
234                 ctx.globalAlpha = 0.5;
235                 ctx.fillStyle = stonesPattern;
236                 ctx.fillRect(0, 0, deviceWidth, deviceHeight);
237                 ctx.restore();
238             }
239 
240             //ctx.shadowOffsetX = 5;
241             //ctx.shadowOffsetY = 5;
242             //ctx.shadowColor = '#000';
243             // colored rectangles
244             ctx.save();
245             ctx.translate(330, 70);
246             for (i = 0; i < 6; i += 1) {
247                 for (j = 0; j < 6; j += 1) {
248                     ctx.fillStyle = 'rgb(' + Math.floor(255 - (42.5 * i)) + ',' + Math.floor(255 - (42.5 * j)) + ',0)';
249                     ctx.fillRect(j * 25, i * 25, 25, 25);
250                 }
251             }
252             ctx.restore();
253 
254             if (crateImage) {
255                 ctx.save();
256                 ctx.translate(50, 525);
257                 ctx.drawImage(crateImage, 40, 0, 128, 128);
258                 ctx.restore();
259             }
260 
261             // Transparent arcs
262             ctx.save();
263             ctx.translate(330, 320);
264             ctx.fillStyle = '#FD0';
265             ctx.fillRect(0, 0, 75, 75);
266             ctx.fillStyle = '#6C0';
267             ctx.fillRect(75, 0, 75, 75);
268             ctx.fillStyle = '#09F';
269             ctx.fillRect(0, 75, 75, 75);
270             ctx.fillStyle = '#F30';
271             ctx.fillRect(75, 75, 75, 75);
272             ctx.fillStyle = '#FFF';
273 
274             ctx.globalAlpha = 0.2;
275 
276             for (i = 0; i < 7; i += 1) {
277                 ctx.beginPath();
278                 ctx.arc(75, 75, 10 + (10 * i), 0, Math.PI * 2, true);
279                 ctx.fill();
280             }
281             ctx.restore();
282 
283             // Thick lines
284             ctx.save();
285             ctx.translate(330, 520);
286             ctx.strokeStyle = '#FFF';
287             var lineJoin = ['round', 'bevel', 'miter'];
288             var lineCap = ['butt', 'round', 'square'];
289             ctx.lineWidth = 10;
290             for (i = 0; i < lineJoin.length; i += 1) {
291                 ctx.lineJoin = lineJoin[i];
292                 ctx.lineCap = lineCap[i];
293                 ctx.beginPath();
294                 ctx.moveTo(-5, 5 + (i * 40));
295                 ctx.lineTo(35, 45 + (i * 40));
296                 ctx.lineTo(75, 5 + (i * 40));
297                 ctx.lineTo(115, 45 + (i * 40));
298                 ctx.lineTo(155, 5 + (i * 40));
299                 ctx.stroke();
300             }
301             ctx.restore();
302 
303             for (i = 0; i < 3; i += 1) {
304                 for (j = 0; j < 3; j += 1) {
305                     ctx.save();
306                     ctx.strokeStyle = "#9CFF00";
307                     ctx.translate((50 + (j * 100)), (50 + (i * 100)));
308 
309                     points = spirographPoints[(i * 3) + j];
310                     numPoints = points.length;
311 
312                     ctx.beginPath();
313 
314                     point = points[0];
315                     ctx.moveTo(point[0], point[1]);
316 
317                     for (var k = 1; k < numPoints; k += 1) {
318                         point = points[k];
319                         ctx.lineTo(point[0], point[1]);
320                     }
321 
322                     ctx.stroke();
323 
324                     ctx.restore();
325                 }
326             }
327 
328             // Concentric rings of circles
329             ctx.save();
330             ctx.translate(150, 390);
331             for (i = 1; i < 6; i += 1) {
332                 // Loop through rings (from inside to out)
333                 ctx.save();
334 
335                 ctx.fillStyle = 'rgb(' + (51 * i) + ',' + (255 - (51 * i)) + ',255)';
336 
337                 ctx.beginPath();
338 
339                 for (j = 0; j < (i * 6); j += 1) {
340                     // draw individual dots
341                     ctx.rotate(Math.PI * 2 / (i * 6));
342                     ctx.moveTo(0, i * 12.5);
343                     ctx.arc(0, i * 12.5, 5, 0, Math.PI * 2, true);
344                 }
345 
346                 ctx.fill();
347 
348                 ctx.restore();
349             }
350             ctx.restore();
351 
352             // Linear gradient
353             ctx.save();
354             ctx.translate(550, 70);
355             ctx.fillStyle = lingrad;
356             ctx.strokeStyle = lingrad2;
357             ctx.fillRect(10, 10, 130, 130);
358             ctx.strokeRect(50, 50, 50, 50);
359             ctx.restore();
360 
361             // Radial gradient
362             ctx.save();
363             ctx.translate(550, 320);
364             ctx.fillStyle = radgrad4;
365             ctx.fillRect(0, 0, 150, 150);
366             ctx.fillStyle = radgrad3;
367             ctx.fillRect(0, 0, 150, 150);
368             ctx.fillStyle = radgrad2;
369             ctx.fillRect(0, 0, 150, 150);
370             ctx.fillStyle = radgrad;
371             ctx.fillRect(0, 0, 150, 150);
372             ctx.restore();
373 
374             // Concave polygon
375             numPoints = concavePoints.length;
376             point = concavePoints[0];
377             ctx.save();
378             ctx.translate(530, 490);
379             ctx.scale(0.5, 0.5);
380             ctx.beginPath();
381             ctx.moveTo(point[0], point[1]);
382             for (i = 1; i < numPoints; i += 1) {
383                 point = concavePoints[i];
384                 ctx.lineTo(point[0], point[1]);
385             }
386             ctx.closePath();
387             ctx.fillStyle = '#00F';
388             ctx.fill();
389             ctx.strokeStyle = '#FFF';
390             ctx.stroke();
391             ctx.restore();
392 
393             // Paths with rounded corners
394             ctx.save();
395             ctx.translate(0, 100);
396             ctx.beginPath();
397             ctx.strokeStyle = "black";
398             ctx.lineWidth = 3;
399             ctx.moveTo(120, 100);
400             ctx.lineTo(180, 100);
401             ctx.arcTo(200, 100, 200, 80, 20);
402             ctx.lineTo(200, 20);
403             ctx.arcTo(200, 0, 180, 0, 20);
404             ctx.lineTo(120, 0);
405             ctx.arcTo(100, 0, 100, 20, 20);
406             ctx.lineTo(100, 80);
407             ctx.arcTo(100, 100, 120, 100, 20);
408             ctx.stroke();
409             ctx.translate(250, 80);
410             ctx.rotate(Math.PI / 4);
411             ctx.beginPath();
412             ctx.moveTo(20, 0);
413             ctx.lineTo(80, 0);
414             ctx.arcTo(100, 0, 100, 20, 20);
415             ctx.lineTo(100, 80);
416             ctx.arcTo(100, 100, 80, 100, 20);
417             ctx.lineTo(20, 100);
418             ctx.arcTo(0, 100, 0, 80, 20);
419             ctx.lineTo(0, 20);
420             ctx.arcTo(0, 0, 20, 0, 20);
421             ctx.stroke();
422             ctx.restore();
423 
424             if (TurbulenzEngine.canvas) {
425                 numFrames += 1;
426 
427                 if ((currentTime - lastSecond) >= 1) {
428                     currentFPS = (numFrames / ((currentTime - lastSecond))).toFixed(2);
429                     numFrames = 0;
430                     lastSecond = currentTime;
431                 }
432             } else {
433                 ctx.endFrame();
434 
435                 graphicsDevice.endFrame();
436 
437                 currentFPS = (graphicsDevice.fps).toFixed(2);
438             }
439         }
440 
441         if (lastFPS !== currentFPS) {
442             lastFPS = currentFPS;
443             if (fpsElement) {
444                 fpsElement.innerHTML = currentFPS + ' fps';
445             }
446         }
447 
448         previousFrameTime = currentTime;
449     }
450 
451     var intervalID = TurbulenzEngine.setInterval(update, 1000 / 60);
452 
453     TurbulenzEngine.onunload = function destroyScene() {
454         // Clear the interval to stop update from being called
455         TurbulenzEngine.clearInterval(intervalID);
456 
457         if (gameSession) {
458             gameSession.destroy();
459             gameSession = null;
460         }
461 
462         // Clear any references to memory
463         radgrad4 = null;
464         radgrad3 = null;
465         radgrad2 = null;
466         radgrad = null;
467         lingrad2 = null;
468         lingrad = null;
469         stonesPattern = null;
470         stonesImage = null;
471         crateImage = null;
472         ctx = null;
473         canvas = null;
474         requestHandler = null;
475 
476         fpsElement = null;
477 
478         // Tell the Turbulenz Engine to force the js virtual machine
479         // to free as much memory as possible
480         TurbulenzEngine.flush();
481 
482         // Clear each native engine reference
483         graphicsDevice = null;
484 
485         TurbulenzEngine.onunload = null;
486     };
487 };