I'm trying to do something I thought would be rather simple. I've an object that I move around stepwise, i.e. I receive messages every say 100 milliseconds that tell me "your object has moved x pixels to the right and y pixels down". The code below simulates that by moving that object on a circle, but note that it is not known in advance where the object will be heading in the next step.
Anyway, that is pretty simple. But now I want to also tell the object, which is actually a set of subobjects, that it is being rotated.
Unfortunately, I am having trouble getting Raphaël to do what I want. I believe the reason is that while I can animate both translation and rotation independently, I have to set the center of the rotation when it starts. Obviously the center of the rotation changes as the object is moving.
Here's the code I'm using and you can view a live demo here. As you can see, the square rotates as expected, but the arrow rotates incorrectly.
// c&p this into http://raphaeljs.com/playground.html
var WORLD_SIZE = 400,
rect = paper.rect(WORLD_SIZE / 2 - 20, 0, 40, 40, 5).attr({ fill: 'red' }),
pointer = paper.path("M 200 20 L 200 50"),
debug = paper.text(25, 10, ""),
obj = paper.set();
obj.push(rect, pointer);
var t = 0,
step = 0.05;
setInterval(function () {
var deg = Math.round(Raphael.deg(t));
t += step;
debug.attr({ text: deg + '°' });
var dx = ((WORLD_SIZE - 40) / 2) * (Math.sin(t - step) - Math.sin(t)),
dy = ((WORLD_SIZE - 40) / 2) * (Math.cos(t - step) - Math.cos(t));
obj.animate({
translation: dx + ' ' + dy,
rotation: -deg
}, 100);
}, 100);
Any help is appreciated!
If you want do a translation and a rotation too, the raphael obj should be like that
obj.animate({
transform: "t" + [dx , dy] + "r" + (-deg)
}, 100);
Check out http://raphaeljs.com/animation.html
Look at the second animation from the top on the right.
Hope this helps!
Here's the code:
(function () {
var path1 = "M170,90c0-20 40,20 40,0c0-20 -40,20 -40,0z",
path2 = "M270,90c0-20 40,20 40,0c0-20 -40,20 -40,0z";
var t = r.path(path1).attr(dashed);
r.path(path2).attr({fill: "none", stroke: "#666", "stroke-dasharray": "- ", rotation: 90});
var el = r.path(path1).attr({fill: "none", stroke: "#fff", "stroke-width": 2}),
elattrs = [{translation: "100 0", rotation: 90}, {translation: "-100 0", rotation: 0}],
now = 0;
r.arrow(240, 90).node.onclick = function () {
el.animate(elattrs[now++], 1000);
if (now == 2) {
now = 0;
}
}; })();
Related
I am using svg.js to create an animation of a bicyle rider. Semi-complete version here: https://pedalfuriously.neocities.org/. I'm running in to a bit of a problem with moving and rotating svg elements during animation created with requestAnimationFrame (rather than the svg.js built in animation).
If you take a look at the link, and use the cadence slider to make the rider pedal very fast, and then flip the slider quickly all the way back to zero, you can see that his lower leg "jiggles" in a disconnected way. What's really doing my head in is that the postion of the legs are determined in each frame based on an absolute relation to the rotation of the cranks (rather than taking some delta time value to determine movement over that frame).
I think I've been able to confirm what aspect of my code is causing the problem. Here is a minimal example that doesn't exhibit the exact behaviour, but I think illustrates the kind of thing I think is responsible:
var draw = SVG("drawing").viewbox(0, 0, 400, 400)
var origin = {
x: 70,
y: 70
}
var length = 60
var blueLine = draw.group()
blueLine.line(0, 0, 0 + length, 0).move(origin.x, origin.y)
.stroke({
color: "#00f",
width: 4
})
blueLine.angle = 0
var greenLine = draw.group()
greenLine.line(0, 0, 0 + length, 0).move(origin.x, origin.y)
.stroke({
color: "#0f0",
width: 4
})
greenLine.angle = 0
var previous = 0
var dt = 0
var step = function(timestamp) {
dt = timestamp - previous
previous = timestamp
blueLine.angle += 0.18 * dt
blueLine.rotate(blueLine.angle, origin.x, origin.y)
var endX = Math.cos(toRad(blueLine.angle)) * length
var endY = Math.sin(toRad(blueLine.angle)) * length
// Comment out this line, and rotation works fine
greenLine.move(endX, endY)
greenLine.angle = blueLine.angle - 10
// Comment out this line, and movement works fine
greenLine.rotate(greenLine.angle, origin.x, origin.y)
// But they don't work together. If I both move and rotate
// the green line, it goes in this crazy huge arc, rather
// than rotating neatly around the end of the blue line
// as expected.
window.requestAnimationFrame(step)
}
window.requestAnimationFrame(step)
function toRad(deg) {
return deg * (Math.PI / 180)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.4/svg.js"></script>
<div id="drawing"></div>
Something else I noticed with my actual code is that if I move the position of the legs, it changes the severity of the problem, or even stops it altogether. If the hips are positioned all the way near the front of the bicycle, the problem is not nearly as bad. Also, if I disable rotation on the lower legs, there is no jiggling. In some positions, the lower leg will just rotate out of the screen instantly on load, even before any motion has been started.
I'm hoping for some guidance on wether I'm misunderstanding the way manipulating elements works, either in svg.js in particular, or SVG in general.
Thank you kind vector graphics experts!
Here is the actual code for the legs. The step() function would probably be the most relevant. Not sure if it will be helpful:
Rider.Leg = function(foot, front, xOffset, yOffset) {
var upper = front ? SVGE.upperLeg : SVGE.upperLegBack
var lower = front ? SVGE.lowerLeg : SVGE.lowerLegBack
this.foot = foot
this.draw = foot.draw
this.geo = {
upper: {
x: this.foot.pedal.gear.x + 150,
y: this.foot.pedal.gear.y - 750,
length: 396
},
lower: {
length: 390
}
}
this.upper = this.draw.group().svg(upper).move(this.geo.upper.x, this.geo.upper.y)
.transform({ scale: 0.95, cx: 0, cy: 0 })
this.lower = this.draw.group().svg(lower).move(this.geo.upper.x, this.geo.upper.y)
}
// Step function does not take in a time argument. Positioning of legs is based only on
// the absolute position of other elements, none of which jiggle.
Rider.Leg.prototype.step = function () {
var angle = this.pedalAngle() - Math.PI
var ha = this.scaleneAngle(this.geo.lower.length, this.geo.upper.length, this.pedalDistance())
var ka = this.scaleneAngle(this.pedalDistance(), this.geo.lower.length, this.geo.upper.length)
var x = this.geo.upper.length * Math.cos(ha + angle)
var y = this.geo.upper.length * Math.sin(ha + angle)
this.upper.rotate(Drive.toDeg(angle + ha), 0, 0)
this.lower.move(this.geo.upper.x + x, + this.geo.upper.y + y)
this.lower.rotate(Drive.toDeg(angle + ha + ka - Math.PI), 0, 0)
}
// Gets the distance between the hip joint and the pedal
Rider.Leg.prototype.pedalDistance = function () {
var pos = this.foot.getPos()
var xDist = this.geo.upper.x - pos.x
var yDist = this.geo.upper.y - pos.y
return Math.hypot(xDist, yDist)
}
// Gets the angle between the hip joint and the pedal
Rider.Leg.prototype.pedalAngle = function () {
var pos = this.foot.getPos()
var xDist = this.geo.upper.x - pos.x
var yDist = this.geo.upper.y - pos.y
return Math.atan2(yDist, xDist)
}
Rider.Leg.prototype.scaleneAngle = function (a, b, c) {
return Math.acos(((b * b) + (c * c) - (a * a)) / (2 * b * c))
}
When you call move() on a group it is internally represented as a translation. svg.js figures out crazy ways to translate the object to the new place without changing any other transformations. That often does not work out. Especially not, when you rotate.
Thats why you should avoid these absolute transformations and go with relative ones. Just call untransform before every move and go from zero. Then you can do:
greenLine.transform({x:endX, y:endY, relative: true})
To move the line by a certain amount. That should work way better.
I recognize various forms of this question have been asked and generally answered in this forum. However I find I am still at a loss as to how to make this work.
The scenario:
I have an HTML5 Canvas where the 0,0 coordinate is top left.
I have an object (player) that can move around the canvas with using
wasd.
I want to be able to have the player shoot(cast, throw, whatever)
something from their current x,y coordinate in the direction of where
the mouse is clicked.
I want to animate an object(bullet, fireball, whatever) in a straight line between the origin (player location when mouse clicked) and destination (xy where mouse is clicked)
I can get all the origin and destination coords and I try to put them into a vector function to determine what the next x,y coordinate is to animate the projectile at but am failing at understanding how to do this.
One important note (possibly important): I do not want to use other libraries or jquery I just want to write it in javascript.
I am basically working from the Lost Decades simple HTML5 game example and trying to extend it
I create a projectile object like so:
spl = {
ox: 0,
oy: 0,
dx: 0,
dy: 0,
speed: 256,
vector: {
len: 0
}
};
on a mouseclick event listener I do this:
addEventListener("mousedown", function(e){
spl.ox = mvo.x;
spl.oy = mvo.y;
spl.dx = e.x;
spl.dy = e.y;
spl.vector = doVector(spl.ox, spl.oy, spl.dx, spl.dy);
}, false);
Where
spl.ox, oy is the position of the player at the click event
spl.dx, dy is the vector of the mouse click
my doVector function is me just trying to work out my linear algebra math like this (it doesn't seem to work out logically to me):
function doVector(ox, oy, dx, dy){
var diffX = (dx - ox);
var diffY = (dy - oy);
var length = Math.round(Math.sqrt(diffX*diffX+diffY*diffY));
var normX = Math.round(dx/length);
var normY = Math.round(dy/length);
var normProof = (normX*normX+normY*normY);
var dotProd = (ox*dx)+(oy*dy);
return{
len: length,
dist: dist,
normX: normX,
normY: normY,
normProof: normProof,
dotProd: dotProd
}
}
My update function (which I presume is where I should put my incrementing vector for the spl object) just handles the player movement with wasd at the moment:
//update objects --/////////////dddw
var update = function(modifier){
if(87 in keysDown){ //up
mvo.y -= Math.round(mvo.speed * modifier);
}
if(83 in keysDown){ //down
mvo.y += Math.round(mvo.speed * modifier);
}
if(65 in keysDown){ //left
mvo.x -= Math.round(mvo.speed * modifier);
}
if(68 in keysDown){ //right
mvo.x += Math.round(mvo.speed * modifier);
}
}// END update objects --/////////////
my render function is bloated as I am trying to figure out the math for the vector/velocity thing:
// render everything
var render = function (){
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.font = "10px verdana";
ctx.fillStyle = "#000088";
ctx.fillText("MVO", mvo.x, mvo.y);
ctx.fillText(mvo.x + ", " + mvo.y, mvo.x-9, mvo.y+11);
ctx.fillStyle = "#008800";
ctx.fillText("OXY", spl.ox, spl.oy);
ctx.fillText(spl.ox + "," + spl.oy, spl.ox-9, spl.oy+11);
ctx.fillStyle = "#880000";
ctx.fillText("DXY", spl.dx-18, spl.dy-18);
ctx.fillText(spl.dx + "," + spl.dy, spl.dx-29, spl.dy-7);
ctx.font = "12px verdana";
ctx.fillStyle = "#bbbbbb";
ctx.fillText("mvo x,y: " + mvo.x + ", " + mvo.y, 32, 32);
ctx.fillText("thing: ", 32, 44);
ctx.fillText("thing: ", 32, 56);
ctx.fillText("thing: ", 32, 68);
ctx.fillText("thing:" , 32, 80);
ctx.fillText("spl origin: " + spl.ox + "," + spl.oy, 525, 32);
ctx.fillText("spl destination: " + spl.dx + "," + spl.dy, 525, 44);
ctx.fillText("vector length: " + spl.vector.len, 525, 56);
ctx.fillText("spl normalized: " + spl.vector.normX + "," + spl.vector.normY, 525, 68);
ctx.fillText("spl norm proof: " + spl.vector.normProof, 525, 80);
ctx.fillText("spl norm dotProd: " + spl.vector.dotProd, 525, 92);
}
and finally my main loop looks like so
// Main loop
var main = function(){
var now = Date.now();
var delta = now - then;
update(delta/1000);
render();
then = now;
requestAnimationFrame(main);
};
Now, first, if you have read and followed all this, thank you very much for your brain cycles.
Secondly, all I want to do is determine how to recalculate the vector for an object moving between two coordinates and update the x,y position to draw at accordingly.
I may be entirely stupid becuase I read this great thing on linear algebra for games but seem unable to make it work. Any help would be really greatly appreciated.
Don't round your direction vector or your bullet direction will be off and it will miss its target.
And as enhzflep said use diffX and diffY insted of dx and dy for normalization or it will be completely wrong.
var length = Math.sqrt(diffX*diffX+diffY*diffY);
var normX = diffX/length;
var normY = diffY/length;
To actually move the bullet, you must update its position in your update function. You need to update bullets position by adding its direction multiplied with speed and time delta (modifier).
spl.ox += spl.vector.normX * spl.speed * modifier;
spl.oy += spl.vector.normY * spl.speed * modifier;
You can then draw it in your render function
ctx.beginPath();
ctx.arc(spl.ox, spl.oy, 5, 0, 2 * Math.PI, false);
ctx.fillStyle = "#000000";
ctx.fill();
ctx.fillText(spl.ox.toFixed(2) + "," + spl.oy.toFixed(2), spl.ox-40, spl.oy+6);
JSFiddle
Trying to create an interactive pie chart and raphaelJS seemed like the best solution. I have almost zero experience with it, (though I'm fairly proficient with jquery) so pardon if I come off as a complete idiot.
Question: I've created a functional interactive pie chart that displays text on hover. My problem is that I can't figure out how to style the text as I need to - adding and tags to the html doesn't have an effect
Here is the script - I'll try to just include the relevant sections:
var angle = -18,
total = 0,
start = 0,
process = function (j) {
var value = values[j],
angleplus = 360 * value / total,
popangle = angle + (angleplus / 2), // angle of text display
color = Raphael.hsb(start, .9, 1),
ms = 300,
delta = 30,
bcolor = Raphael.hsb(start, 1, 1),
p = sector(cx, cy, r, angle, angle + angleplus, {
fill: "90-" + bcolor + "-" + color,
stroke: "#F3F5F2",
"stroke-width": 4,
"stroke-opacity": 1,
opacity: .5}),
txt = paper.text(cx + (r + delta + 155) * Math.cos(0 * rad),
cy + (r + delta + 75) * Math.sin(-10 * rad),
labels[j]).attr({
fill: "#111",
stroke: "none",
opacity: 0,
"font-family": "Gotham",
"font-size": 14});
});
Here's the jsfiddle with the rest of the script and html (can't get it to run correctly though, works fine normally) - http://jsfiddle.net/9L286/
Currently, "txt" defines the style for text - I want to be able to style both the title ("item 1") and the description. Hopefully I'm just blind and there's a simple solution. If more info is needed, just ask. Thanks!
New to raphael.js, I'm looking for an explanation on how to do something like move planets around a sun in an orbit. I'm stuck trying to create a path and animate the movement of the circle around it.
Thanks for any pointers in the right direction!
My friend #Kevin Nielsen is right, you'll want "getPointAtLength." There's a nice little Raphael function here that adds an .animateAlong() function, though it needs a little modification to work on circular objects. I stripped it to the necessities for you.
Assuming you recognize post-1609 astronomy, you'll want elliptical orbits. (Though the difference in the short and long radii are quite small in reality, which is why Copernicus was a bit off the mark.) But you can't use the .ellipse() function, since you need the ellipse as a path in order to animate along it. See the SVG specifications for the elliptical arc, or just try a bunch of combinations until it looks right, like I did:
var paper = Raphael("canvas", 500, 500);
var center = {x: 200, y: 100 };
var a = 100;
var b = 80;
//see http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
var ellipse = "M" + (center.x - a) + "," + center.y + " a " + a + "," + b + " 0 1,1 0,0.1";
var orbit = paper.path(ellipse);
Now you want to draw the earth at one of the foci of the ellipse, and the moon along the path. We'll start it at the perigee.
var focus = Math.pow(a*a - b*b, 0.5);
var palebluedot = paper.circle(center.x - focus, center.y, 25)
.attr({
stroke: 0,
fill: "blue"
});
var moon = paper.circle(center.x - a, center.y, 10)
.attr({
stroke: 0,
fill: "#CCC"
});
And here's your modified "animateAlong" function:
//Adapted from https://github.com/brianblakely/raphael-animate-along/blob/master/raphael-animate-along.js
Raphael.el.animateAlong = function(path, duration, easing, callback) {
var element = this;
element.path = path;
element.pathLen = element.path.getTotalLength();
duration = (typeof duration === "undefined") ? 5000 : duration;
easing = (typeof easing === "undefined") ? "linear" : duration;
//create an "along" function to take a variable from zero to 1 and return coordinates. Note we're using cx and cy specifically for a circle
paper.customAttributes.along = function(v) {
var point = this.path.getPointAtLength(v * this.pathLen),
attrs = {
cx: point.x,
cy: point.y
};
this.rotateWith && (attrs.transform = 'r'+point.alpha);
return attrs;
};
element.attr({along: 0 }).animate({along: 1}, duration, easing, function() {
callback && callback.call(element);
});
};
and here it is:
moon.animateAlong(orbit, 2000);
jsFiddle
#Chris Wilson's answer is right on the money.
One slight modification I needed was to have the animation repeat indefinitely. #boom doesn't ask for it specifically, but as I can imagine this could be a common requirement for orbit animations, here's my modification to Chris's version of .animateAlong():
Raphael.el.animateAlong = function(path, duration, repetitions) {
var element = this;
element.path = path;
element.pathLen = element.path.getTotalLength();
duration = (typeof duration === "undefined") ? 5000 : duration;
repetitions = (typeof repetitions === "undefined") ? 1 : repetitions;
paper.customAttributes.along = function(v) {
var point = this.path.getPointAtLength(v * this.pathLen),
attrs = { cx: point.x, cy: point.y };
this.rotateWith && (attrs.transform = 'r'+point.alpha);
return attrs;
};
element.attr({along:0});
var anim = Raphael.animation({along: 1}, duration);
element.animate(anim.repeat(repetitions));
};
Note that I've dropped the easing and callback parameters (as I didn't need them) and added the repetitions parameter, which specifies the number of repetitions to perform.
An example call (starting an endlessly looping orbit animation) is:
moon.animateAlong(orbit, 2000, Infinity);
in an HTML page I'd like to layout a list of elements in a circular manner.
So I was wondering if there was a simple way of doing this with HTML5/CSS3;
and if a plugin of jQuery / jQuery UI or any other JavaScript library manages this kind of layout.
Thanks in advance for any help.
EDIT:
As of now I've used jQuery Radmenu : http://www.tikku.com/jquery-radmenu-plugin;
but its inner working is a bit clumsy.
I may end up with a custom solution inspired by dzejkej code sample.
Simple pure JavaScript example how to place HTML into a circular layout:
// retrieve the elements however you want (class name, tag name, ..)
var elems = document.getElementsByTagName('div');
var increase = Math.PI * 2 / elems.length;
var x = 0, y = 0, angle = 0;
for (var i = 0; i < elems.length; i++) {
elem = elems[i];
// modify to change the radius and position of a circle
x = 100 * Math.cos(angle) + 200;
y = 100 * Math.sin(angle) + 200;
elem.style.position = 'absolute';
elem.style.left = x + 'px';
elem.style.top = y + 'px';
angle += increase;
}
HERE is the working code.
YOu can use RaphaelJS, with jQuery or any other framework you enjoy.
This demo will help you: http://raphaeljs.com/hand.html
window.onload = function () {
var r = Raphael("holder", 640, 480), angle = 0;
while (angle < 360) {
var color = Raphael.getColor();
(function (t, c) {
r.circle(320, 450, 20).attr({stroke: c, fill: c, transform: t, "fill-opacity": .4}).click(function () {
s.animate({transform: t, stroke: c}, 2000, "bounce");
}).mouseover(function () {
this.animate({"fill-opacity": .75}, 500);
}).mouseout(function () {
this.animate({"fill-opacity": .4}, 500);
});
})("r" + angle + " 320 240", color);
angle += 30;
}
Raphael.getColor.reset();
var s = r.set();
s.push(r.path("M320,240c-50,100,50,110,0,190").attr({fill: "none", "stroke-width": 2}));
s.push(r.circle(320, 450, 20).attr({fill: "none", "stroke-width": 2}));
s.push(r.circle(320, 240, 5).attr({fill: "none", "stroke-width": 10}));
s.attr({stroke: Raphael.getColor()});
};