Interactive SVG Mockups with Inkscape & Javascript

Update: I have a much more efficient, awesome version of this almost ready to write up, thanks to the awesome help you gave me in the comments. Thanks so much! I’ll post it soon.

Yes, it is clickable. Please click it!


Can’t see the loveliness? Click here.

The backstory

So I’ve been working on non-Fedora projects for the past few weeks, and I just started digging back into the Anaconda mockups this morning. Coming back to UI design from a slight break seemed to magnify issues that I feel in 2011 there must exist a solution–like defining clickable areas within mockups and linking them together to make lightweight interactive prototypes. (Right? ….right?!)

The Dark Side offers cookies

After some failed attempts to embed JQuery into SVG (if you’ve achieved this or know it to be a nutty proposition, please school me!) I got really frustrated and signed up for a free-as-in-beer and proprietary site to see what was possible. I found a site that lets you upload flat PNG mockups and then drag out clickable areas. Per clickable area, you can define what mockup to load next via a dropdown of all uploaded mockups. It also lets you add people to the project to collaborate and modify your screen flows, and it has a comment and notes system.
It was pretty nice. I really enjoyed using it, and felt I got a lot out of it with minimal time investment. I set up some Anaconda mockup screen flows with it. But it felt dirty and wrong. I don’t like relying on service providers who don’t have a financially-defined responsibility to me, and I don’t like any part of my workflow relying on proprietary software. Uploading mockup screens PNG-file-by-PNG-file via a webform also seemed tedious.

The Jedis win & the approach

After that brief but thankfully short-lived flirtation with the dark side and after a bit of rumination, I went through various approaches. First note the layout of the SVGs I’m working with:

  • Multiple SVG files
  • Multiple screen designs within each SVG file
  • Screens are contained in SVG files around specific ‘topic’ areas (e.g., networking, storage, etc.)
  • Screens are arranged within each SVG file vertically to show progress, horizontally to show alternative views (eg, what screen 2 looks like if tab B is active vs tab A).
  • Each screen is defined by a rectangle or group object with an id of the format screen-nameofscreen. For example, ‘screen-keyboard-selection’ is the id for a keyboard selection screen.

The approaches I went through:

  • Use a small javascript function in the top of the SVG that would change the x,y coordinates of any screen’s group object, identified by the group object id passed into the function. Then, use Inkscape’s ‘onclick’ property in ‘object properties’ on the screen to define clickable areas and pass in the ID of the next screen. (Didn’t work… while it seemed to change the x,y values, it didn’t actually move the object.)
  • The same as above, using dx and dy instead of x,y. It wasn’t easily workable because it moved the screens relative to themselves, not relative to the ‘page’ or viewable area of the SVG.
  • Use a small javascript function in the top of the SVG that would change position of any screen’s group object using a translate function, identified by the group object id passed into the function. Then, use Inkscape’s ‘onclick’ property in ‘object properties’ on the screen to define clickable areas and pass in the ID of the next screen. (Didn’t work… I think maybe I screwed up the syntax though. I tried a bunch of different ways and none seemed to do anything.)
  • (The winner)Use a small javascript function in the top of the SVG that would load a different SVG. The SVG files would be named the same as the group object ids passed into the function. Write a script, bash or python, to separate all the individual screens in each SVG file into individual SVG files, one SVG per screen.

Try it yourself!

Step 1: Create your mockups

Well, of course, first you’ll need mockups lovingly arranged on your Inkscape canvas.

Step 2: Label your screens


Next, you’ll need to label the screens in your Inkscape document. Depending on how you created each screen, you can just set the ID (Right-click, Object Properties…) of each screen if you have a large rectangle defining the screen, or you can group each screen’s various objects together, one group per screen, and set the ID (again, right-click, Object Properties…) for the group. You set the id in the ‘id’ field in the ‘Object Properties’ dialog shown above. Make sure you click the ‘set’ button or else your id won’t stick to the object.
Once you’ve got an ID for each screen, it’s time to move on to the next step.

Step 3: Define the clickable areas


This is the fun part! Any object in any screen can be clickable, and can lead to any screen in the set. If you drew buttons in your UI designs, click on the buttons (if they’re grouped, you can use the node selector tool directly below the main selector in the left-hand side toolbox to select objects within each screen group without having to ungroup it) and go to Object > Object Properties. This time, rather than set the id field, we’re going to click on the ‘Interactivity’ disclosure triangle way at the bottom of the ‘Object Properties’ dialog, and we’re going to set a value in the very first field, ‘onclick.’ In the onclick field, type the following, where ‘foobar’ is the name of the screen you want the button to link to:
nextScreen(evt,'screen-foobar')
So, you should be passing into the ‘nextScreen’ function, in quotes, the full id of the screen you would like that button to link to. E.g., ‘screen-keyboard-settings’ or ‘screen-window-list.’
Go through your mockups setting up these connections as you’d like. One cool thing is that once you open up the ‘Object Properties’ dialog, you don’t have to keep closing and re-opening it; it will just automatically update to reflect any object you’ve got currently selected.

Step 4: Add a little magic to your SVG

Save out your svg. For the sake of having a name to refer to it as, let’s call it pandas.svg. Then, open up pandas.svg in your text editor of choice (I like gedit) and paste the highlighted snippet of code below into your svg:



<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="800"
height="600"
id="anaconda-01-welcome"
inkscape:version="0.48.1 r9760"
sodipodi:docname="sample.svg">

<script
type="text/ecmascript"
id="script3">
function nextScreen(evt,screen) {
document.location.href = screen+".svg"
return;
}
</script>

<defs
id="defs23899">
<clipPath
id="clipPath9432">
<rect
width="519.01636"
height="267.28638"

Step 4: Generate individual screen SVG files

More code. In the same directory as your SVG, make a subdirectory named “SVG”, then download this python script into that directory and run it like so:

python script.py foobar.svg

It will spit out one SVG per screen, and each SVG will be named after its screen. All of the SVGs will be placed in the SVG subfolder.
So your directory structure should look something like this after running the script:

  • InkscapeAwesome
    • foobar.svg
    • script.py
    • SVG
      • screen-a.svg
      • screen-b.svg
      • screen-c.svg

Download the SVG splitter python script
For reference, here it is in all its (I’m quite sure) ashamedly badly-written glory:

import os
import xml.dom.minidom
import sys
def get_a_document(doc):
return xml.dom.minidom.parse(doc)
def find_buttons(doc):
buttons = {}
g_nodes = doc.getElementsByTagName('g')
for g in g_nodes:
if g.attributes.has_key("onclick"):
buttons[g.attributes["id"].value]=(g.attributes['onclick'].value)
return buttons
def find_screens(doc):
screens = []
object_nodes = doc.getElementsByTagName('g')
object_nodes += doc.getElementsByTagName('rect')
for obj in object_nodes:
if obj.attributes['id'].value.find('screen') >= 0:
screens.append(obj.attributes['id'].value)
return screens
filename=sys.argv[1];
doc = get_a_document(filename)
buttons = find_buttons(doc)
for screen in find_screens(doc):
new_filename="SVG/"+screen+".svg"
os.system("cp "+filename+" "+new_filename)
os.system("inkscape -f "+new_filename+" --select="+screen+" --verb=FitCanvasToSelectionOrDrawing --verb=FileSave --verb=FileClose")
print "\nExported "+new_filename

Step 5: Open up in a browser and enjoy!

Your lovely clickable mockups should now just work if you open the first SVG file directly into a browser.
Hold on to the original SVG file you used to generate the screen-by-screen SVG files… edit that file in Inkscape if you’d like to update the mockups, and run the python script again to re-export the individual mockups.
If you’d like to embed the mockups in a webpage, you can try something like what I did in this blog post:

<object data="http://duffy.fedorapeople.org/blog/resources/interactive-mocks/SVG/screen-begin.svg" width="500" height="250" type="image/svg+xml" codebase="http://www.adobe.com/svg/viewer/install/"> </object>

Okay, so the above gets chewed up and spit out on planet. I tried this, no love from planet either:

<iframe width="500" height="250" src="http://duffy.fedorapeople.org/blog/resources/interactive-mocks/SVG/screen-begin.svg"></iframe>

This is actually quite terrible

This is quick ‘n dirty get-it-done style hackery. This is not awesome. It’s:

  • Horribly inefficient, as each individual screen’s SVG has all screens’ artwork within it (just not in the viewable pane)
  • Slower than it should be because we’re loading a separate file per click rather than panning within the same SVG file
  • The SVG splitter script literally pops up an Inkscape window that closes itself down and pops open and close again per screen in your file.
  • Written by a designer

That being said, it works. If you have ideas on making it better, I would love to hear them!

24 Comments

  1. Graham Lyon says:

    +1 purely for the doom reference – ultimate doom no-clipping, if memory serves… 🙂

  2. this idea is awesome. mockups are very popular these days.
    i choose jquery for my webdesigns, but i dont like so much.
    im looking moo tools for now(it looks like well designed).
    in the other side svg is working good with browsers (gecko,webkit), and scriptable.
    im thinking about mockups for a while. i want to build a web application
    for collecting and categorizing all linux releated mockups. people can start own
    mockups on the application, everyone can see/rate/comment/fork. After read your post,
    i tought why i dont include a basic js library/editor for interactive mockups!!!
    what do you think about that?
    PS: sorry about my language.

  3. Cool!!! It’s really a good news! And how about manipulate some nodes?

  4. Jack says:

    Ah, beautiful, and good use of an emerging standard. While I don’t have a lot of experience in this area yet, it’s good to see how easy it can be to get started. I’m looking forward to what comes from this, as it seems quite flexible.

  5. leighman says:

    How do you get the fonts to display when the user doesn’t have them installed?
    Just converted to path?

    1. mairin says:

      Converting them to path actually added several megs to the files, so I flattened the text to bitmap. Cheap, but effective.

  6. Jens says:

    I solved this problem by drawing each mockup image on a separate layer on top of each other.
    See the source:
    http://pastebin.com/5a2e3BjX

    1. mairin says:

      Yeah, I avoided this approach because i like spreading my mockups out on one layer, but I think across layers may be the most efficient way for the end interactive animation. Thanks for this, I’ll give it a try.

  7. would be a good idea to do an extension for that, its useful for gui design

  8. pratfall says:

    I think you just re-created Hyper Card in SVG. Brilliant!

  9. Did you know you can use <a> tags in SVGs? I don’t see a way to do it in Inkscape.
    http://www.w3.org/TR/SVG11/linking.html#Links

    1. mairin says:

      I didn’t… Inkscape has the built-in ‘onclick’ attribute though, so it seems it might be a better bet. I want to place priority on working with what Inkscape’s got to make the workflow easier – it would be a bummer to have to copy/paste a tags manually in the SVG I think. Unless Inkscape does have UI to place them; Inkscape has tons of hidden goodies…!

      1. Found it. Right click on the object and select “Create Link” from the menu. There’s no immediate feedback that a link has been created, and you have to right click again and select “Link Properties” to give a target. That property sheet should just pop-up when you create the link, and preferably not in some far corner of the screen like it did for me.

  10. Robin Norwood says:

    Looks awesome, I’ll definitely give it a try next time I do mockups. I think there’s a typo in step 4 (er…the second step four, you have two of them ;-)), though:
    python foobar.svg
    should probably be:
    python script.py foobar.svg

    1. mairin says:

      LOL thanks for the catch, I fixed it! You might want to try the layers method for a faster animated SVG!

  11. moving elements: Use the translate method:
    svg.getElementbyId(oid).setAttribute(‘transform’, ‘translate(‘+x+’,’+y+’)’);
    You also need some code to handle breaking down matrix transformations since groups often have transforms from Inkscape.
    Email me for schooling.

    1. mairin says:

      Cool, thanks! As I suspected, I had the syntax for the translate call wrong 🙂

  12. Casey Dahlin says:

    If you’re going to go this far, why not just start doing the mockups in glade? Its not as freeform as making images but its got many, many advantages, and I’m sure the tool could get better for what you’re doing with your use case pressing on it.

  13. Axel says:

    Hi Máirín,
    Assuming you’ve linked the jQuery.js file in the .html file and embedded the .svg file in said .html file using
    You can access jQuery from an .svg file by using ‘parent’ like so:
    parent.$(‘bla’).doIt()

  14. Axel says:

    Argh! My previous post got mangled. Let’s see if this works:
    …and embedded the .svg file in said .html file using:
    You can access…

  15. Axel says:

    Nope, still didn’t work. Anyway, I meant to say embed the svg file using the embed tag.

  16. Very cool article. I love seeing people finally start using SVG in web development. (After preaching it for years…)
    For the interested reader, I wrote a similar article some time ago, but focused more on CSS and how to author it with both the SVG wireframe and the final HTML in mind: http://www.manuel-strehl.de/dev/wireframes_with_svg.en.html
    (@Greg: I also explained there, how to get an a element in SVG 😉 )

    1. Greg says:

      I would just type it in a text editor and hit reload in the browser. 😉
      I’m doing different things now, but I was using the W3C XHTML+MathML+SVG profile a few years ago to make some tools for work including an interactive graph and a device demonstration.

  17. To make this usable for most people you would need to have a way to make the mouse pointer change to a target hand icon over active elements. Otherwise most people would never think to click.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.