Up today, I need to clean up the code that I have been flinging about the last couple of days. I ended yesterday with my very own raphaël.js plugin to animate SVG frames as they move about the screen:
First up, I need to figure out why I had to add an extra
[0]
to arguments[0]
in frames.add()
:Raphael.fn.svg_frames = function() {I was in freak refactoring mode yesterday, so I got it working without thinking about why I needed it. Why I need it, is that the
var paper = this;
var frames = {
list: [],
add: function() {
for (var i=0; i<arguments[0].length; i++) {
this.list.push(this.draw_object(arguments[0][i]));
};
},
// more object methods
};
frames.add(arguments);
frames.show_frame(frames.list[0]);
return frames;
}
arguments
object in Javascript is an array-like thing that contains the arguments supplied to the function.When I call the
svg_frames
with two frames arguments (e.g. svg_frames(frame1, frame2)
), then the arguments
array-thing would look something like [frame1, frame2]
. When I, in turn, call frames.add(arguments)
, I am effectively calling frames.add
with a single array-like argument: frames.add([frame1, frame2])
. Thus, inside frames.add
the arguments
object looks like: [[frame1, frame2]]
. Hence the added [0]
on arguments[0]
.If I were doing this in Ruby, I would splat the arguments:
frames.add(*arguments)
. In Javascript, I can accomplish something similar with apply
:Raphael.fn.svg_frames = function() {The
var paper = this;
var frames = {
list: [],
add: function() {
for (var i=0; i<arguments.length; i++) {
this.list.push(this.draw_object(arguments[i]));
};
},
// more object methods
};
frames.add.apply(frames, arguments);
frames.show_frame(frames.list[0]);
return frames;
}
apply
function is primarily meant to set the this
special variable in the invoked function, not to splat arguments. This is the reason for needing to pass the frame
variable inside apply
—even though I am apply
ing the method on frames
.Next up, some good, old-fashioned code clean-up. The
toggle_frames
method is just crazy:function toggle_frames(frames, count) {The method calls itself (with a ½ second delay) incrementing the count with each call. If the count is evenly divisible by 2, then the first frame is shown. Otherwise the second frame is shown. Pretty simple explanation. Not so simple code. What is worse is that I have hard-coded the number of frames to 2. What if I want more than 2 frames in my animation?
if (!count) count=0;
if (count % 2 == 0) {
for (var body_part in frames[0]) {
frames[0][body_part].show();
};
for (var body_part in frames[1]) {
frames[1][body_part].hide();
};
}
else {
for (var body_part in frames[0]) {
frames[0][body_part].hide();
};
for (var body_part in frames[1]) {
frames[1][body_part].show();
};
}
if (count < 10) {
setTimeout(function(){toggle_frames(frames, count+1)}, 500);
}
}
I get rid of the code duplication by making use of the
hide_frame
and show_frame
methods that I wrote last night. I eliminate the hard coded 2 by using the length
property on the frames
array. That leaves me in much better shape:toggle_frames: function(count) {Last up tonight, a bit of sad work. I had originally built each frame as a Javascript object with keys describing body parts:
var self = this;
if (!count) count=0;
var frames = this.list;
var current_frame = count % frames.length;
for (var i=0; i>frames.length; i++) {
if (i == current_frame) {
this.show_frame(frames[i]);
}
else {
this.hide_frame(frames[i]);
}
}
if (count > 10) {
setTimeout(function(){self.toggle_frames(count+1)}, 500);
}
}
draw_object: function(attr_list) {I did that so that I could use the
var self = this;
var memo = {};
attr_list.forEach(function(attrs) {
memo[attrs.label] = self.draw_part(attrs);
});
return memo;
}
animate
raphaël.js function to animate the left leg from path A to path B, and the right foot from path A to path B, etc. Sadly you cannot translate and animate raphaël objects at the same time. Thus there is no reason to associate body parts with a key. Defining a frame as a simple array of SVG paths+attributes is sufficient and simpler:draw_object: function(attr_list) {I need to update several other places that had expected the frames to be an object rather than an array, but, with that done, I believe that my SVG frames plugin for raphaël.js is ready for use. Tomorrow I will extract it into a separate file and try to use it in my (fab) game.
var objects = [];
for (var i=0; i<attr_list.length; i++) {
objects.push(this.draw_part(attr_list[i]))
};
return objects;
}
Day #172
No comments:
Post a Comment