Raphael transform object diagonally and infinite setIntervals - javascript

I'm working on a small animation where the user drags a circle and the circle returns back to the starting point. I figured out a way to have the circle return to the starting point. The only problem is that it will hit one of the sides of the frame before returning. Is it possible for it to go straight back (follow the path of a line drawn between the shape and starting point).
The other problem is that my setInterval doesn't want to stop. If you try pulling it a second time it would pull it back before you release your mouse. It also seems to speed up after every time. I have tried using a while loop with a timer but the results weren't as good. Is this fixable?
var paper = Raphael(0, 0, 320, 200);
//var path = paper.path("M10 10L40 40").attr({stoke:'#000000'});
//var pathArray = path.attr("path");
var circle = paper.circle(50, 50, 20);
var newX;
var newY;
circle.attr("fill", "#f00");
circle.attr("stroke", "#fff");
var start = function () {
this.attr({cx: 50, cy: 50});
this.cx = this.attr("cx"),
this.cy = this.attr("cy");
},
move = function (dx, dy) {
var X = this.cx + dx,
Y = this.cy + dy;
this.attr({cx: X, cy: Y});
},
up = function () {
setInterval(function () {
if(circle.attr('cx') > 50){
circle.attr({cx : (circle.attr('cx') - 1)});
} else if (circle.attr('cx') < 50){
circle.attr({cx : (circle.attr('cx') + 1)});
}
if(circle.attr('cy') > 50){
circle.attr({cy : (circle.attr('cy') - 1)});
} else if (circle.attr('cy') < 50){
circle.attr({cy : (circle.attr('cy') + 1)});
}
path.attr({path: pathArray});
},2);
};
circle.drag(move, start, up);
Here's the Jfiddle: http://jsfiddle.net/Uznp2/
Thanks alot :D

I modified the "up" function to the one below
up = function () {
//starting x, y of circle to go back to
var interval = 1000;
var startingPointX = 50;
var startingPointY = 50;
var centerX = this.getBBox().x + (this.attr("r")/2);
var centerY = this.getBBox().y + (this.attr("r")/2);
var transX = (centerX - startingPointX) * -1;
var transY = (centerY - startingPointY) * -1;
this.animate({transform: "...T"+transX+", "+transY}, interval);
};
and the "start" function as follows:
var start = function () {
this.cx = this.attr("cx"),
this.cy = this.attr("cy");
}
Is this the behavior you are looking for? Sorry if I misunderstood the question.

If the circle need to get back to its initial position post drag, we can achieve that via simple animation using transform attribute.
// Assuming that (50,50) is the location the circle prior to drag-move (as seen in the code provided)
// The animation is set to execute in 1000 milliseconds, using the easing function of 'easeIn'.
up = function () {
circle.animate({transform: 'T50,50'}, 1000, 'easeIn');
};
Hope this helps.

Related

trying to make a drawn line move like a laser in javascript

I made this red line in JavaScript that goes to closest target (balloon 1 to 3) to the player but I need to make it so that it moves like a laser starting from player position into the target position. I thought about multiple ways of implementing this with no luck.
function Tick() {
// Erase the sprite from its current location.
eraseSprite();
for (var i = 0; i < lasers.length; i++) {
lasers[i].x += lasers[i].direction.x * laserSpeed;
lasers[i].y += lasers[i].direction.y * laserSpeed;
//Hit detection here
}
function detectCharClosest() {
var ballon1char = {
x: balloon1X,
y: balloon1Y
};
var ballon2char = {
x: balloon2X,
y: balloon2Y
};
var ballon3char = {
x: balloon3X,
y: balloon3Y,
};
ballon1char.distanceFromPlayer = Math.sqrt((CharX - balloon1X) ** 2 + (CharY - balloon1Y) ** 2);
ballon2char.distanceFromPlayer = Math.sqrt((CharX - balloon2X) ** 2 + (CharY - balloon2Y) ** 2);
ballon3char.distanceFromPlayer = Math.sqrt((CharX - balloon3X) ** 2 + (CharY - balloon3Y) ** 2);
var minDistance = Math.min(
ballon1char.distanceFromPlayer,
ballon2char.distanceFromPlayer,
ballon3char.distanceFromPlayer);
console.log(ballon1char);
console.log(ballon2char);
console.log(ballon3char);
for (let i = 0; i < 3; i++) {
if (minDistance == ballon1char.distanceFromPlayer)
return ballon1char
if (minDistance == ballon2char.distanceFromPlayer)
return ballon2char
if (minDistance == ballon3char.distanceFromPlayer)
return ballon3char
}
}
function loadComplete() {
console.log("Load is complete.");
canvas = document.getElementById("theCanvas");
ctx = canvas.getContext("2d");
myInterval = self.setInterval(function () { Tick() }, INTERVAL);
myInterval = self.setInterval(function () { laserTicker(detectCharClosest()) }, 2000);
function laserTicker(balloon) {
//gets the closest ballon to go to
laserDo(balloon);
}
function laserDo(balloon) {
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = "#F44336"; // "red";
ctx.moveTo(CharX + 16, CharY + 16);
ctx.lineTo(balloon.x, balloon.y);
// lasers.push({x: })
ctx.stroke();
}
I didn't put all of my code here so If something doesn't make sense please tell me. I'm still new to JavaScript and learning it. One way I thought I could make this work was by taking the distance between the player and the target and dividing it by the speed on the x and y axis then changing having it start from the player position and keeps on adding up on both axis until it reaches the target. That didn't work out though. If you have any suggestions then please tell me.
Thanks

Raphael JS to draw concentric lines

I'm stuck using Raphael JS : I want to make a basic animation that draws concentric lines while loading some stuff.
So, I made this function :
function loadingButton(width, height) {
width = width ? width : 240;
height = height ? height : 240;
var loadingButton = Raphael("loading-button", width, height);
var center = 120,
xloc = center,
yloc = center,
R = 120,
imgW = 124,
imgH = 140;
var lines;
var percent = loadingButton.text(center, center, '0');
percent.attr({'font-size': 36, 'fill': '#fff'});
var count = 0;
var interval = setInterval(function(){
if( count <= 100){
var start_x = center+Math.round((center-30)*Math.cos(4*count*Math.PI/200));
var start_y = center+Math.round((center-30)*Math.sin(4*count*Math.PI/200));
var end_x = center+Math.round((center-10)*Math.cos(4*count*Math.PI/200));
var end_y = center+Math.round((center-10)*Math.sin(4*count*Math.PI/200));
lines = loadingButton.path("M"+start_x+" "+start_y+"L"+end_x+" "+end_y).attr({"stroke":"#FFF","stroke-width":"1"});
percent.attr({text: count});
count++;
}
else {
clearInterval(interval);
}
}, 50);
};
With live demo here : http://jsfiddle.net/rfuqjL65/
The thing is, as you can see on the fiddle, the animation is starting on the first quarter (90°), not on the top (0°).
And well, the problem is : I want the animation starts on the top.
Any ideas ?
I can't get your fiddle running, but:
You can add/substract Math.PI/2 to the angle argument (in radians) for Math.cos and Math.sin in your coordinate variables, that should do the trick!
http://jsfiddle.net/rfuqjL65/2/
I change your code. And this worked.
And also
var start_x = center+Math.round((center-30)*Math.sin(4*count*Math.PI/200));
var start_y = center-Math.round((center-30)*Math.cos(4*count*Math.PI/200));
var end_x = center+Math.round((center-10)*Math.sin(4*count*Math.PI/200));
var end_y = center-Math.round((center-10)*Math.cos(4*count*Math.PI/200));

jQuery each loop returns data twice

Please, play with teh fiddle below. ONE bug goes as it should - turns its "head" and crawls in proper direction. But several bugs (starting with two and up) destroy it all. Jquery "each" returns coordinates twice so instead of two sets of coordinates for two bugs FOUR are generated.
$(document).ready(function () {
function bug() {
$('.bug').each(function () {
//var bugs = $('.bug').length;
var h = $(window).height() / 2;
var w = $(window).width() / 2;
var nh = Math.floor(Math.random() * h);
var nw = Math.floor(Math.random() * w);
//$this = $(this);
//var newCoordinates = makeNewPosition();
var p = $(this).offset();
var OldY = p.top;
var NewY = nh;
var OldX = p.left;
var NewX = nw;
var y = OldY - NewY;
var x = OldX - NewX;
angle = Math.atan2(y, x);
angle *= 180 / Math.PI
angle = Math.ceil(angle);
console.log(p);
$(this).delay(1000).rotate({
animateTo: angle
});
$(this).animate({
top: nh,
left: nw
}, 5000, "linear", function () {
bug();
});
});
};
bug();
});
http://jsfiddle.net/p400uhy2/
http://jsfiddle.net/p400uhy2/4/
As mentioned by #Noah B, the problem is that each "bug" is setting the loop for all "bugs".
I'd make bug() function per element, so that each "bug" can be set individually.
EDIT (#Roko C. Buljan comment)
function bug() {
// ... your code ...
// calculate animation time, so that each of bugs runs same fast in long and short distance:
var top_diff = Math.abs(OldY - nh),
left_diff = Math.abs(OldX - nw),
speed = Math.floor(Math.sqrt((top_diff * top_diff) + (left_diff * left_diff))) * 15;
$(this).animate({
top: nh,
left: nw
}, speed, "linear", function () {
// rerun bug() function only for that single element:
bug.call(this);
});
};
$('.bug').each(bug);
DEMO
The problem is that you had .each() calling a function with .each() in it...so each bug had the bug() callback. You just have to move the bug() call outside of the .each(){}. See fiddle: http://jsfiddle.net/p400uhy2/2/

Raphael .mouseup() function only firing once

I'm attempting to have a draggable element snap back to the position of another element in Rapheal after dragging it. The problem I'm experiencing is that the .mouseup() function only executes the functions within it once. After you drag or move the element again, it will not longer execute the positioning functions I have within it.
My end goal is:
Drag the red square
When the red square is let go off (mouseup), snap square back to the blue square position.
Here is the code I've tried using, but I can't seem to get it to function correctly:
JSfiddle: http://jsfiddle.net/4GWEU/3/
Javascript:
//Makes elements Draggable.
Raphael.st.draggable = function() {
var me = this,
lx = 0,
ly = 0,
ox = 0,
oy = 0,
moveFnc = function(dx, dy) {
lx = dx + ox;
ly = dy + oy;
me.transform('t' + lx + ',' + ly);
},
startFnc = function() {
//window.draggedElement = this;
},
endFnc = function() {
ox = lx;
oy = ly;
};
this.drag(moveFnc, startFnc, endFnc);
};
var container = document.getElementById('container');
var paper = Raphael(container, '539', '537');
var shape1 = paper.rect(50,50, 50,50);
shape1.attr({x: '50',y: '50',fill: 'red','stroke-width': '0','stroke-opacity': '1'});
shape1Set = paper.set(shape1);
shape1Set.draggable();
var shape2 = paper.rect(50,50, 50,50);
shape2.attr({x: '150',y: '50',fill: 'blue','stroke-width': '0','stroke-opacity': '1'});
shape1Set.mousedown(function(event) {
console.log('mousedown');
});
shape1Set.mouseup(function(event) {
console.log('mouseup');
positionElementToElement(shape1, shape2);
});
$('#runPosition').click(function () {
positionElementToElement(shape1, shape2);
});
$('#runPosition2').click(function () {
positionElementToElement2(shape1, shape2);
});
function positionElementToElement(element, positionTargetElement)
{
var parentBBox = positionTargetElement.getBBox();
parent_x = parentBBox.x;
parent_y = parentBBox.y;
parent_width = parentBBox.width;
parent_height = parentBBox.height;
var elementBBox = element.getBBox();
element_width = elementBBox.width;
element_height = elementBBox.height;
var x_pos = parent_x + (parent_width / 2) - (element_width / 2) + 100;
var y_pos = parent_y + (parent_height / 2) - (element_height / 2) + 100;
console.log('Positioning element to: '+x_pos+' '+y_pos);
element.animate({'x' : x_pos, 'y' : y_pos}, 100);
}
function positionElementToElement2(element, positionTargetElement)
{
var parentBBox = positionTargetElement.getBBox();
parent_x = parentBBox.x;
parent_y = parentBBox.y;
parent_width = parentBBox.width;
parent_height = parentBBox.height;
var elementBBox = element.getBBox();
element_width = elementBBox.width;
element_height = elementBBox.height;
var x_pos = parent_x + (parent_width / 2) - (element_width / 2);
var y_pos = parent_y + (parent_height / 2) - (element_height / 2);
console.log('Positioning element to: '+x_pos+' '+y_pos);
element.animate({'x' : x_pos, 'y' : y_pos}, 100);
}
HTML:
Run Position
Run Position2
<div id="container"></div>
Notes:
I've duplicated the positionElementToElement() function and set one of them with an offset. I've binded both functions to the Run Position 1 and Run Position 2 links.
After dragging the item, clicking the Run Position 1 link no longer sets the square back where it should go (even though the function is logging the same x/y coordinates as when it worked.
I've figured out how to do this properly.
You have to modify the x and y attributes of the element directly.
It's also important to note that when retrieving the x and y attributes from an element using element.attr('x'); or element.attr('y'); it returns a string value, not an integer. Because of this, you have to use parseInt() on these returned values to properly add up the movement x and y values to apply to the element when it moves.
The following code will snap the red square to the blue square, when the red square is moved.
Working Example: http://jsfiddle.net/naQQ2/2/
window.onload = function () {
var R = Raphael(0, 0, "100%", "100%"),
shape1 = R.rect(50,50, 50,50);
shape1.attr({x:'50',y:'50',fill: 'red','stroke-width': '0','stroke-opacity': '1'});
shape2 = R.rect(50,50, 50,50);
shape2.attr({x:'150',y:'50',fill: 'blue','stroke-width': '0','stroke-opacity': '1'});
var start = function () {
console.log(this);
this.ox = parseInt(this.attr('x'));
this.oy = parseInt(this.attr('y'));
this.animate({opacity: .25}, 500, ">");
},
move = function (dx, dy) {
this.attr({x: this.ox + dx, y: this.oy + dy});
},
up = function () {
//Snap to shape2 on mouseup.
var snapx = parseInt(shape2.attr("x"));
snapy = parseInt(shape2.attr("y"));
this.animate({x: snapx, y: snapy}, 100);
this.animate({opacity: 1}, 500, ">");
};
R.set(shape1, shape2).drag(move, start, up);
};

javascript how to move element on route on scroll

I have a route like a vertical snake. (like this http://www.my-favorite-coloring.net/Images/Large/Animals-Reptiles-Snake-31371.png )
How I can move element (circle 10x10) on route by X and Y position on scroll?
Horizonal is ok :
var coin = $('#coin');
$(window).scroll(function(){
var coinTop = coin.css('top'),
cointLeft = coin.css('left');
if($(window).scrollTop() > 100 && $(window).scrollTop() < 600){
$("#coin").css({ "left": contLeft + 'px' });
};
});
But how I cat move it smoothly along the route?
I would suggest using a vector (SVG) library, namely raphael.js for the animation part.
In raphael.js you can define a path and then animate any object along the length of that path.
Please see the example and a corresponding stackoverflow thread for better understanding:
http://raphaeljs.com/gear.html
How to animate a Raphael object along a path?
Compared to the thread, you need to attach the animation on the onScroll event, as opposed to attaching it to a Interval.
Edit:
Adding the relevant code snippet from the link, as the commenter suggested:
HTML:
<div id="holder"></div>
JS:
var e;
var myPath;
var animation; //make the variables global, so you can access them in the animation function
window.onload = function() {
var r = Raphael("holder", 620, 420),
discattr = {
fill: "#666",
stroke: "none"
};
function curve(x, y, zx, zy, colour) {
var ax = Math.floor(Math.random() * 200) + x;
var ay = Math.floor(Math.random() * 200) + (y - 100);
var bx = Math.floor(Math.random() * 200) + (zx - 200);
var by = Math.floor(Math.random() * 200) + (zy - 100);
e = r.image("http://openclipart.org/image/800px/svg_to_png/10310/Odysseus_boy.png", x, y, 10, 10);
var path = [["M", x, y], ["C", ax, ay, bx, by, zx, zy]];
myPath = r.path(path).attr({
stroke: colour,
"stroke-width": 2,
"stroke-linecap": "round",
"stroke-opacity": 0.2
});
controls = r.set(
r.circle(x, y, 5).attr(discattr), r.circle(zx, zy, 5).attr(discattr));
}
curve(100,100,200,300,"red");
animation = window.setInterval("animate()", 10); //execute the animation function all 10ms (change the value for another speed)
};
var counter = 0; // a counter that counts animation steps
function animate(){
if(myPath.getTotalLength() <= counter){ //break as soon as the total length is reached
clearInterval(animation);
return;
}
var pos = myPath.getPointAtLength(counter); //get the position (see Raphael docs)
e.attr({x: pos.x, y: pos.y}); //set the circle position
counter++; // count the step counter one up
};
Update:
I've recently used pathAnimator for the same task. Be careful about the performance though, a complicated animation might be quite intensive.

Categories

Resources