22.4. VML: Vector Markup Language VML is Microsoft's answer to SVG. Like SVG, VML is an XML grammar for describing drawings. VML is similar to SVG in many ways. Though not quite as extensive, VML provides a full set of drawing capabilities and is available natively in IE 5.5 and later. Microsoft (and several partner companies) submitted VML to the W3C for consideration as a standard, but this effort never went anywhere. The best documentation for VML is Microsoft's submission, still available at the W3C web site at http://www.w3.org/TR/NOTE-VML. Note that despite the presence of this document on the W3C site, it has never been standardized, and the implementation in IE is proprietary to Microsoft. VML is a powerful technology that never really caught on. Because it is not widely used,[*] it has apparently never been carefully documented. Microsoft's own web sites typically point to their W3C submission as the authoritative source of documentation. Unfortunately, since this document was a preliminary submission, it never went through the careful review of the standardization process and remains incomplete in some parts and confusing in others. When working with VML, you may need to experiment with the IE implementation in order to create the drawings you want. That is a relatively minor caveat when you consider that VML is a powerful client-side vector-drawing engine, embedded in the web browser that still dominates the market. [*] Google Maps (http://local.google.com) is the only high-profile site I know of that uses VML. VML is an XML dialect, distinct from HTML, but IE does not support true XHTML documents, and its DOM implementation does not support namespace-aware functions such as document.createElementNS( ). IE makes VML work by using an HTML "behavior" (another IE-specific extension) to handle tags in the VML namespace. All HTML files that contain VML must first declare the namespace like this: <html xmlns:v="urn:schemas-microsoft-com:vml"> Alternatively, this namespace declaration can be achieved in an IE-specific (and nonstandard) way like this: <xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v"/> Then you must specify how tags in that namespace are handled with this nonstandard bit of CSS: <style> v\:* { behavior: url(#default#VML); }</style> Once these pieces are in place, VML drawings can be freely intermixed with HTML, as in the following code that produces results much like the SVG figure of Figure 22-4: <html xmlns:v="urn:schemas-microsoft-com:vml"> <head> <style> v\:* { behavior: url(#default#VML); }</style> </head> <body> This is a red square:<v:rect style="width:10px;height:10px;" fillcolor="red"/> This is a blue circle:<v:oval style="width:10px;height:10px;" fillcolor="blue"/> </html> However, this chapter focuses on using client-side JavaScript to draw graphics dynamically. Example 22-10 shows how to create a pie chart using VML. The example is much like the SVG pie chart example, with the substitution of VML drawing primitives for SVG primitives. For simplicity, this example combines a makeVMLCanvas( ) function and pieChart( ) function with the code that invokes those functions to produce the chart. The output of this code is not shown here: it looks quite a bit like the SVG pie chart of Figure 22-5. Example 22-10. Drawing a pie chart with JavaScript and VML <!-- HTML documents that use VML must declare a namespace for it --> <html xmlns:v="urn:schemas-microsoft-com:vml"> <head> <!-- This is how we associate VML behavior with the VML namespace --> <style> v\:* { behavior: url(#default#VML); }</style> <script> /* * Create and return a VML <v:group> element in which VML drawing can be done. * Note that the returned element has not yet been added to the document. */ function makeVMLCanvas(id, pixelWidth, pixelHeight) { var vml = document.createElement("v:group"); vml.setAttribute("id", id); vml.style.width = pixelWidth + "px"; vml.style.height = pixelHeight + "px"; vml.setAttribute("coordsize", pixelWidth + " " + pixelHeight); // Start with a white rectangle with a thin black outline. var rect = document.createElement("v:rect"); rect.style.width = pixelWidth + "px"; rect.style.height = pixelHeight + "px"; vml.appendChild(rect); return vml; } /* Draw a pie chart in a VML canvas */ function pieChart(canvas, data, cx, cy, r, colors, labels, lx, ly) { // Get the canvas element if specified by id if (typeof canvas == "string") canvas = document.getElementById(canvas); // Add up the data values var total = 0; for(var i = 0; i < data.length; i++) total += data[i]; // Figure out the size (in degrees) of each wedge var angles = [] for(var i = 0; i < data.length; i++) angles[i] = data[i]/total*360; // Now loop through all the wedges // VML measures angles in degrees/65535 and goes // counterclockwise from 3 o'clock startangle = 90; // Start at 12 o'clock. for(var i = 0; i < data.length; i++) { // Tweak the angles so that our pie goes clockwise from 12 o'clock. var sa = Math.round(startangle * 65535); var a = -Math.round(angles[i] * 65536); // Create a VML shape element var wedge = document.createElement("v:shape"); // VML describes the shape of a path in a similar way to SVG var path = "M " + cx + " " + cy + // Move to (cx,cy) " AE " + cx + " " + cy + " " + // Arc with center at (cx,cy) r + " " + r + " " + // Horiz and vertical radii sa + " " + a + // Start angle and total angle " X E"; // Close path to center and end wedge.setAttribute("path", path); // Set wedge shape wedge.setAttribute("fillcolor", colors[i]); // Set wedge color wedge.setAttribute("strokeweight", "2px"); // Outline width // Position the wedge using CSS styles. The coordinates of the // path are interpreted relative to this size, so we give each // shape the same size as the canvas itself. wedge.style.position = "absolute"; wedge.style.width = canvas.style.width; wedge.style.height = canvas.style.height; // Add the wedge to the canvas canvas.appendChild(wedge); // Next wedge begins where this one ends startangle -= angles[i]; // Create a VML <rect> element for the legend var icon = document.createElement("v:rect"); icon.style.left = lx + "px"; // CSS positioning icon.style.top = (ly+i*30) + "px"; icon.style.width = "20px"; // CSS size icon.style.height = "20px"; icon.setAttribute("fillcolor", colors[i]); // Same color as wedge icon.setAttribute("stroke", "black"); // Outline color icon.setAttribute("strokeweight", "2"); // Outline width canvas.appendChild(icon); // Add to canvas // VML has advanced text capabilities, but most text is simple // HTML and is added directly to the VML canvas, using // canvas coordinates var label = document.createElement("div"); // <div> to hold the text label.appendChild(document.createTextNode(labels[i])); // the text label.style.position = "absolute"; // Position with CSS label.style.left = (lx + 30) + "px"; label.style.top = (ly + 30*i + 5) + "px"; label.style.fontFamily = "sans-serif"; // Text styles label.style.fontSize = "16px"; canvas.appendChild(label); // Add text to canvas } } function init( ) { var canvas = makeVMLCanvas("canvas", 600, 400); document.body.appendChild(canvas); pieChart(canvas, [12, 23, 34, 45], 200, 200, 150, ["red", "blue", "yellow", "green"], ["North", "South", "East", "West"], 400, 100); } </script> </head> <body onload="init( )"> </html> | |