We're going to make the smiley move using javascript. The basic idea is that we
want to move the smiley just a tiny bit, but do it really quickly, lots of
times so that the smiley looks like it's moving smoothly. First of all though,
we need to put a <script> in the file so that we have
somewhere to write our javascript. So, add these lines to the file, after the
<svg> element but before the <rect>
element:
<!-- Script to do the animation -->
<script
type="text/javascript"><![CDATA[
]]></script>
Pretty much all of our javascript will go in between those lines of code.
Next, we'll need some variables to keep track of where smiley is.
Variables are like letters in algebra. They represent something like a number,
but you don't really care what the exact value is. We'll need one to keep track
of how far across our smiley is (we'll call it x), and we'll need
one to keep track of how far down our smiley is (we'll call it y).
We'll also need variables to keep trak of how fast the smiley is going across
(vx) and how fast it's going down (vy).
To get variables in javascript, we type var, then we type the
name of the variable. Oh, and by the way, nearly everything in javascript has
to have a semi-colon ";" at the end of it. It's kind of like a full-stop to say
"this bit's done, let's go on to the next bit."
So the code for our variables will look something like this:
var x; var y; var vx; var vy;
Next, we'll need a variable to tell the computer how often to re-draw the
smiley's position. Animations on TV usually do this about 30 times a second, so
we want to refresh every 0.033, seconds (that's 1/30). The computer does things
in milliseconds though, so we need to multiply that by 1000. So we want the
computer to refresh the smiley every 33 miliseconds. We'll add another variable
called interval to represent this.
var interval = 33;
We also need the script to know how high and how wide the picture is, and
what the diameter of the ball is, so
we'll add another couple of variables for those. Javascript lets us put in
values for them at the same time we create them by adding an =
sign.
var docHeight = 400; var docWidth = 400; var ballDiam = 60;
Finally, we want a variable to represent the SVG document we're editing, since we make the smiley move by changing the SVG. We also want a variable that will represent the smiley face group that we created in the SVG document.
var svgdoc; var smileyFace;
OK. Now we want to set up the smiley so that it does something when we click
it. To do this, we go back to the group (<g>) element and add an
attribute called onclick. Add an onclick attribute
so that the opening <g> tag looks like the following:
<g id="smileyFace" transform="translate(3,3)"
onclick="alert('Greetings Earthling');">
If you load your picture in a viewer now, and click on the smiley you should get an alert box saying "Greetings Earthling".
Obviously, we don't want it to throw up an alert box all the time, we want
it to move. To do this, we have to write a function to tell the viewer
to start the animation. Back in our script block, put in the
following:
var intervalTimer = -1; // This function starts the animation function startAnim() { var svgdoc = evt.getCurrentNode().getOwnerDocument(); smileyFace = svgdoc.getElementById("smileyFace"); if (intervalTimer == -1) { intervalTimer = setInterval("nextFrame()", interval); } y = 0; vy = 0; x = 0; vx = 0.5; }
Once again, let's try and break this up a bit…
| var intervalTimer = -1; | This is yet another variable to keep track of the timer that tells the screen to change every 33 milliseconds. |
| // This function starts the animation |
This is a comment to tell anyone reading the code what the function does.
There are two ways of doing comments in javascript. You can either put two
forward slashes // at the beggining of a line
like here, or, you can put your comments between a slash and a star:
/* … */
|
| function startAnim() { | This line says that we want to create a function called "startAnim". |
| svgdoc = evt.getCurrentNode().getOwnerDocument(); | This makes our svgdoc variable actually represent the SVG that we wrote. Don't worry too much about how it does this at the moment. |
| smileyFace = svgdoc.getElementById("smileyFace"); | This is the variable to represent the smiley face. What we've essentially done is tell the viewer "go through the SVG we wrote until you find the element with an id of 'smileyFace', then make this variable represent that." |
|
if (intervalTimer == -1) { intervalTimer = setInterval("nextFrame()", interval); } |
This piece of code says: "if intervalTimer still equals -1,
then set up a timer that goes off every interval milliseconds
and runs the function called nextFrame. Then make the
variable intervalTimer represent this timer instead of being
equal to -1." Now we need to write a nextFrame function to do
the actual moving of the smiley face.
|
|
y = 0; vy = 0; x = 0; vx = 0.5; |
These set the position and speed of the smiley when it starts moving.
y = 0 puts the smiley at the top of the screen. The
vy = 0 says that when we start the smiley doesn't have any
speed downward, as if we had just lifted it up there and let it go. For the
very tiniest fraction of a second, it has no speed. The x = 0
puts the smiley at the left-hand side of the screen. The vx = 0
says that to start with the ball is moving to the right at a speed of 1.5 (
the "v" in "vx" stands for velocity, which means 'speed in just one
direction').
|
| } | This says that the function finishes here. |
Next, we need to write the function to move the smiley. Put this in the code
after the startAnim function but before the
]]></script> bit.
// This function moves the smiley for each frame function nextFrame() { y = y + 1; x = x + 1; var transform = "translate(" + x + "," + y + ")"; smileyFace.setAttribute("transform", transform); }
We also need to change the onclick event so that it calls our
startAnim function instead of bringing up the alert box. Change
the <g> tag to look like the following:
<g id="smileyFace" transform="translate(3,3)"
onclick="startAnim();">
Load this up in the viewer and click on the smiley to see what it does. The smiley should move diagonally down and to the right. This is because we've told it to move one pixel down and one pixel across every 33 milliseconds. You'll notice that it completely ignores the edges of the picture and just keeps on going until you can't see it– it certainly doesn't bounce off them. It doesn't look like it's falling either because it's just moving at a constant speed. So, we need to make it look like it's falling and tell it about the walls so it will bounce.
First of all, falling. Change the nextFrame function to look
like this:
var g = 9.8 / 2800; // This function moves the smiley for each frame function nextFrame() { vy = vy + g*interval; var y_next = y + vy*interval; y = y_next; x = 0; var transform = "translate(" + x + "," + y + ")"; smileyFace.setAttribute("transform", transform); }
Load the picture up in the viewer again and click the smiley to see what it does. It should look like it's falling now, but it will very quickly fall off the bottom of the screen. Let's have a look at the changes that made this happen.
| var g = 9.8 / 2800; |
g stands for gravity. Everything on earth falls at the same
rate of 9.8 meters per second squared (m.s-2). We divide this by
2800 to get a rough conversion from metres to pixels.
|
| vy = vy + g*interval; |
This is where we tell the smiley to get faster and faster as it falls, just
like a ball would if you dropped it from a height. The speed it's falling in
this frame is the speed it was falling in the last frame, plus g
times the amount of time that passed since the last frame.
|
|
var y_next =
y +
vy*interval; y = y_next; |
These lines work out what the next y-position of the smiley should be if it's
moving at a speed of vy.
|
OK. Now let's make the smiley bounce. To do this we'll have to change our
nextFrame function again.
var g = 9.8 / 2800; var wallDamp = 0.95; // This function moves the smiley for each frame function nextFrame() { vy = vy + g*interval; y_next = y + vy*interval; if (y_next < (docHeight - ballDiam)) { y = y_next; } else { vy = -wallDamp*vy; y = (docHeight - ballDiam); } var transform = "translate(" + x + "," + y + ")"; smileyFace.setAttribute("transform", transform); }
If you have a look at the picture now and click on the smiley, then it should fall and bounce when it hits the bottom of the picture. We'll take a look at the changes that made it do this.
| var wallDamp = 0.95; |
When a ball bounces it doesn't bounce to the height that you dropped it from,
it gets slightly lower with each bounce. This is because the ball loses some
of its energy each time it bounces. We use this variable to set how much
energy the ball gets to keep, each time it bounces. With the
wallDamp set at 0.95, it gets to keep 95% of its energy and
loses 5% each time it bounces. Try setting it to different values to see
what happens.
|
|
if (y_next <
(docHeight -
ballDiam)) { y = y_next; } else { vy = -wallDamp*vy; y = (docHeight - ballDiam); } |
This is where we make the ball bounce. It says, if the smiley is
not about to fall off the bottom of the picture, then just let it
keep falling. Otherwise reverse the direction the ball is
travelling, reduce it's speed by the amount wallDamp, and make
it's position look like it's just hit the bottom.
|
OK. So our ball bounces, but it still looks pretty boring. It doesn't move
from side to side at all. To make this happen we have to change the
vx and x variables. We'll change our
nextFrame() function again to look like this:
var g = 9.8 / 2800; var wallDamp = 0.95; // This function moves the smiley for each frame function nextFrame() { // Work out the y-position vy = vy + g*interval; y_next = y + vy*interval; if (y_next < (docHeight - ballDiam)) { y = y_next; } else { vy = -wallDamp*vy; y = (docHeight - ballDiam); } // Work out the x-position vx = 0.98*vx; var x_next = x + vx*interval; if (x_next > docWidth - ballDiam) { vx = -wallDamp * vx; x = docWidth - ballDiam; } else if (x_next < 0){ vx = -wallDamp * vx; x = 0; } else { x = x_next; } var transform = "translate(" + x + "," + y + ")"; smileyFace.setAttribute("transform", transform); }
Let's have a look at what we added:
| // Work out the x-position | This is a comment to explain what the code is doing. |
| vx = 0.98*vx; | This works out how fast the ball is travelling in the x-direction, i.e. horizontally. |
| var x_next = x + vx * interval; | Work out how far the ball will be from the left-hand side of the picture. |
| if (x_next > docWidth - ballDiam) { | If the ball is about to hit the right-hand wall, then … |
|
vx =
-wallDamp * vx;
x = docWidth - ballDiam; |
Reverse the direction the smiley is travelling and reduce its speed by
wallDamp. Make sure the smiley doesn't go outside the borders by
setting its x-position so that it just touches the right edge.
|
| } else if (x_next < 0){ | Otherwise, if the smiley is about to hit the left wall, then … |
|
vx =
-wallDamp * vx;
x = 0; |
Reverse the direction the smiley is travelling and reduce its speed by
wallDamp. Make sure the smiley doesn't go outside the borders by
setting its x-position so that it just touches the left edge.
|
|
} else { x = x_next; } |
Otherwise (i.e. if the smiley isn't about to hit either wall), just let the smiley keep going. |
And you're finished. If everything has gone to plan, your
<script> tag should look something like this:
<!-- Script to do the animation --> <script type="text/javascript"><![CDATA[ var x; var y; var vy; var vx; var interval = 33; var docHeight = 400; var docWidth = 400; var ballDiam = 60; var intervalTimer = -1; var svgdoc, smileyFace; // This function starts the animation function startAnim() { svgdoc = evt.getCurrentNode().getOwnerDocument(); smileyFace = svgdoc.getElementById("smileyFace"); if (intervalTimer == -1) { intervalTimer = setInterval("nextFrame()", interval); } y = 0; vy=0; x=0; vx=0.5; } var g=9.8/2800; var wallDamp = 0.95; // This function moves the smiley for each frame function nextFrame() { // Work out the y-position vy = vy + g*interval; var y_next = y + vy*interval; if (y_next < (docHeight - ballDiam)) { y = y_next; } else { vy = -wallDamp*vy; y = (docHeight - ballDiam); } // Work out the x-position vx = 0.98*vx; var x_next = x + vx*interval; if (x_next > docWidth-ballDiam) { vx = -wallDamp*vx; x = docWidth-ballDiam; } else if (x_next < 0){ vx = -wallDamp*vx; x = 0; } else { x = x_next; } var transform = "translate(" + x + "," + y + ")"; smileyFace.setAttribute("transform", transform); ]]></script>
If you want to experiment, try changing the values for vx,
vy or wallDamp. To start with, you might want to keep
wallDamp between 0 and 1, but feel free to try other values once
you've got a feel for it.
THE END.