Dec 052011
 

Concerto 2 is going to be a technical marvel if I have anything to say about it.  We’re going to be using SVG when possible as alternatives to server-size RMagick work when we need to generate simple graphics.  In theory this will save a few ms of processing time (returning text > processing an image file) and reduce our cache size if enough people support svg images.  We’ll still fall back to PNG / JPEGs for people with outdated browsers, and you probably should too; there’s a lot of folks out there who don’t have SVG support… optimistically this will be the minority.

One of the things that Concerto does it return a small preview of a template when you’re modifying it.  A template is essentially a high-res background image (often 1920×1080) and an accompanying set of transparent boxes (positions) that content get displayed in and the preview is really important to help see where those invisible boxes are.  This turned out to be really easy to implement in <svg> using the <rect> element.  Just like I’d draw div elements on a screen’s dom, I can draw rectangles positioned relative to the svg image to recreate the exact same layout but in a more graphical fashion.  The background image was quickly added in using svg’s image element, setting it’s width and height to 100% and the x and y coords to 0,0.

<image id="background" xlink:href="/media/19" height="100%" width="100%" x="0" y="0" />

I found that this almost scaled the template appropriately.  By default the aspect ratio was constraining it to the image’s aspect ratio, so the width and height of 100 was really width = 100% or height = 100% depending what was smaller.  Quick fix for small stretching issue was to add preserveAspectRatio=”none”.

<image id="background" xlink:href="/media/19" height="100%" width="100%" x="0" y="0"
       preserveAspectRatio="none" />

This was working wonderfully, and with my rectangles overlaying the positions it was a near pixel perfect replica of the code that I had already used to generate the PNG / JPEG images.  The only subtle differences were in the text / font rendering, and I don’t really care too much about that.

One problem left: resizing the svg / resizing the window.

Despite using relative sizes with percentages, the image wasn’t getting resized when the window was resizing like all the other objects were.  This meant that you needed to refresh the page to see the correct box placement over the image if you ever dragged thing, and that’s not a very desirable outcome.  I didn’t have much luck figuring out why this behavior was happening, but it seems like the image object is a special case in SVG-land that isn’t handled the same as drawable graphic elements like text or images.

To work around this I ended up writing some very short emcascript (aka JavaScript) and embedding it in the SVG.  I’ve tested the resulting behavior in Chrome and Safari and will assume it works in Firefox too.  Every time the svg get’s resized the onresize event is triggered, just like in JavaScript’s and we fire a callback to resize the image element… really just reminding it to fill the screen.

 
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink" height="100%" width="100%"
     onresize="resize(evt)">
  <script type="text/ecmascript"><![CDATA[
    function resize(evt){
      var background = document.getElementById('background');
      background.setAttribute("width", "100%");
      background.setAttribute("height", "100%");
    }
  ]]></script>
  <g>
    <image id="background" xlink:href="/media/19" height="100%" width="100%" x="0" y="0"
           preserveAspectRatio="none" />
    <g>
      <rect x="2.5%" y="2.6%"
            width="56.7%" height="77.0%" 
            style="fill:grey; stroke:none; fill-opacity:0.6;"
            id="position_25" />
      <text y="45.1%" x="30.85%" 
            style="fill:black; stroke:black;" font-size="300%" text-anchor="middle">
        Graphics
      </text>
    </g>
    <g>
      <rect x="22.1%" y="88.5%"
            width="75.4%" height="10.0%" 
            style="fill:grey; stroke:none; fill-opacity:0.6;"
            id="position_26" />
      <text y="97.5%" x="59.8%" 
            style="fill:black; stroke:black;" font-size="300%" text-anchor="middle">
        Ticker
      </text>
    </g>
  </g>
</svg>

If you’re particularly curious, you can see this code in Concerto 2 here or by appending “.svg” to a template preview link in a Concerto 2 install to force the svg image.