I have a rect rotated by 45degrees when I try and move it in a straight line using the normal move drag functionality it moves with the 45degree rotation. I've seen a lot of posts regarding this and that this is intended to work like this but I haven't found a simple way to fix this.
I know about the raphael.free_transform.js plugin but I don't need 90% of the script.
From other posts I know I'm supposed to use Math.atan2 but alas my Math skills aren't that great.
My current move function looks like this:
function (dx, dy) {
var att = this.type == "rect" ? {x: this.ox + dx, y: this.oy + dy} : {cx: this.ox + dx, cy: this.oy + dy};
this.attr(att);
for (var i = connections.length; i--;) {
r.connection(connections[i]);
}
r.safari();
}
You have to use "transform" method instead of simply changing x-y attrs.
var data = {};
var R = Raphael('raphael', 500, 500);
var rect = R.rect(100, 100, 100, 50).attr({fill: "#aa5555"}).transform('r45');
var default_transform = rect.transform();
var onmove = function (dx, dy) {
rect.transform(default_transform + 'T' + dx + ',' + dy);
};
var onstart = function () {};
var onend = function () {
default_transform = rect.transform();
};
rect.drag(onmove, onstart, onend);
I have created a live demo for you on JSfiddle: http://jsfiddle.net/pybBq/
Please note that you have to use big T letter in transform string (not small t) to make transformation absolute and not relative. Please read Raphael's docs on transform for more info: http://raphaeljs.com/reference.html#Element.transform
Related
I'm trying Snap to manipulate SVG but I'm having some issues when dragging a group of elements along a path.
I modified this code that I found here.
start = function () {
this.data("ox", +this.getBBox().cx);
this.data("oy", +this.getBBox().cy);
},
move = function (dx, dy) {
var tmpPt = {
x: this.data('ox') + dx,
y: this.data('oy') + dy
};
// move will be called with dx and dy
l = gradSearch(l, tmpPt);
pt = path.getPointAtLength(l);
if (!isNaN(pt.x) && !isNaN(pt.y)) {
this.transform('t' + (pt.x - this.data("ox")) + ',' + (pt.y - this.data("oy")));
};
},
end = function () {
console.log('End of drag');
}
I can drag the group, it stays where I drop it, but when I start to drag again, the group goes back to it's origin point and if I go crazy it starts to be really buggy.
I'm new to SVG, I tried some tutorials to learn but this stuff seams to be out of my league, I'd really appreciate some insights from you
Edit : It's better with my code, thanks for pointing that out Ian.
I think the problem is that you are resetting ox each time. In the previous examples, they used attr('cx') not ox, so they were always referencing the very original position of the shape to offset from. As you are resetting this each time, the difference will always be zero, so it resets to scratch. So you need a couple of modifications.
So lets all 'ox' the 'original position' before we do anything as our base.
And call 'sx' the 'starting position' that we figure out every drag start. We will need this to add 'dx' (the delta difference to add in a drag).
We can store the original position intitially...and then change the transform later to reference that original position, rather than the start position.
group.drag(move, start, end);
group.data("ox", +group.getBBox().cx);
group.data("oy", +group.getBBox().cy);
start = function () {
this.data("sx", +this.getBBox().cx);
this.data("sy", +this.getBBox().cy);
},
move = function (dx, dy) {
var tmpPt = {
x: this.data('sx') + dx,
y: this.data('sy') + dy
};
l = gradSearch(l, tmpPt);
pt = path.getPointAtLength(l);
// We change this!!!
if (!isNaN(pt.x) && !isNaN(pt.y)) {
this.transform('t' + (pt.x - this.data("ox")) + ',' + (pt.y - this.data("oy")) );
};
},
jsfiddle
I was trying to find the source code for this rafael.js example but on the page the link is broken.
Could someone give the simplest source code example that demonstrates how one can use the mouse to drag a circle using rafael.js ? Just like in the linked example ?
You can reference the source itself at http://raphaeljs.com/reference.js, at L133 you find the relevant example...
(function (r) {
var x, y;
r.circle(15, 15, 10).attr(fill).drag(function (dx, dy) {
this.attr({
cx: Math.min(Math.max(x + dx, 15), 85),
cy: Math.min(Math.max(y + dy, 15), 85)
});
}, function () {
x = this.attr("cx");
y = this.attr("cy");
});
})(prepare("Element.drag-extra"))
Removing dependencies and refactoring to make it—in my humble opinion—clearer, you get...
var paper = Raphael(10, 50, 320, 200);
var x, y;
paper.circle(15, 15, 10).attr("fill", "red").drag(
function (dx, dy) {
this.attr("cx", x + dx);
this.attr("cy", y + dy);
},
function () {
x = this.attr("cx");
y = this.attr("cy");
}
);
You can find the working example here: http://jsfiddle.net/ke1Ltbft/1/
I personally prefer to refactor the code a bit...
paper.circle.drag(start, move, end)
function start(x, y) {
// mouse/touch start code
}
function move(dx, dy) {
// mouse/touch move code
}
function end(x, y) {
// mouse/touch end code
}
I'm trying to hack arbor.js so I can perform on edges hover event and then display any information from it.
So let's say I can move over any edge and detect which edge's been hovered.
This is the drawing edges part of the rendering code:
particleSystem.eachEdge(function(edge, pt1, pt2){
// edge: {source:Node, target:Node, length:#, data:{}}
// pt1: {x:#, y:#} source position in screen coords
// pt2: {x:#, y:#} target position in screen coords
// find the start point
var tail = intersect_line_box(pt1, pt2, nodeBoxes[edge.source.name])
var head = intersect_line_box(tail, pt2, nodeBoxes[edge.target.name])
ctx.save()
ctx.beginPath()
ctx.lineWidth = (!isNaN(weight)) ? parseFloat(weight) : 1
ctx.strokeStyle = (color) ? color : "#cccccc"
ctx.fillStyle = null
//We save edge's line data, "Diego"
var slope = ((head.y)-(tail.y))/((head.x)-(tail.x))
a.push({x1:tail.x, y1:tail.y, x2:head.x, y2:head.y, m:slope})
ctx.moveTo(tail.x, tail.y)
ctx.lineTo(head.x, head.y)
ctx.stroke()
// Why pushing here makes x2 and y2 undefined?, "Diego"
ctx.restore()
})
This is the refered function:
var intersect_line_box = function(p1, p2, boxTuple)
{
var p3 = {x:boxTuple[0], y:boxTuple[1]},
w = boxTuple[2],
h = boxTuple[3]
var tl = {x: p3.x, y: p3.y};
var tr = {x: p3.x + w, y: p3.y};
var bl = {x: p3.x, y: p3.y + h};
var br = {x: p3.x + w, y: p3.y + h};
return intersect_line_line(p1, p2, tl, tr) ||
intersect_line_line(p1, p2, tr, br) ||
intersect_line_line(p1, p2, br, bl) ||
intersect_line_line(p1, p2, bl, tl) ||
false
}
I thought about two approaches:
First approach
First one is adding a 'hover' anonymous function inside var handler = {...}. So when $(canvas).mousemove(handler.hover) all would be managed by hover's function. I thought capturing the mouse's position and comparing it with the previous edges line coordinates.
I have a variable like:
var a = []
And then, before drawing again I clear out saved edges:
a=[]
And save the to be drawed edges:
var slope = ((head.y)-(tail.y))/((head.x)-(tail.x))
a.push({x1:tail.x, y1:tail.y, x2:head.x, y2:head.y, m:slope})
So, hover's event function is:
hover: function(e) {
var pos = $(canvas).offset();
var x = e.pageX-pos.left
var y = e.pageY-pos.top
_mouseP = arbor.Point(e.pageX-pos.left, e.pageY-pos.top)
for (var i = 0; i<a.length; i++) {
if ( (_mouseP.x-a[i].x1)) == (a[i].m* (_mouseP.y-a[i].y1)) )
console.log("This is an edge)
},
Second approach
What about going for jQuery? So something like:
$("anyDrawedOnCanvasStuff").hover(function(){}
would trigger events automatically. However I don't think this as possible, is it?
I quite completed first approach, but something weird is happening with the saved points in a array, they're all double and obtained mouse points happened to be integer. And also, they don't even get close to complete the formula's logic.
I'm trying to do a simple animation in Raphael.js in which a paper.text object is moved from its current position to another position. Here is some of my code (there is far too much to post all of it):
songPos = getSongPosition(this, charIndex);
letter.path.animate({x: songPos.x, y: songPos.y, "font-size": this.correctFontSize}, 500, onAnimationComplete);
Here is the Letter object being referenced in the above code:
function Letter(args)
{
this.letter = args.letter || "A";
this.correct = args.correct || false;
this.transformation = args.transformation || "";
this.initX = args.x || 0;
this.initY = args.y || 0;
this.path = null;
}
Letter.prototype.buildPath = function()
{
this.path = paper.text(this.initX, this.initY, this.letter);
if(this.transformation)
{
this.path.transform(this.transformation);
}
return this;
};
The problem is I'm printing the x and y values returned by getSongPosition, and they're correct, but the animate method is sending my text object somewhere off-screen. I've also tried just setting the animation attributes to {x: 0, y: 0}, but I still get the same results. I can post more of the code if it is necessary.
Any help would be greatly appreciated.
UPDATE 1:
Part of my program requires I be able move objects to specific coordinates. Some of the objects will be rotated and others will not, so I wrote this:
Letter.prototype.getMoveTransform = function(x, y)
{
var bbox = this.path.getBBox(true);
var dx = x - bbox.x;
var dy = y - bbox.y;
return "T" + dx + "," + dy + (this.transformation == null ? "" : this.transformation);
};
I believe this is the root of my problem. This function is supposed to generate the transformation required to move a rotated object to (x, y). I'm not sure why I have to re-rotate the object on every animation.
UPDATE 2:
I have somehow solved my problem. I would post my solution, but I don't understand why any of this works/didn't work in the first place anymore.
this.path.getBBox(true); should be this.path.getBBox() or this.path.getBBox(false);
you need get transformed position every time to calculate the dx and dy
In the following fiddle, you can click and drag around the image, and it will not be able to exit the blue border. By clicking the red and green rectangles, you can rotate the image. However when you click and drag a rotated object, the image does not follow the mouse. I would like the image to follow the mouse even if it is rotated.
http://jsfiddle.net/n3Sn5/
I think the issue occurs within my move function
move = function (dx, dy)
{
nowX = Math.min(boundary.attr("x")+boundary.attr("width")-this.attr("width"), this.ox + dx);
nowY = Math.min(boundary.attr("y")+boundary.attr("height")-this.attr("height"), this.oy + dy);
nowX = Math.max(boundary.attr("x"), nowX);
nowY = Math.max(boundary.attr("y"), nowY);
this.attr({x: nowX, y: nowY });
}
One thing to notice is that when you click and drag a rotated object, after you release your mouse click, if you rotate the image, it snaps to where your mouse was when you released the mouse click, even obeying the boundary.
I was able to get the rotated image to drag with the mouse previously, but by adding the boundary rectangle, i had to use a more complex approach.
If anyone has an idea of what I need to change, I would be very grateful!
Thanks!
The required output can be achieved in a bit different way. Please check the fiddle at http://jsfiddle.net/6BbRL/. I have trimmed to code to keep the basic parts for demo.
var paper = Raphael(0, 0, 475, 475),
boxX = 100,
boxY = 100,
boxWidth = 300,
boxHeight = 200,
// EDITED
imgWidth = 50,
imgHeight = 50,
box = paper.rect(boxX, boxY, boxWidth, boxHeight).attr({fill:"#ffffff"}),
// EDITED
html5 = paper.image("http://www.w3.org/html/logo/downloads/HTML5_Badge_512.png",boxX+boxWidth-imgWidth,boxY+boxHeight-imgHeight,imgWidth,imgHeight)
.attr({cursor: "move"}),
elementCounterClockwise = paper.rect(180, 0, 50, 50).attr({fill:"#ff5555", cursor:"pointer"}),
elementClockwise = paper.rect(250, 0, 50, 50).attr({ fill: "#55ff55", cursor: "pointer" }),
boundary = paper.rect(50,50,400,300).attr({stroke: '#3333FF'}),
transform,
// EDITED
xBound = {min: 50 + imgWidth/2, max: 450 - imgWidth/2},
yBound = {min: 50 + imgHeight/2, max: 350 - imgHeight/2};
start = function (x, y) {
// Find min and max values of dx and dy for "html5" element and store them for validating dx and dy in move()
// This is required to impose a rectagular bound on drag movement of "html5" element.
transform = html5.transform();
}
move = function (dx, dy, x, y) {
// To restrict movement of the dragged element, Validate dx and dy before applying below.
// Here, dx and dy are shifts along x and y axes, with respect to drag start position.
// EDITED
var deltaX = x > xBound.max && xBound.max - x || x < xBound.min && xBound.min - x || 0;
deltaY = y > yBound.max && yBound.max - y || y < yBound.min && yBound.min - y || 0;
this.attr({transform: transform + 'T'+ [dx + deltaX, dy + deltaY]});
}
up = function () {
};
html5.drag(move, start, up);
elementClockwise.click(function() {
html5.animate({transform: '...r90'}, 100);
})
elementCounterClockwise.click(function() {
html5.animate({transform: '...r-90'}, 100);
})
Use of '...' to append a transformation to the pre-existing transformation state (Raphael API) is important for the rotational issue. While, for translating the element on drag requires absolute translation, which neglects the rotational state of the element while translating the element.
//EDIT NOTE
Drag bounding is worked on and updated. However, there remains an issue with incorporating the difference between mouse position and image center.
I can help you with your rotation and drag problem, you need to store the rotation and apply it after you have moved the object.
elementClockwise.node.onclick = function()
{
html5.animate({'transform': html5.transform() +'r90'}, 100, onAnimComplete);
}
elementCounterClockwise.node.onclick = function()
{
html5.animate({'transform': html5.transform() +'r-90'}, 100, onAnimComplete);
}
function onAnimComplete(){
default_transform = html5.transform();
}
At present I can't get the boundary to work, but will have a try later.
http://jsfiddle.net/n3Sn5/2/