From 1ae93fdba800615faca28668d0e5aab2ded3e941 Mon Sep 17 00:00:00 2001 From: Martin Drapeau Date: Sat, 21 Mar 2015 08:59:03 -0400 Subject: [PATCH] Doc updates. Revamped adjustViewport function to work cross devices. --- ball/main.js | 3 +- frog/index.html | 1 + frog/main.js | 4 +- index.html | 79 +++++++++++++++++++++------------------- mario/index.html | 1 + mario/main.js | 6 +-- src/adjust-viewport.js | 47 ++++++++++++++++++------ super-mario-bros/main.js | 4 +- 8 files changed, 85 insertions(+), 60 deletions(-) diff --git a/ball/main.js b/ball/main.js index 6bfa15b..0a7cdca 100644 --- a/ball/main.js +++ b/ball/main.js @@ -102,7 +102,6 @@ $(window).on("load", function() { engine: engine }); - // Ensure the canvas is always visible and centered - adjustViewport(canvas, 960, 700); + adjustViewport(canvas); }); \ No newline at end of file diff --git a/frog/index.html b/frog/index.html index d9c00e1..c68cd06 100644 --- a/frog/index.html +++ b/frog/index.html @@ -8,6 +8,7 @@ + diff --git a/frog/main.js b/frog/main.js index ac16e61..809c78d 100644 --- a/frog/main.js +++ b/frog/main.js @@ -11,6 +11,7 @@ $(window).on("load", function() { var canvas = document.getElementById("foreground"), context = canvas.getContext("2d"); + adjustViewport(canvas); var spriteNames = [ "land1", "land2", "land3", "land4", "land5", "land6", @@ -232,8 +233,5 @@ $(window).on("load", function() { context: context, controller: controller }); - - // Ensure the canvas is always visible and centered - adjustViewport(canvas, 960, 700); }); \ No newline at end of file diff --git a/index.html b/index.html index a00bb7d..d5ac8e3 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ Backbone Game Engine - + @@ -50,7 +50,7 @@

HTML5 Canvas & Backbone

- An elementary HTML5 Canvas game engine built on Backbone. Examples: + An elementary HTML5 Canvas game engine built on Backbone. Specialized for 2D platformers, and optimized for mobile. Examples:

  • Elementary: Bouncing ball
  • @@ -84,6 +84,8 @@

    Features:

    • Built on Backbone. Events, models, collections, inheritance and RESTful persistence. Why reinvent the wheel?
    • HTML5 canvas only. No jQuery, as little DOM manipulations as possible.
    • +
    • Mobile optimized. Built to run in the browser or in CocoonJS/Phone Gap. Optimized for maxium FPS.
    • +
    • 2D platformer. Built with side-scrollers in mind. Built-in classes for sprites, sprite sheets, characters, hero, quad-tree collision detection, world and editor.
    • No compilation. You don't need to install node, grunt or whatever else. Just code and press F5 to run.
    • No server required. Fork this repo and your Github site is up and going. Create your own game and point your friends to it. Rebase to pull in latest engine updates.
    • Built for mobile. Conceived to run on tablets. Share your URL with Mom so she can add it to the home screen of her iPad.
    • @@ -91,7 +93,6 @@

      Features:

    • Save state. With HTML5 Local Storage, save where you are.
    • World editor. Conceived for tile-based games, comes with a world editor. Place your tiles and characters, then hit play to try it out. Hit save to save your world.
    -
@@ -1545,50 +1546,52 @@

Mobile Devices

Touch Events

- Backbone.Input, Backbone.Button and Backbone.WorldEditor support touch and mouse events transparently. Works on Android, iOS and Windows. + Backbone.Engine, Backbone.Input, Backbone.Button and Backbone.WorldEditor support touch and mouse events transparently. Works on Android, iOS and Windows.

Viewport resizing and canvas centering

- In addition, we provide global function adjustViewport(canvas, width, height) to dynamically adjust the viewport size and center the canvas when an orientation change occurs. This function can be found in adjust-viewport.js located in the src folder. -

-

- On mobile devices the orientation of the web page can change between protrait and landscape. Since we cannot lock the orientation, we must find a way to adjust the viewport width to ensure the canvas is always visible. We use the meta tag viewport for that purpose. -

-

- As an example, let's assume our canvas is 960x700 pixels; therefore in landscape. In order for it to be visible and maximized on any portrait screen, we must control the width of the viewport. In index.html we declare the viewport meta tag to be the width of the canvas: + On mobile devices, the meta tag viewport is set to 960 pixels wide. + On iOS, Android and Windows mobile devices, this will ensure the canvas is full width. + The HTML file contains the necessary header tags to ensure everything works. + You can change the viewport width value to whatever you want.

-
<meta name="viewport" content="width=960, user-scalable=no"/>
+
+<meta name="viewport" content="width=960, user-scalable=no"/>
+<meta name="mobileoptimized" content="0" />
+

- When the device is rotated to landscape, we need to adjust so that the height of the canvas is used instead. - The natural solution would be to set content="height=700, user-scalable=no". - However this is not supported (as reported by Quirksmode). - Instead, we must derive the width based on the viewport ratio using this formula: + Not all screens have the same aspec ratio. + To take care of the height, you can change the height of the canvas upon start by calling the global function adjustViewport() (see file adjust-viewport.js for details).

-
700 * window.innerWidth / window.innerHeight
+
+var canvas = document.getElementById("foreground");
+adjustViewport(canvas);
+

- Since the mobile device orientation changes when we rotate it, we must recalculate and reset the viewport width dynamically. We can do so using the resize event. Finally, we are also able to center the canvas by adjusting its left position. Here is the final code: + If you want to maintain the aspect ratio, pass true. The canvas will be centered on screen.

-
-function adjustViewport(canvas, width, height) {
-
-  var viewport = document.querySelector("meta[name=viewport]");
-
-  function onResize() {
-    if (window.innerWidth > window.innerHeight) {
-      // Landscape
-      canvas.style.left = _.max([0, (window.innerWidth - width) / 2]) + "px";
-      viewport.setAttribute("content", "width=" + Math.ceil(height * window.innerWidth / window.innerHeight) + ",user-scalable=no");
-    } else {
-      // Portrait
-      canvas.style.left = "0px";
-      viewport.setAttribute("content", "width=" + width + ",user-scalable=no");
-    }
-  }
-
-  window.addEventListener("resize", _.debounce(onResize, 300));
-  onResize();
-}
+
+var canvas = document.getElementById("foreground");
+adjustViewport(canvas, true);
 
+

+ On desktop the viewport meta tag is ignored. + adjustViewport will center the canvas, even handling resizes. + It will try to reduce the height of the canvas if too tall unless you omit the keepRatio argument. +

+

Web App

+

+ These meta tags are set to enable Web App support: +

+
+<meta name="apple-mobile-web-app-capable" content="yes" />
+<meta name="mobile-web-app-capable" content="yes" />
+<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
+
+

+ To suggest users to put add the home page to the home screen, checkout this great plugin: + Cubiq's Add To Homescreen. +

diff --git a/mario/index.html b/mario/index.html index d7ba967..aa0b2a5 100644 --- a/mario/index.html +++ b/mario/index.html @@ -8,6 +8,7 @@ + diff --git a/mario/main.js b/mario/main.js index cd8c211..f068ebe 100644 --- a/mario/main.js +++ b/mario/main.js @@ -19,6 +19,7 @@ $(window).on("load", function() { }); var canvas = document.getElementById("foreground"); + adjustViewport(canvas); var spriteSheets = new Backbone.SpriteSheetCollection([{ id: "mario", @@ -38,6 +39,8 @@ $(window).on("load", function() { var mario = new Backbone.Mario({ x: 400, y: 200, floor: 500 + }, { + input: input }); var world = new Backbone.World({ @@ -68,8 +71,5 @@ $(window).on("load", function() { world: world, mario: mario }); - - // Ensure the canvas is always visible and centered - adjustViewport(canvas, 960, 700); }); \ No newline at end of file diff --git a/src/adjust-viewport.js b/src/adjust-viewport.js index 31589a9..c6dbca4 100644 --- a/src/adjust-viewport.js +++ b/src/adjust-viewport.js @@ -9,31 +9,56 @@ * */ - // Ensures the canvas is always visible and centered by adjusting - // the viewport and the canvas left position. - var resizeCount = 0; - function adjustViewport(canvas, width, height) { + // Ensures the canvas is always full size and/or centered. + // + // On mobile: + // Works in conjunction with meta tag viewport where width is set to the canvas width. + // + // Will modify the canvas height to fit the device aspect ratio, never exceeding the + // origin canvas height. + // Set keepRatio to true to maintain your aspect ratio. In such a case, the viewport + // is adjusted to the height of the canvas. The canvas is kept centered on screen (with + // black bars left and right to fill empty space). + // + // On Desktop: + // The viewport meta tag is ignored. Instead, the canvas is kept centered. The height + // may be reduced if the window height is less than the canvas (to avoid scrolling). + // Set keepRatio to maintain the canvas height. - var viewport = document.querySelector("meta[name=viewport]"); + function adjustViewport(canvas, keepRatio) { + + var viewport = document.querySelector("meta[name=viewport]"), + mobile = "onorientationchange" in window || + window.navigator.msMaxTouchPoints || + window.navigator.isCocoonJS; function onResize() { if (window.innerWidth > window.innerHeight) { // Landscape - canvas.style.left = _.max([0, (window.innerWidth - width) / 2]) + "px"; - viewport.setAttribute("content", "width=" + Math.ceil(height * window.innerWidth / window.innerHeight) + ",user-scalable=no"); + canvas.style.left = _.max([0, (window.innerWidth - canvas.width) / 2]) + "px"; + if (mobile && viewport) + viewport.setAttribute("content", "width=" + Math.ceil(canvas.height * window.innerWidth / window.innerHeight) + ",user-scalable=no"); } else { // Portrait canvas.style.left = "0px"; - viewport.setAttribute("content", "width=" + width + ",user-scalable=no"); + if (mobile && viewport) + viewport.setAttribute("content", "width=" + canvas.width + ",user-scalable=no"); } } - window.addEventListener("resize", _.debounce(onResize, 300)); - setTimeout(onResize, 10); + if (mobile && !keepRatio) { + canvas.height = Math.round(Math.min(canvas.height, canvas.width * Math.min(window.innerHeight, window.innerWidth) / Math.max(window.innerHeight, window.innerWidth) )); + } else { + if (!keepRatio) + canvas.height = Math.round(Math.min(canvas.height, window.innerHeight)); + window.addEventListener("resize", _.debounce(onResize, 300)); + setTimeout(onResize, 10); + } + } _.extend(window, { - adjustViewport: adjustViewport, + adjustViewport: adjustViewport }); }).call(this); \ No newline at end of file diff --git a/super-mario-bros/main.js b/super-mario-bros/main.js index ec32ead..719afbe 100644 --- a/super-mario-bros/main.js +++ b/super-mario-bros/main.js @@ -12,6 +12,7 @@ $(window).on("load", function() { var canvas = document.getElementById("foreground"), context = canvas.getContext("2d"); + adjustViewport(canvas); var spriteNames = [ "ground", "brick-top", "brick", "ground2", "block", "block2", "question-block", "pennie", @@ -250,7 +251,4 @@ $(window).on("load", function() { controller: controller, }); - // Ensure the canvas is always visible and centered - adjustViewport(canvas, canvas.width, canvas.height); - }); \ No newline at end of file