Friday, May 23, 2014

DOM Animation of SVG


Up today, I try to build on the SVG loading solution for my <x-pizza> custom Dart Polymer element:



Thanks to last night, I am loading SVG content directly into my application via the Polymer Elements' <polymer-ajax>:
<link rel="import" 
      href="../../../packages/polymer_elements/polymer_ajax/polymer_ajax.html">
<polymer-element name="x-pizza">
  <template>
    <polymer-ajax
       id="pizza.svg"
       url="/packages/svg_example/images/pizza.svg"
       on-polymer-response="{{responseReceived}}"></polymer-ajax>
    <polymer-ajax
       id="pepperoni.svg"
       url="/packages/svg_example/images/pepperoni.svg"
       on-polymer-response="{{responseReceived}}"></polymer-ajax>
    <!-- ... -->
  </template>
  <script type="application/dart" src="x_pizza.dart"></script>
</polymer-element>
When each is loaded, I populate a content map:
  Map svgContent = {};
  responseReceived(e, detail, node){
    svgContent[node.id] = detail['response'];
  }
And then use that content to build up groups of toppings from that SVG text:
  _svgPepperoni()   => _svgImage('pepperoni');
  _svgSausage()     => _svgImage('sausage');
  _svgGreenPepper() => _svgImage('green_pepper');

  _svgImage(basename) => new GElement()
    ..append(
      new SvgElement.svg(svgContent['$basename.svg'])
    );
To animate, I convert the _svgPepperoni() method to make use of the two layers that I added to the SVG toppings the other day:

  _svgPepperoni() {
    var svg = _svgImage('pepperoni');
    return svg
      ..onMouseOver.listen((e){
        svg.query('#up').style.display = '';
        svg.query('#down').style.display = 'none';
      })
      ..onMouseLeave.listen((e){
        svg.query('#up').style.display = 'none';
        svg.query('#down').style.display = '';
      });
  }
Which does the trick nicely:



Except for one little problem. The SVG image is clipped to 25 pixels, which cuts off the nice drop shadow effect of the topping:



Unfortunately, I can find no explanation for why this is the case. My best guess is that the path only accounts for roughly 25 square pixels, which is where the browser gets this value from. Setting width attributes and style values has no effect. It is also possible that this is a function of adding SVG elements to another SVG:



The “inner” SVG in this case is the pepperoni SVG, which I use to populate a <g> element for the topping. I do try adding the path directly to the topping <g> tag, but even that does not make a difference.

Stumped, I call it a night here. Thankfully, the DOM mouse-over code was quite easy. Hopefully I am just making a dumb SVG mistake with the topping size. I will investigate further tomorrow.

Day #72

2 comments:

  1. I don't know if the crop problem is caused by a wrong sized viewport or an effect filter area problem.
    If is it the latter, you can expand the filter area from Inkscape. Filter area is by default 1.5x (150%) of the object that was applied the effect to.
    You just need to open the "Filter Editor..." dialog (you find it at the bottom of the Filters menu), select the Drop Shadow filter and select the lower tab called "Filter General Settings". Here you find the Coordinates and Dimensions of the Filter Effect Area where the filter will be rendered. Just increment Dimensions from 1.5 to 2.0 or more (both in height and width) and change the Coordinates to recenter the effect box, for example from -0.25, -0.25 to -0.50, -0.50.
    This screenshot can help you understand better:
    http://oi57.tinypic.com/21kx99s.jpg
    You can also change the filter effect box size programmatically, changing the attributes of the SvgSvgElement.

    ReplyDelete
    Replies
    1. Much thanks for the hint -- and for all the help along the way!

      Delete