A common scenario when building a touch interface is having a particular UI element that you want to scale seamlessly between different screen sizes. This is different than having the layout scale. When scaling a layout, you make sure that content appears in roughly the same place, or in an optimized layout for a given screen real estate. What I am talking about here is you have a particular interactive element that needs to scale according to screen size. If you have a simple need, like scaling a logo, you can find examples of how to do that here.
An example would help illustrate what I am talking about. Imagine I am making a basketball application and I want to allow users to tap an on-screen basketball court to track where shots are taken. An example of what I am talking about is shown to the right. My screen layout will be dependent on the court. The bigger the screen, the bigger the court. Using SVG is a great way to solve this problem.
First, we need to acquire or create our SVG. I am not going to go into all the in-and-outs no this end. In my example, I create the SVGcourt using Inkscape.
When working with SVGthat you want to make interactive, you will be embedding the SVG directly into your HTML markup. For example, in my basketball application, I may want the court to always be in the top-middle of the screen. I want to have equal spacing on each side, but I want to make sure that court is always the biggest part of the main display. To accomplish this, I could set up a grid with the following CSS and HTML.
mypage.css
.main {
display: -ms-grid;
-ms-grid-columns: 1fr 4fr 1fr;
-ms-grid-rows: 3fr 1fr;
}
.court {
-ms-grid-column: 2;
}
mypage.html
<div class="main">
<svg class="court">...court svg...</svg>
</div>
A few things to point out. One, the <svg> is really like any other tag. Insert into the markup where ever you want the SVG to appear. Secondly, you can apply things like element id’s and CSS to help layout your SVG. Adding interactivity to SVG elements is handled just like html elements. For example, if I want to handle a click event on the court element, and it has an id=”myCourt”, I could write code like this:
myCourt.onclick = function() { console.log("shot"); };
There are, however a few more things we want to do to really make our SVG layout consistently.
In my example, I drew a basketball court using Inkscape. The first element in that drawing was the outer rectangle of the court. The initial SVG looks something like this:
<svg>
<g id="FullCourt">
<rect
style="fill:#1a1a1a"
id="fullCourt"
width="725"
height="515"
x="0"
y="0" />
</g>
</svg>
This is not a SVG tutorial, but this markup basically defines a group layer that contains a single rectangle. Notice, however that rectangle contains properties to define its height and width. If we just stuck this SVG into our markup, we would get the same size court regardless of how big or small the screen was. I want the court, however, to scale proportionally to fill all of the available space in the middle column of the grid that contains it. Because the columns in my grid are weighted, the middle column will grow and shrink based on the horizontal pixel count of my screen. We solve this by updating the root <svg> tag to use a viewbox:
<svg viewBox="0 0 725 515">
...
</svg>
The viewbox attribute tells the SVG to scale the content inside the tag to fit the available space by drawing the children elements in a coordinate system defined by the dimensions in the attribute. In my example, the viewport coordinates match the size of the bounding court rectangle, so everything will just scale normally. But you can change the viewbox values to zoom in and zoom out of the SVG.
What becomes a challenge, however, when using this approach is that the coordinate systems now differ between the two environments. When I click or tap inside the SVG, Windows will report the screen xy coordinates of the click or tap. These are not the same coordinates inside the SVG. This is not a problem if all you want to do are handle click events, but if you want to update the SVG as a result, you need to compensate for the difference in coordinate systems. For example, in my app, if you tap on the court, I may want to place a circle to indicate a shot. Where do I draw the circle? I have the tap screen coordinates, but those are not identical the the SVG drawing coordinates.
function coordinateTransform(screenPoint, someSvgObject) {
var CTM = someSvgObject.getScreenCTM();
return screenPoint.matrixTransform(CTM.inverse());
}
As much as I would like to take credit for figuring this out on my own, matrix transforms and I seldom cross paths, and for good reason. If you wan the details, you can go to SVG Coordinate Transformations. Our coordinateTransform() function, however, is just half the battle. In the example mentioned, we want to draw a shot on the court (represented by a circle, perhaps). Here is how we could accomplish that:
function drawShot(mouseEventArgs) {
var point = Court.createSVGPoint();
point.x = mouseEventArgs.pageX;
point.y = mouseEventArgs.pageY;
var shotPoint = coordinateTransform(point, Court);
// Draw circle
}
In drawShot(), we pass in the coordinates from the mouse or tap event handler. shotpoint.x and shotpoint.y will represent the xy coordinates of the mouse click in our SVG coordinate system. We can then draw a new SVG element at this location with code similar to this:
var shot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
shot.setAttributeNS(null, "r", 15);
shot.setAttributeNS(null, "class","shot");
shot.setAttributeNS(null, "cx", shotPoint.x);
shot.setAttributeNS(null, "cy", shotPoint.y);
myCourt.appendChild(shot);
I’ll let you research how to draw other SVG elements on your own. One thing to point out is the setting of the class attribute on the new circle. You can use css classes to control the appearance of your SVG elements, in addition to their location. In this case, I may want to control the thickness of the line, the fill color, etc.
.shot{
pointer-events: none; fill:#d40000;fill-opacity:1;stroke:#000000;
stroke-width:3.68409061;stroke-miterlimit:4;stroke-opacity:1;
stroke-dasharray:none;
}
I hope this helps getting started with SVG on Windows 8.