Sunday, February 19, 2012

Darty Animation

‹prev | My Chain | next›

Last night, I was able to get my Dart-based image carousel more or less working:

video

When the image on the right appears, it immediately covers the image that used to be there before it has a chance to rotate. The effect is less than desirable. Part of the problem is that I am starting the carousel at image #3 instead of #4:


The carousel achieves this effect by drawing each face of the carousel one at a time. When the carousel moves to the next image, it moves each image on the carousel. The animation effect comes from the CSS3 transform and translate transitions:
    // ...
    img.style.transition = 'all 1s ease-in-out';
    img.style.transform = 'rotateY(-45deg) translateZ(${_depth()}px)';
    // ...
With each move, I apply a different rotateY to the image. The transition takes care of the rest. Before fixing the right image, I factor the image drawing functionality into a single private method:
  _draw(offset, angle) {
    var img = imageFor(image_urls[_pos + offset]);

    img.style.transition = 'all 1s ease-in-out';
    img.style.position = 'absolute';
    img.style.transformStyle = 'preserve-3d';
    img.style.opacity = '0.8';
    img.style.transform = 'rotateY(${angle}deg) translateZ(${_depth()}px)';

    return img;
  }
With that, the five faces of the visible carousel can be expressed quite simply as:
  _draw0() => _draw(0, '-90');
  _draw1() => _draw(1, '-45');
  _draw2() => _draw(2, '0');
  _draw3() => _draw(3, '45');
  _draw4() => _draw(4, '90');
I really dig that compact dart function/method syntax.

The externally facing draw() is expressed as:
  void draw() {
    _draw0();
    _draw1();
    _draw2();
    _draw3();
    _draw4();

    // make sure old images don't linger
    _removeOld();
  }
Finally, I have a next() method that rotates things by incrementing the internal _pos counter and telling the carousel to draw itself:
  void next() {
    _pos++;
    draw();
  }
Actually starting at #4 instead of #3 helps, but there is still some bleed though of #4 superimposed over #3. To resolve that, I start #4 with zero opacity and give #3 an opacity of 0.8. For good measure, I give #0 an opacity again of zero so that there is no abrupt disappearance when the image is removed. Since the _draw() method returns an Image element, and since the shorthand syntax for _draw0 through _draw4() returns the result on the right-hand side of the hash-rocket (=>) operator, I can apply these opacities in the draw() method like so:
  void draw() {
    _draw0().style.opacity = '0';
    _draw1();
    _draw2();
    _draw3().style.opacity = '0.8';
    _draw4().style.opacity = '0';

    _removeOld();
  }
When an image is added in slot #4, it has opacity zero. When it rotates to #3, it is assigned opacity of 0.8, which it retains as it move to #2 and #1. Finally, when it reaches #0, its opcacity is again set to zero where it can be safely removed from the UI.

The effect is much nicer:

video

After adding a prev() method to my carousel, I swap out the auto-rotating for a rotate by arrow key handler:
main() {
  final gallery_el = document.query("#gallery");
  final urls = images.
    split("\n").
    filter((img) { return !img.isEmpty(); }).
    map((img) { return "images/${img}"; });

  var gallery = new ImageCarousel(gallery_el, urls);

  document.on.keyUp.add((event) {
    if (event.keyCode == 37) gallery.prev();
    if (event.keyCode == 39) gallery.next();
  });
}
Last up tonight, I try compiling this into Javascript using Dart's frogc compiler. It works, but nothing displays, at least not under Firefox version 10. I trace this to frogc's use of -moz-transform-style in the generated JS. If I remove that:
// ...
CSSStyleDeclarationWrappingImplementation.prototype.set$transformStyle = function(value) {
//  this.setProperty(("" + CSSStyleDeclarationWrappingImplementation.get$_browserPrefix() + "transform-style"), value, "");
  this.setProperty(("" + "transform-style"), value, "");
}
Then it works, although the images are not scaled quite a smoothly in Firefox as they were in Dartium:


All in all, it was relatively easy to get this carousel working in Dart. It took me longer to figure out the necesary CSS3 styles than it did to get the Dart coded.


Day #301

No comments:

Post a Comment