Friday, June 6, 2014

SVG Assets in Polymer


Tonight seems a good opportunity to verify that my recent approach to an SVG Polymer custom element will work as well in JavaScript as it has in Dart.

I start with a bower update which, thanks to my non-explicit version dependency in bower.json installs Polymer 0.3.1:
➜  js git:(master) ✗ grep -C1 Polymer/polymer bower.json
  "dependencies": {
    "polymer": "Polymer/polymer"
  }
➜  js git:(master) ✗ bower update 
...
polymer#0.3.1 bower_components/polymer
├── core-component-page#0.3.1
└── platform#0.3.1
...
I fire up a Python simple server (it's in my shell history):
➜  js git:(master) ✗ python -m SimpleHTTPServer 8000
Serving HTTP on 0.0.0.0 port 8000 ...
And... everything still works:



Refreshing.

It works, but those are the in-application drawn SVG elements:
  // ...
  _svgSausage: function() {
    var el = document.createElementNS("http://www.w3.org/2000/svg", "circle");
    el.setAttribute('r', '3');
    el.setAttribute('fill', 'brown');
    return el;
  },
  // ...
In the Dart version, I switched to an asset loading approach that lent itself better to a designer workflow. I relied on the <core-ajax> Polymer element in Dart, so let's see how it works in JavaScript. I add core-ajax to the list of dependencies:
➜  js git:(master) ✗ bower install -S Polymer/core-ajax

Then, I copy the SVG assets into the JavaScript version of this <x-pizza> element:
➜  js git:(master) ✗ mkdir assets/images     
➜  js git:(master) ✗ cp ../dart/lib/images/* assets/images/
➜  js git:(master) ✗ ls assets/images 
green_pepper.svg  pepperoni.svg  pizza.svg  sausage.svg
Next, I teach the template to use <core-ajax> to load these assets. I need the appropriate <core-ajax> tags for each asset and need to link in the core-ajax.html definition:
<link rel="import"
      href="../bower_components/polymer/polymer.html">
<link rel="import"
      href="../bower_components/core-ajax/core-ajax.html">
<polymer-element name="x-pizza">
  <template>
    <core-ajax
       auto
       id="pizza.svg"
       url="../../assets/images/pizza.svg"
       on-core-response="{{responseReceived}}"></core-ajax>
    <!-- ... -->
  </template>
  <script src="x_pizza.js"></script>
</polymer-element>
The rest of the effort flows fairly smoothly from here. I no longer have Futures, so I use a state variable to tell the graphics when all SVG toppings have loaded:

  // ...
  svgContent: {},
  svgLoaded: false,

  responseReceived: function(e, detail, node){
    this.svgContent[node.id] = detail.response;

    var numLoaded = Object.keys(this.svgContent).length,
        numDefined = this.shadowRoot.querySelectorAll('core-ajax').length;
    if (numLoaded == numDefined) {
      this.svgLoaded = true;
      this._updateGraphic();
    }
    if (node.id == 'pizza.svg') this._updateGraphic();
  },

  /* Start SVG Code */
  svg: null,

  _updateGraphic: function() {
    this.$['pizza'].innerHTML = this.svgContent['pizza.svg'];
    if (!this.svgLoaded) return;

    this._addWholeToppings();
    this._addFirstHalfToppings();
    this._addSecondHalfToppings();
  },
  // ...
That aside, this follows the core-ajax loading convention that I was able to use in Dart closely. Well, except that manipulating SVG in JavaScript is ugly as sin:
  _svgSausage: function() {
    var ns = "http://www.w3.org/2000/svg";
    var group = document.createElementNS(ns, "g");

    group.innerHTML = this.svgContent['sausage.svg'];
    var svg = group.querySelector('svg');

    var mouseCircle = document.createElementNS(ns, "circle");
    mouseCircle.setAttribute('r', '15');
    mouseCircle.setAttribute('cx', '15');
    mouseCircle.setAttribute('cy', '15');
    mouseCircle.setAttribute('style', 'opacity:0');
    group.appendChild(mouseCircle);
    // ...
  }
Compare that to the equivalent Dart:
  _svgImage(basename) {
    var svg = new GElement()
      ..append(
          new CircleElement()
            ..setAttribute('r', '15')
            ..setAttribute('cx', '15')
            ..setAttribute('cy', '15')
            ..setAttribute('style', 'opacity:0')
        )
      ..append(new SvgElement.svg(svgContent['$basename.svg']));
    // ...
  }
Method cascades and proper constructors really help. That aside, the JavaScript code flows nicely given my experience from Dart. I will likely take tomorrow to see if there are any JavaScript specific improvements that I can realize (maybe checking out snap) , but this seems promising.


Day #86

No comments:

Post a Comment