Using AngularJS with SVG for rich graphical interactivity

First of all, down with Flash! Using flash used to be your only real option when creating rich interactive graphics driven designs for web applications. Does anyone remember Homestarrunner? When desktop computers and occasionally laptops were the only real interface for the internet, flash was acceptable. With the recent influx of traffic from mobile devices which reject flash due to security concerns and performance, it is no longer a viable option. HTML5 to the rescue! Recently 2 popular viable options have sprung into the scene to fill the flash gap, SVG and canvas.

SVGs in my mind are clear winner and will likely be adopted by the web community by large due to a few huge upsides:

I won’t dive too much into how to build SVGs because there are many great tutorials available as well as an awesome open source solution Inkscape or Adobe Illustrator for those with thousands to spend on graphics software. Let’s instead look more into how we can use AngularJS to make SVGs dynamic.

When planning out a dynamic SVG scene it is a good idea to build out a map on where to apply your logic. I want to build a simple scalable weather scene that can be plugged into any weather API and respond to the current conditions. First I will build each scene using simple vector shapes and colors, then I will apply CSS3 keyframes to animate the scenes, and finally I will will wrap an angular controller around the SVG to hook my XML into angular.

Creating the scenes

My first scene will be the “Sunny” condition, so I put together a whimsical sun and a few bubbly clouds. When building the scene I want to keep in mind that not all browsers support keyframe animations, so where I place each element will be where it is rendered in those browsers without support. Once I have my scene together, I select all objects in the scene and group them together. Then I apply an appropriate ID to the group, “Sunny_frame” for this one. It’s a good idea to look at the XML being generated at this point to give yourself an idea of how the tree is being applied.


      
      
        
        
        
        
        
        
        
        
          
            
              
            
          
          
            
              
            
          
        
        
          
            
              
            
          
          
            
              
            
          
        
        
        
          
            
              
            
          
          
            
              
            
          
        
      
    

The scene is all of the path objects grouped together under one g element which has the attribute id="Sunny_frame". If you look at each path you can see the style plainly spelled out using CSS selectors style="fill:#ffffff; fill-opacity:1; stroke:#ffffff; stroke-width:0.40407968". The geek in me gets pretty excited about that. All I would have to do to apply style with CSS would be to copy the ID of one of these elements, and override it’s applied styles!

Next I build two more scenes one for cloudy and one for rainy then group and ID them accordingly.

Now looking at the XML tree for this SVG you would see the 3 major conditions I want to represent grouped together and ID’d appropriately.


  
      
  
  
      
  
  
      
  

Next I add a text node for some AngularJS dynamic goodness.

Now I have each of my scenes ready and a text node that I will later make dynamic with Angular, it’s time to export the SVG and apply the CSS to animate the scene. Once I have exported the CSV, I open it up and change the height and width parameters to 100%. This allows the SVG to expand dynamically to the element I wrap it in.


  

The great thing about the SVG element is the viewBox attribute that allows the element to retain its aspect ratio when resizing.

Wrapping with Angular

Next up is to wrap the element into an angular controller. There are two ways to attack this, if you plan to heavily modify your SVG with angular directives, then copying the SVG directly into your HTML might be the best option. I don’t plan to add any other interactivity, so I will use ng-include to make angular fetch and insert the document. This way I keep my HTML tidy.

<div ng-controller="Weather">
  <div class="backdrop {{cond}}" ng-include="'sun_2.svg'"></div>

Also note that I am printing out the cond variable in the div that wraps my SVG element. I plan to use this to control the color of the sky in the background. Now I create an ultra simple directive and give my two dynamic variables presets.

angular.module('app')
    .controller('Weather', function($scope) {
        $scope.cond = 'Sunny';
        $scope.temp = 72;
    });

Next I make my variables dynamic by allowing a couple of fields to manipulate the scope variables

  <div ng-controller="Weather">
    
<select class="form-control" ng-model="cond" ng-options="value for value in ['Sunny','Cloudy','Rainy']"></select>
<input type="number" class="form-control" ng-model="temp">
<div class="backdrop {{cond}}" ng-include="'sun_2.svg'"></div> </div>

Then I will use CSS to hide my ‘inactive’ conditions and give my sky some color.

.backdrop.Sunny
{
    background-color:#AECAF8;
}
.backdrop.Cloudy
{
    background-color:rgb(150, 179, 196);
}
.backdrop.Rainy
{
    background-color:#7E8DA6;
}
.backdrop #Cloudy_frame,
.backdrop #Sunny_frame,
.backdrop #Rainy_frame
{
    opacity:0;
    transition:all 5s ease;
}
.backdrop.Sunny #Sunny_frame
{
    opacity:1;
    transition:all 5s ease;
}
.backdrop.Cloudy #Cloudy_frame
{
    opacity:1;
    transition:all 5s ease;
}
.backdrop.Rainy #Rainy_frame
{
    opacity:1;
    transition:all 5s ease;
}

Using the combined classes for my wrapping div I apply color to the background suitable for each scene. Then I set the opacity of each scene to 0%. Finally I combine the wrapping divs selectors and set the opacity for the active scene to 100%, ".backdrop.Rainy #Rainy_frame { opacity:1; ...".


Since I set ‘Sunny’ as my preset ‘cond’ variable you can see that my scene is now showing the active scene and if I select another condition the inactive conditions will switch as expected.

The next piece which I won’t get into here is to apply CSS3 keyframe transitions to add animations to my scene and make it appear alive. For more information on how to animate with keyframes check out this awesome resource.

The Result



In Conclusion

Next I could hook this into a JSONP api like Yahoo Weather to dynamically bind the conditions. Let your imagination run wild and think of all of the awesome applications of this graphical, interactive, scalable, and well supported solution!

To name a very few. The abstraction of AngularJS plus the declarative nature of SVG really shine here and allows us to separate our data modeling concerns from our visual output and style. This type of rich interactivity has been available to us for some years via JavaScript and SVG, but the simplicity of combining this with a framework just makes a lot of sense!