Sunday, June 8, 2014

Drawing and Redrawing SnapSVG in Polymer


Encouraged by my initial success with Snap.svg in the <x-pizza> Polymer element, I am ready to make the full switch. Let's see if it goes similarly smoothly.

I am still not 100% committed to using Snap.svg in the “Extras” screencasts for Patterns in Polymer. The purpose of those screencasts is to demonstrate Polymer coding, not SVG. But, if Snap.svg stays out of the way sufficiently, then it could serve just that purpose—especially since coding SVG in native JavaScript is so horrendous.

Anyhow, the SVG assets will likely still come in via <core-ajax> elements in the <x-pizza> definition:
<link rel="import"
      href="../bower_components/polymer/polymer.html">
<link rel="import"
      href="../bower_components/core-ajax/core-ajax.html">
<script src="../bower_components/snap.svg/dist/snap.svg.js"></script>
<polymer-element name="x-pizza">
  <template>
    <core-ajax
       auto
       id="pizza.svg"
       url="../../assets/images/pizza.svg"
       on-core-response="{{responseReceived}}"></core-ajax>
    <!-- ... -->
    <div id="pizza"></div>
    <!-- ... -->
  </template>
  <script src="x_pizza.js"></script>
</polymer-element>
The responseReceived() callback places the SVG asset text in svgContent['pizza.svg'] (or whatever the id on <core-ajax was). I want to take that and start drawing with Snap.svg. I am replacing some good, old-fashioned innerHTML code for the pizza image:
  _updateGraphic: function() {
    this.$['pizza'].innerHTML = this.svgContent['pizza.svg'];
    this.svg = this.$.pizza.children[0];
    if (!this.svgLoaded) return;

    this._addWholeToppings();
    this._addFirstHalfToppings();
    this._addSecondHalfToppings();
  },
Part of the purpose of that is to wipe the contents of the previous display so that the pizza graphic is changed with each topping added or removed the pizza shopper:



My first inclination is to clear the previous Snap paper (if defined) and then start drawing:
  _updateGraphic: function() {
    if (this.snap) this.snap.clear();
    this.snap = Snap(this.$['pizza']);

    var f = Snap.fragment(this.svgContent['pizza.svg']);
    this.svg = f.select('svg').node;
    this.snap.add(f);

    if (!this.svgLoaded) return;

    this._addWholeToppings();
    this._addFirstHalfToppings();
    this._addSecondHalfToppings();
  },
This does not work because the this.$['pizza'] element on which I try to draw is a <div> tag. I had assumed that Snap.svg would start drawing SVG underneath the <div> and it took me a while to realize my mistake.

In the end, I continue to use the innerHTML solution to reset the SVG content, but then run Snap on top of this content now that it is a proper SVG element:
  _updateGraphic: function() {
    this.$.pizza.innerHTML = this.svgContent['pizza.svg'];
    this.svg = this.$.pizza.children[0];

    this.snap = Snap(this.svg);

    if (!this.svgLoaded) return;

    this._addWholeToppings();
    this._addFirstHalfToppings();
    this._addSecondHalfToppings();
  },
That is not a huge win for tonight, but I pushed through a mistake and better positioned myself for continued Snap.svg exploration.

That misunderstanding aside, other Snap.svg work goes much smoother. I was able to replace some ugly pure JavaScript SVG event handlers:
    group.addEventListener('mouseenter', function(e){
      path.setAttribute('style', style + ';filter:url(#topping-drop-shadow)');
      path.setAttribute('transform', 'translate(0,0)');
    });
    group.addEventListener('mouseleave', function(e){
      path.setAttribute('style', style);
      path.setAttribute('transform', 'translate(2.8577037,1.6074583)');
    });
With some much prettier Snap.svg equivalents:
    group.mouseover(function(e){
      path.attr({
        style: style + ';filter:url(#topping-drop-shadow)',
        transform: 'translate(0,0)'
      });
    });
    group.mouseout(function(e){
      path.attr({
        style: style,
        transform: 'translate(2.8577037,1.6074583)'
      });
    });
That would be even prettier to read if JavaScript supported method cascades, but what are you going to do? Code in Dart?



Day #88

No comments:

Post a Comment