Monday, June 9, 2014

Easy Snap.svg Translations in Polymer


I believe (hope) that I am ready to connect the pieces of my custom <x-pizza> Polymer element with Snap.svg.

Yesterday, I learned that Snap.svg operates on <svg> tags instead of my beloved <div> tags. Actually, <div> aren't beloved at all—I'm just trained via much heartache to keep throwing more and more <div> tags at a classical DOM problem until something works. Trained so much that I instinctively do so even when the situation does not call for it. Anyhow…

I am loading SVG assets into a svgContent object inside the <x-pizza> element. After last night, I put the base pizza SVG content inside a <div> then Snap() the newly innerHTML'd <svg> element:
  // ...
  snap: null,
  _updateGraphic: function() {
    this.$.pizza.innerHTML = this.svgContent['pizza.svg'];
    this.svg = this.$.pizza.children[0];
    this.snap = Snap(this.svg);

    this._addWholeToppings();
    this._addFirstHalfToppings();
    this._addSecondHalfToppings();
  },
  // ...
In the remainder of my code, I am still using the this.svg node instead of the this.snap element. Hopefully switching to the latter will improve things like:
  // ...
  _addSecondHalfTopping: function(maker) {
    for (var i=0; i<20; i++) {
      var angle = 2*Math.PI*Math.random();
      var u = Math.random() + Math.random();
      var distance = 125 * ((u < 1.0) ? u : 2-u);

      var topping = maker.call(this);
      topping.setAttribute(
        'transform',
        'translate(' +
          (142 + Math.abs(distance * Math.sin(angle))) + ', ' +
          (142 + (distance * Math.cos(angle))) +
        ')'
      );
      this.svg.appendChild(topping);
    }
  },
  // ...
I do not believe that there is much to be improved with the random-placement code at the outset, but hopefully Snap.svg can clean up the topping placement code. And it does… a little:
  // ...
  _addSecondHalfTopping: function(maker) {
    for (var i=0; i<20; i++) {
      var angle = 2*Math.PI*Math.random();
      var u = Math.random() + Math.random();
      var distance = 125 * ((u < 1.0) ? u : 2-u);

      var topping = maker.call(this);
      topping.attr({transform:
        'translate(' +
          (142 + Math.abs(distance * Math.sin(angle))) + ', ' +
          (142 + (distance * Math.cos(angle))) +
        ')'
      });
      this.snap.add(topping);
    }
  },
  // ...
(I also switch the maker code to return a Snap.svg element instead of a node to make that work.)

That is really only a minor improvement over the previous raw SVG coding. What might make it better is eliminating all that ugly string concatenation. Short of pulling in yet another JavaScript library, I am not going to get string interpolation. But Snap.svg does give me a Matrix object that can translate an element. Y'know… with numbers:
  // ...
  _addSecondHalfTopping: function(maker) {
    for (var i=0; i<20; i++) {
      var angle = 2*Math.PI*Math.random();
      var u = Math.random() + Math.random();
      var distance = 125 * ((u < 1.0) ? u : 2-u);
      var t = new Snap.Matrix().
        translate(
          (142 + Math.abs(distance * Math.sin(angle))),
          (142 + (distance * Math.cos(angle)))
        );

      var topping = maker.call(this);
      topping.transform(t);
      this.snap.add(topping);
    }
  },
  // ...
Aside from the rest of the code that randomized X-Y positions over a circle, this code is pretty clear. It lost some unnecessarily verbose DOM coding as well as string concatenation, which counts as a win. And, the <x-pizza> element still works:



With that, the full <x-pizza> element is using Snap.svg—except for some animation code, which I will convert tomorrow. So far, the conversion from raw SVG to Snap.svg for <x-pizza>'s assets has been nice, but not a huge win. I am hopeful that Snap.svg will really shine with said animation code—my current implementation is truly awful!

We'll find out… tomorrow.


Day #89

No comments:

Post a Comment