Saturday, February 18, 2012

Rotating Image Carousel in Dart

‹prev | My Chain | next›

For the past few days I have been enjoying playing with the concept of an image gallery in Dart. Actually, it is as much playing with experimental CSS3 features as it has been Dart, but regardless, it has been fun.

I had gotten the gallery working to the point that I could move the main image off to the side, shrink and blur it:


The animation was all done with simple CSS3 (well, some of it was really CSS1):
removeEl(el) {
  if (el == null) return;

  el.style.transition = 'all 1s ease-in-out';

  el.style.left = '550px';
  el.style.height = '150px';
  el.style.width = '250px';
  el.style.filter = 'blur(3px)';

  window.setTimeout(() {
    el.remove();
  }, 7*1000);
}
Instead of doing something like that, I would like to make a more realistic carousel. Using CSS3 transform and translate, I ought to be able to make the images rotate about, like a carousel or merry-go-round. So I break things up in to an ImageCarousel class:
class ImageCarousel {
  Element el;
  List image_urls;

  int panelWidth = 250;
  int _pos = 0;

  Map _image_cache;

  ImageCarousel(this.el, this.image_urls) {
    _image_cache = {};

    el.style.perspective = '1000px';

    draw();
  }
}
I am going to use panels on this carousel of width 250 pixels -- that should yield a carousel that is a little larger than 500 pixel wide, depending on perspective. For perspective, or the distance that the user is from the page, I use 1000 pixels.

I instanstiate this object with:
main() {
  final gallery_el = document.query("#gallery");
  final urls = images.
    split("\n");

  var gallery = new ImageCarousel(gallery_el, urls);

  window.setInterval(() {
    gallery.next();
  }, 3*1000);
}
The images are a newline separated list of image URLs. The #gallery element is a simple <div> tag on the page.

After the object is created, the constructor calls the draw() method. The draw() method calls five private draw methods that draw each side of the carousel:
void draw() {
    _draw0();
    _draw1();
    _draw2();
    _draw3();
    _draw4();
  }
Each of which looks something like:
_draw1() {
    var img = imageFor(image_urls[_pos + 1]);

    img.style.transition = 'all 1s ease-in-out';
    img.style.position = 'absolute';
    img.style.transformStyle = 'preserve-3d';
    img.style.transform = 'rotateY(-45deg) translateZ(${_depth()}px)';
  }
I am still using the same transition of 1 second, easing into and out of motion. I "preserve-3d" so that the images do not look flat on the page. And lastly, I rotate the images, depending where on the carousel they happen to be. The actual 3D transform is done with CSS3 transforms. Here, I am rotating the image about the Y axis. The translate along the z-axis (out of the page) is necessary because the images to the left and right of the main image are rotated about the center of the image. That is, it would look as if one edge were sticking out of the page. To get everything looking OK, I have to ensure that the images behave as if they are a certain distance away from the center. The _depth() function is swiped from the very cool Intro to CSS 3D transforms article.

And it works. Kind of:


I need to improve the rotate-in transform so that it does not seem as though it overwrite the previous image, only for the previous image to pop out. Still, this is a pretty good start. I think tomorrow I will take some time to make it a little more Darty.


Day #300

1 comment: