Friday, June 26, 2015

Building an Interactive SVG Pie Chart with Asp.Net MVC Razor and C#

In one of my recent web development projects I set out to redesign a basic pie chart. The previous implementation was generated by a javascript framework that did more than it was used for, and lacked some of the flexibility that we wanted. So for the rewrite I decided to simplify the code, and take an opportunity to learn more about SVG graphics.

Bear in mind as you read this that it's all still pretty "fresh" ...the code probably can (and should) be refactored. But this should serve as a good basic introduction. There are a number of good tutorials on the web for generating SVG pie charts. This post focuses on my implementation using Asp.Net MVC, C#, and Razor. WHat you'll see below is the combination of various bits of knowledge gathered from around the web and a fair amount of tinkering and troubleshooting. I think the final result looks pretty nice, so I figured it was worth sharing.

Why SVG? Because it's a vector-based image, it can scale up or down and never get pixellated, and looks good on any screen from desktop to mobile. The image data itself is very minimal, and because it can be written inline with your HTML, it doesn't require extra HTTP calls. The SVG code itself is pretty simple as well, so I don't need to rely on more intensive server-side image generation code. There are plenty of reasons why SVG is cool, but those are just a few that come to mind right now. SVG is pretty widely supported now across all major browsers except IE8, which at least for my current project I don't need to worry about.

I started out by writing a basic C# class to hold all the relevant information needed for a simple pie chart:

Having this dedicated PieChart class allows me to generate pie charts throughout my application in a consistent way. In my MVC controller, I can build a view model for the page, and include a PieChart object. Then, in my Razor view, I can add a simple call to "DisplayFor" to generate the inline SVG, using a shared display template.

The bulk of the code lives in the shared razor template in Views/Shared/DisplayTemplates/PieChart.cshtml

To keep everything fairly self-contained, I decided to include the SVG-specific C# code right in the Razor file. While this may not adhere very well to separation of concerns, it certainly helps with manageability and making it easier to follow/understand. A fair tradeoff if you ask me. Here's the entire Razor file. I'll explain each section below:


@functions

Normally I don't put complex functions in my razor views, so I didn't know the "@functions" block existed until now. For this application it was the perfect container for some simple trigonometric calculations.

PieSlicePathDesc

In SVG, we can use the path element to define complex shapes. In this case, we'll need to generate multiple "pie slices". This function does the math to calculate the x/y coordinates of each point, and outputs the path description in SVG's language. Basically the path description boils down to these instructions:

  • Start drawing at the center of our pie chart
  • Move to a point on the outside of the circle
  • Draw an arc with a given radius until we reach the next x/y coordinate
  • Draw a line back to the starting point to complete the shape

With all this logic defined here, we can easily call it in the razor markup later.

startAngle

For my pie chart, I want the first slice to start at the top of the circle. Here I set the starting angle.

svg

With most of the code out of the way, we can start generating the actual SVG tags. For a great reference to all the SVG tags and attributes, check out MDN's SVG Element Reference. The viewBox attribute defines the limits of the image using the SVG's internal coordinate system. I sized this image to have a padding around the outside equal to 10% of the radius. This comes in handy later for our hover effect.

style

Here in the SVG I embedded some CSS styling to apply a consistent style to all the pie slices, mainly to give them all a white outline.

@foreach loop

Using Razor, we can easily enumerate over the collection of PieChartSlice objects and generate the SVG paths.

g (Group) element

This is a basic SVG element to group together other SVG elements together. This allows us to combine the pie slice, as well as a tooltip and some hover animation elements.

title element

In SVG, the title element acts much like the title attribute in HTML. In most browsers this text will appear as a tooltip when the mouse hovers over it. I could have used other elements to write the text on the image, but I decided to keep it hidden to keep the image clean and uncluttered. If the user hovers over the pie slice, they can see the label, the value, and the percentage. The text is also available to screen readers and other web accessibility tools.

circle or path

In the event that our pie chart's data set only contains one item, we only need to draw a full circle, not a slice.  If we are generating multiple slices, the SVG path is used, and calls the PieSlicePathDesc method we defined earlier.

Hover animations with animateTransform

To add a little interactivity, I wanted to add a hover effect to each pie slice, where the slice would expand outward while hovering over it. To do this I added some animateTransform elements to the group containing the pie slice. There is one animation that expands from the center by 5% on mouseover, and another animation that shrinks back down on mouseout.

Finished!

My final implementation was a bit more complex. I added some code for generating the pie slice colors and some other changes specific to my project, but this should give you a basic idea of how it works.

To see the final result, below is some actual inline SVG generated by this code:

Thinking: 0:38:39 (39.3%) Writing Code: 0:34:16 (34.9%) Researching: 0:11:33 (11.8%) Refactoring: 0:09:52 (10%) Getting Distracted: 0:03:54 (4%)