I'm currently exploring the potential of CSS3 for casual game scenarios. For Windows Store apps in particular, CSS3 animations are enticing because you don't have to worry about cross-browser compatibility or APIs that won't work with older versions of Internet Explorer, since everything is IE 10+. Compatibility is one of the main reasons why proponents of Flash for Web apps argue against HTML5/CSS3. But for Windows Store apps, these arguments against HTML5/CSS3 are a non-factor. Instead, if you want to do a game (or just animations) for Windows Store, you need to figure out whether you need CSS3, or HTML5 <canvas>, or maybe the newly supported WebGL—if we're talking JavaScript (we are)—for fancier stuff.

Updated info, 2/19/2014: Check out the completed sample (Windows Store version)

Despite a few hiccups, CSS3 development has been pretty fast–which is an advantage of the technology vs. using the HTML5 <canvas> (note that <canvas> also has a lot of advantages, especially for games with a large number of independent objects). However, I thought I'd talk about a couple of areas where I got tripped up a bit using CSS3. In this post, I'll talk about the need to dynamically manipulate the @keyframes rule using JavaScript.

Basic CSS3 animations are pretty straightforward. You create your DOM elements, declaratively or otherwise, and specify a few animation properties. And then you create a set of one or more @keyframes
rules to handle the actual animations.

Here is some of the CSS code for an HTML element representing a game object. We'll say it's for a DIV element containing an image (<div id="piece"></div>). The animation-name rule specifies the @keyframes "enterPiece" rule.

  #piece {

      position: absolute;

      animation-duration: 1.5s;

      animation-fill-mode: forwards;

      animation-name: "enterPiece"

  }

Here is the CSS code for the @keyframes "enterPiece" rule. @keyframes allows you to specify beginning/end states and anything in between (such as what the animation should look like when 50% complete). In this example, I just set the beginning/end states.

  @keyframes enterPiece {

      from {

          transform: translateY(-150px) scale(1);

          opacity: 0;

      }

      to {

          transform: translateY(0px) scale(1);

          opacity: 1;

      }

  }

This is all pretty straightforward. The animation for the game object moves the object from a relative starting position of -150px to 0px, and changes the opacity from 0 to 1 while moving it this distance. (I include a scale value for informational purposes but I won't be using it—I'll be setting that value dynamically.)

Here's code to set some of these animation values in JavaScript: In this code, we set the animation duration, fill mode, and the animation name ("enterPiece", again), which specifies the same @keyframes code in CSS. In this case, we're doing it for a new element that we're about to add to the DOM.

  newNode.style.animationDuration = "1.5s";

  newNode.style.animationFillMode = "forwards";

  newNode.style.animationName = "enterPiece";

As I tested my code, I realized I would need to set different scale values for each piece, due to the use of a 3D perspective. This was a little trickier to do in JavaScript, because animated transform values such as scale, translate, and rotate belong to the harder to access @keyframes rule. I refused to create a bunch of extra @keyframes rules in the CSS**. Instead, after some investigation, I was able to obtain the rule using the following code, which iterates through the stylesheets to get the rule I wanted.

**requestAnimationFrame is another way to go. More on this in a later post.

Note:  If you don't need to show these values changing within an animation, you can just include them in CSS code, for example, like this: transform: rotateX(180deg).

  var cssRule;

  // Returns a reference to the specified CSS rule(s).

  function getRule() {

      var rule;

      var ss = document.styleSheets;

      for (var i = 0; i < ss.length; ++i) {

          // loop through all the rules!

          for (var x = 0; x < ss[i].cssRules.length; ++x) {

              rule = ss[i].cssRules[x];

              if (rule.name == "enterPiece" && rule.type

                      == CSSRule.KEYFRAMES_RULE) {

                  cssRule = rule;

              }

          }

      }

  }

To make sure I'm not getting the wrong CSS rule, I check the type, which needs to be CSSRule.KEYFRAMES_RULE, and I check the name. It's best to run this code during app initialization, and cache any rules which you need to modify in JavaScript.

Note: For non-IE browsers, check current recommendations for using the CSSRule.KEYFRAMES_RULE.

Once I had the right rule, I had to change it. The API documentation led me to think that I should use insertRule, but some testing indicated otherwise. Instead, I used appendRule along with deleteRule.

First, I needed to clear out the current rule values. To use deleteRule, you must pass an index to indicate which part of the existing rule to delete. An index of 0 indicates the from portion (that is, the "0%" portion) of the rule. Once I deleted the rule parts, I replaced them using appendRule, and here I passed in my dynamically set scale values.

  cssRule.deleteRule("0");

  cssRule.deleteRule("1");

  cssRule.appendRule("0% { transform: translateY(-150px) "

      + scale +"; opacity: 0; }");

  cssRule.appendRule("100% { transform: translateY(0px) "

      + scale + "; opacity: 1; }");

 

And this worked!

In Part II of this series, I talk about running multiple animations and using requestAnimationFrame. I'll share the full code when it's closer to final state.