HTML5 Portfolio Project – Part 5

When I start a project, I like to tackle the parts that are new to me first. Obviously, I enjoy the dopamine hit from exploring something unfamiliar, but also, I've found that this is a great way to catch "gotchas" early, rather than waiting until the end when work-arounds might require substancial changes to parts of the code that would otherwise be more pedestrian.

With that approach in mind, I've spent a ridiculous amount of the last couple weeks hacking on a circular mask transition effect. I'd gotten this to work fine in the past for image content, but I wanted to be sure it would support other types of content too—a video player for instance. Ideally, I'd end up with a jQuery effect that could handle an arbitrary <div>. It should work on the latest version of any browser (and degrade gracefully in IE7/8).

This has proved itself a challenge several orders of magnitude larger than I'd expected.

The Big Deal

Perhaps the HTML spec isn't as clear as it ought to be: if you specify overflow: hidden, the clipping region should take border-radius into account. Firefox and IE9 do. That leaves a whole lot of everything else. As long as your content is an image, you can apply a border-radius to that, and the clipping looks OK as long as the image is square (IE7/8 notwithstanding). But you can't properly offset the image in its container (we'll get to why we need that eventually) unless you apply the image as a background.

In the interest of moving forward, I'm going to use the background-image technique, but that isn't going to help for videos etc. I also evaluated webkit-mask-image (webkit-only obviously) and using a canvas buffer (doesn't seem to work in Safari). Now, if you're paying attention, a combination of all-of-the-above would let us support all browsers, but I'm calling "scope creep" and deferring this to version 1.1. In the meantime, I'll probably use a lightbox for videos.

It hasn't been a complete waste. On the contrary, I learned quite a bit about CSS PIE and ExplorerCanvas.

Schedule

Speaking of which, multiple commercial projects are conflicting with my original release schedule. June 1 is going to come and go, so I'm setting a new goal of July 1.

I'm not doing so well keeping to the hourly budget either. I burned through the original estimate just trying to get this effect working (everywhere). I'd originally estimated 3 hours and spent closer to 30. Ouch!

Like I said, warts and all.

Stay Tuned

Next time I'll show the sunburst background (a canvas-based effect) and the aforementioned circular mask (of doom) with compound borders. I already have this working, but I want to clean up the code before doing a write-up.

 

HTML5 Portfolio Project – Part 4

I'm structuring the project folder like this:

/portfolio
  /deploy
    /css
    /img
      /icons
        /link.png
        /video.png
        /pic.png
    /js
      /framework
      /lib
        /jquery.js 
   
    /background.png
    /index.html

    /contact
      /thumb.png
      /bio_contact.html
    /projects
      /project1
        /thumb.png
        /video.html
        /description.html
      /project2
        /thumb.png
        /video.html
        /description.html
    
  /src
    /project1
      /video

  /notes

If your information architecture is tight, you'll likely see echoes of your file hierarchy in your data hierarchy and echoes from both in your code's object hierarchy. Guess that means we're going to need some code then.

Stay Tuned:

In the next installment, we're going to spend a little time playing with jQuery-based animation so that we're comfortable with it before trying to integrate the effect into the site itself.

HTML5 Portfolio Project – Part 3

Don Your Project Manager Hat

A week with no updates?! ... And things are about to get more busy, not less.

Plus, upon reviewing the project plan, I noticed an omission: there's no specific line item for the mask transition. It seemed so obvious, I neglected to capture it in the schedule. I'm adding 3 hours to account for it (new total: 44 hrs).

Ok, To Work Then

Last time, I talked a bit about the data model. Since then, I've thought of some improvements. Just because the Flash version worked a certain way doesn't mean I shouldn't re-evaluate my assumptions.

First, the home screen ought to be treated as a node. It has a url and graphical content, just like the portfolio pieces. It's button-ness behaves a little differently, and it's sized larger, but we should be able to abstract away the differences. In theory, it would be nice to allow unlimited node nesting. This would allow projects to have sub-projects (for example) and enable a lot more organizational flexibility, extending the useful lifetime of the site. We'll see if we can squeeze it in. (Google: YAGNI)

[
  {
    "url"         : "/",
    "title"       : "shovemedia portfolio 2012",
    "content"     : "homepage.html",
    "sections"     : [
          {
            "url"         : "contact",
            "client"      : "Email:<br />jon@shovemedia.com",
            "title"       : "contact info",
            "date"        : "01/01/2011",
            "role"        : "developer",
            "link"        : "mailto:jon@shovemedia.com?subject=New Business",
            "thumbnail"   : "thumbnails/email.jpg",
            "content"     : "contact.html"
          },
          {
            "url"         : "3D",
            "client"      : "Photobiz",
            "title"       : "3D Templates",
            "date"        : "01/01/2011",
            "role"        : "developer",
            "link"        : "http://photobiz.com",
            "thumbnail"   : "thumbnails/3d-templates.jpg",
            "sections"        : [
                    {
                      "url"     : "video",
                      "title"   : "watch!",
                      "icon"    : "video",
                      "content" : "papervision/videoPlayer.html"
                    },
                    {
                      "url"     : "image1",
                      "title"   : "Image 1",
                      "icon"    : "pic",
                      "content" : "papervision/image1.jpg"
                    },
                    {
                      "url"     : "description",
                      "icon"    : "description",
                      "content" : "papervision/description.html",
                    }
                  ]
          }
        ]
  }
]

Don't get lost in the braces. Deeper nesting is a necessary evil, I think. Otherwise, I'm going to end up with redundant references to "link" all the flattened elements.

I've also changed some of my naming conventions. Each nodes stores its child nodes in an Array called "sections".

Automatic Value-Object Mediation with JS Signals

Just a little something I cooked up tonight after a few beers with Lee:

ModelMediator = function (model) {
  this.__onChanged__ = new Dictionary();

  for (var key in model)
  {
    this.__defineSetter__(key, function (arg) { this.set(model, key, arg) });
    this.__defineGetter__(key, function () { this.get(model, key) });
    this.__onChanged__.set(key, new signals.Signal());
  }
}

ModelMediator.prototype.get = function (model, key)
{
  return model[key]
}

ModelMediator.prototype.set = function (model, key, value)
{
  model[key] = value;
  this.__onChanged__.get(key).dispatch({model:model, key:key, value:value});
}

Dictionary class here

Downloading and understanding JS Signals is left as an exercise for the reader.

Dictionary Class (JavaScript / JS)

I sorely miss the Dictionary class from AS3, but wasn't excited by what my Googles led to and figured I'd learn more by rolling my own.

JavaScript Arrays can be used as a hash, but only with String or Numeric keys. This class lets you use any Object. A function, an Array, a class ... anything. It could be faster, among other possible improvements, but this implementation has been coming in handy all over.

Dictionary = function ()
{
  this.keys = [];
  this.values = [];
}

Dictionary.prototype.get = function (key)
{
  var index = this.keys.indexOf(key);
  if (index != -1)
  {
    return this.values[index];
  }
}

Dictionary.prototype.set = function (key, value)
{
  var index = this.keys.indexOf(key);
  if (index == -1)
  {
    this.keys.push(key);
    index = this.keys.length - 1;
  }

  this.values[index] = value;
}

Dictionary.prototype.remove = function (key)
{
  var index = this.keys.indexOf(key);
  if (index == -1)
  {
      return undefined;
  }

  var value = this.values[index];

  delete(this.values[index]);
  delete(this.keys[index]);

  return value;
}

Removing Trace and Logging Statements for Production

I'm saving these recipes here so I can bookmark, and I'm sure someone else will find them useful.

I decided to convert calls to trace or my logging framework to use conditional compilation so that they won't be included when I do a production build, but are included when I do a debug build.

The Flex compiler supports this by allowing you to set a variable at compile-time, eg:

-define=CONFIG::debug,true

In your code, simply wrap the code to be conditionally compiled in curly braces, prepended with this variable, eg:

CONFIG::Debug
{
	trace( 'Debug' );
}

I spent some time writing a regular expression that finds trace statements, and wraps them in this block. In plain English, start at the beginning of the line, match zero or more whitespace, the word "trace", zero or more whitespace, an open parenthesis, the rest of the line.

Search for: ^([ \t]*)(trace[ \t]*\(.*)$
Replace with: $1CONFIG::debug { $2 }

FDT's internal parser doesn't like the conditional compilation syntax, so we need to change our replacement text slightly to wrap that part in markers instructing it to ignore them.

Search for: ^([ \t]*)(trace[ \t]*\(.*)$
Replace with: $1/*FDT_IGNORE*/ CONFIG::debug /*FDT_IGNORE*/ { $2 }

The same recipe for any call to logger, eg: logger.debug, logger.info, logger.warn, etc.

Search for: ^([ \t]*)(logger\..*)$
Replace with: $1/*FDT_IGNORE*/ CONFIG::debug /*FDT_IGNORE*/ { $2 }

Masking TLF (Text Layout Framework)

Just a quick note to point out a bug (?) I found today while working with the ActionScript 3 Text Layout Framework.

I wanted to add a mask to the Sprite container I'd passed to my TLF ContainerController. Whatever it is that happened wasn't correct: there was masking occurring, but the shape I was using as a mask was (incorrectly) visible on the stage. Rolling over my buttons caused redraw of stage regions, and my TLF text in those areas would go from invisible to incorrectly masked.

After trying several things, I pulled an old-school trick out of my bag: nest the Sprite I was passing as a container to the ContainerController in another holder Sprite, and apply the mask to the holder instead. From there, everything worked as expected.

Note: I further tracked this down to be caused by the ContainerController being created smaller than the text stuffed inside. Only then do I have problems with masking. YMMV.