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 };