Friday, January 20, 2012

Dart Animation

‹prev | My Chain | next›

My sweet comic book application written entirely in Dart is starting to come together:


At this point, I can delete stuff from my front-end, but I am forced to navigate to a separate page to add new comics to the collection. Bah!

Instead, I would like to convert that last line to a link to a smooth animation of an add-form. Since I am building this in Dartium, a webkit based browser, I should be able to do something along the lines of CSS3 animations. Let's see...

First up, I define a "faded" CSS class that describes the start state for my add-form. In this case, I want the thing hidden:
.faded {
  opacity: 0;
  -webkit-transition: opacity 1s ease-in-out;
}
To get my fade animation, I make the opacity as a "-webkit-transition" property with a duration of 1 second. The ease-in-out transformation function starts things off slow, gets going at a pretty good pace, and then stops slowly. Next, I define two other CSS classes describing the end state of my transitions:
.fade-in {
  opacity: 1;
}

.fade-out {
  opacity: 0;
}
With that, I am ready for some Dart. In addition to loading my comic book collection via Ajax, I now want to attach the necessary handlers to the add-form:
main() {
  load_comics();
  attach_add_handler();
}
The appropriate action when clicking the add text is to enable the add-form. So I add enable_add_form as a click handler:
attach_add_handler() {
  document.
    query('#add-comic').
    on.
    click.
    add(enable_add_form);
}
(yes, I hate horizontal code that much)

To enable the form, I merely need to add the fade-in class to my form:
enable_add_form(event) {
  final form_div = document.query('#add-comic-form');

  form_div.classes.remove('fade-out');
  form_div.classes.add('fade-in');

  form_div.queryAll('a').forEach((el) {
    el.on.click.add(disable_add_form);
    event.preventDefault();
  });
}
For good measure, I also remove the fade-out class (if present). I also add the disable_add_form handler as a click handler to all child <a> tags (e.g. Cancel links).

The disable_add_form handler is, of course, the inverse of enable_add_form:
disable_add_form(event) {
  final form_div = document.query('#add-comic-form');

  form_div.classes.remove('fade-in');
  form_div.classes.add('fade-out');

  form_div.queryAll('a').forEach((el) {
    el.on.click.remove(disable_add_form);
    event.preventDefault();
  });
}
And it works. When the page first loads, there is no form (as before). Clicking on the add comic text causes the form to fade into view:

The problem with this approach is that I need to build those CSS classes in addition to Dart. One way to get around this is to replace the add/remove class names and, instead, use webkit's animation frames:
enable_add_form(event) {
  final form_div = document.query('#add-comic-form');

  var start = new Date.now();
  step(timestamp) {
    var progress = timestamp - start.value
      , opacity = "${progress/1000}";

    form_div.style.opacity = opacity;
    if (progress < 1000) window.webkitRequestAnimationFrame(step, form_div);
    true;
  }
  window.webkitRequestAnimationFrame(step, form_div);
}
Hooking in to this animation stuff is cool, but it is overkill for simple animations.

Happily, while rooting through documentation, I found that CSSStyleDeclaration supports setting animation properties rather easily:
enable_add_form(event) {
  final form_div = document.query('#add-comic-form');

  form_div.style.transition = 'opacity 1s ease-in-out';
  form_div.style.opacity = "1";

  form_div.queryAll('a').forEach((el) {
    el.on.click.add(disable_add_form);
    event.preventDefault();
  });
}
That is pretty darn nice.

Day #271

1 comment:

  1. Access CSS and classes is definitely awesome that way. Attributes are also an easy access, form_div.attributes['someattribute]='somevalue';

    ReplyDelete