by Martin Grimme (gDesklets SVG logo is a courtesy of Johannes Rebhan)
The <canvas> element provides a canvas for drawing scalable vector graphics onto. These drawings can even be animated.
This tutorial shows you how you can make full use of the <canvas> element and SVG scripting.
SVG stands for Scalable Vector Graphics. It is a standardized graphics format based on XML. gDesklets can read SVG and animate images by modifying properties in the XML tree of the SVG.
Good tools for creating SVG drawings on Unix systems are Sodipodi and its amazing fork Inkscape. Of course, since SVG is clean XML, you can also create drawings with a simple text editor.
The easiest to way to render drawings is loading them from file. The <canvas> can read SVG drawings, so you can make an image with your favorite SVG drawing tool and load it into your applet.
The SVG file can be loaded into the canvas with the uri property. Use the width and height properties to scale the image.
<?xml version="1.0" encoding="UTF-8"?> <display> <canvas id="mycanvas" uri="gdesklets.svg" width="200" height="200"/> </display>
A SVG image is composed of elements. These elements in turn could even be decomposed into more elements, perhaps. Here you can see a halfway decomposed gDesklets logo:
Every piece or composition of pieces can have an ID by which it can be identified and addressed. By manipulating the properties of such an element, scripts can animate the image.
The composition tree of SVG elements is represented by a Document Object Model, a DOM. The DOM is a tree of nodes where each node represents a node in the SVG tree of elements. Element properties can be directly manipulated on the DOM.
The <canvas> display element provides a mini-DOM for this purpose through its dom property.
You can use your SVG editor for finding the ID of the elements which you want to manipulate. If you have the ID, you can use the get() method to retrieve the corresponding node and directly modify its properties.
After having finished manipulating the DOM, you can have the image redraw itself by calling the update() method on the DOM.
<script> dom = Dsp.mycanvas.dom node = dom.get("rect588") node["style"] = "fill:yellow" dom.update() </script>
Of course, we can use this in action handlers, too (remember to call the update() method):
<?xml version="1.0" encoding="UTF-8"?> <display> <canvas uri="gdesklets.svg" width="200" height="200" on-enter="self.dom.get('rect588')['style'] = 'fill:yellow'; self.dom.update()" on-leave="self.dom.get('rect588')['style'] = 'fill:blue'; self.dom.update()"/> </display>
Instead of loading SVG files, you can also feed the canvas directly with a string of SVG data. The graphics property accepts SVG code.
SVG usually requires the width and height properties specified in the <svg> root tag. gDesklets, however, automatically sets these to 100 x 100 if you omit them.
<?xml version="1.0" encoding="UTF-8"?> <display window-flags="above"> <canvas id="mycanvas" width="200" height="200"/> <script><![CDATA[ svg = """ <svg> <rect x="0" y="0" width="100" height="100" style="fill:white; stroke:black; fill-opacity:50%"/> <circle cx="50" cy="50" r="20" style="stroke:black; fill:yellow"/> </svg> """ Dsp.mycanvas.graphics = svg ]]></script> </display>
Of course, the DOM is also available for images set by the graphics property. For the end of this tutorial, we are going to let the ball on the image bounce.
First of all, the ball needs an ID, so that we can easily access it through the DOM:
<circle id="ball" cx="50" cy="50" r="20" style="stroke:black; fill:yellow"/>
The animation can be done in a timer. We simply change the x and y properties regularly to make it move. Special treatment is needed for edges since the ball has to bounce back there. This can be achieved by just inverting the current movement direction.
<?xml version="1.0" encoding="UTF-8"?> <display window-flags="above"> <canvas id="mycanvas" width="200" height="200"/> <script><![CDATA[ svg = """ <svg> <rect x="0" y="0" width="100" height="100" style="fill:white; stroke:black; fill-opacity:50%"/> <circle id="ball" cx="50" cy="50" r="20" style="stroke:black; fill:yellow"/> </svg> """ Dsp.mycanvas.graphics = svg dx = 3 dy = 2 def bounce(): global dx, dy ball = Dsp.mycanvas.dom.get("ball") x = int(ball["cx"]) y = int(ball["cy"]) # bounce back at the edges if (x <= 20 or x >= 80): dx = -dx if (y <= 20 or y >= 80): dy = -dy # move the ball x += dx y += dy ball["cx"] = str(x) ball["cy"] = str(y) # redraw image Dsp.mycanvas.dom.update() # keep the animation running return True # animate every 100 milliseconds add_timer(100, bounce) ]]></script> </display>
Please note that all properties of SVG elements are strings and have to be strings. That's why we need the conversions in the timer.