javascript how to move element on route on scroll - javascript

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.

Related

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/

How to Show/ hide part of canvas?

Here is My JS fiddle
I have a requirement like when user clicks on center circle, that should toggle outer circle and when user clicks on outer small circles that should change center circle value.
Here i am not getting how to Show/ hide part of Canvas when user clicks on center circle?
Any help on how to do this?
GenerateCanvas();
function GenerateCanvas() {
try {
var FlagCircleCenterCoordinates = new Array();
var FlagCircles = [];
var CenterX = document.getElementById('canvasFlag').width / 2;
var CenterY = document.getElementById('canvasFlag').height / 2;
var OuterTrackRadius = 98;
var InnerTrackRadius = 70;
var InnerCircleRadius = 20;
var FlagElement = document.getElementById("canvasFlag");
var ObjContext = FlagElement.getContext("2d");
// Outer track
ObjContext.fillStyle = "#FFF";
ObjContext.beginPath();
ObjContext.arc(CenterX, CenterY, OuterTrackRadius, 0, 2 * Math.PI);
ObjContext.strokeStyle = '#CCC';
ObjContext.stroke();
ObjContext.fill();
// Inner track
ObjContext.beginPath();
ObjContext.arc(CenterX, CenterY, InnerTrackRadius, 0, 2 * Math.PI);
ObjContext.strokeStyle = '#CCC';
ObjContext.stroke();
// Inner small circle
ObjContext.beginPath();
ObjContext.arc(CenterX, CenterY, InnerCircleRadius, 0, 2 * Math.PI);
ObjContext.strokeStyle = '#CCC';
ObjContext.stroke();
//Max 17...other wide need to change the Inner and Outer circle radius
var FlagImagesArray = [1, 2, 3,4,5];
if (FlagImagesArray.length > 0) {
var StepAngle = 2 * Math.PI / FlagImagesArray.length;
var FlagCircleRadius = (OuterTrackRadius - InnerTrackRadius) / 2;
var RadiusOfFlagCircleCenters = OuterTrackRadius - FlagCircleRadius;
for (var LoopCnt in FlagImagesArray) {
var CircleCenterCoordinates = new Object();
CircleCenterCoordinates.PostionX = CenterX + (Math.cos(StepAngle * (parseInt(LoopCnt) + 1)) * RadiusOfFlagCircleCenters);
CircleCenterCoordinates.PostionY = CenterY + (Math.sin(StepAngle * (parseInt(LoopCnt) + 1)) * RadiusOfFlagCircleCenters);
ObjContext.beginPath();
ObjContext.arc(CircleCenterCoordinates.PostionX, CircleCenterCoordinates.PostionY, FlagCircleRadius, 0, 2 * Math.PI);
ObjContext.strokeStyle = '#CCC';
ObjContext.stroke();
ObjContext.fillStyle = 'blue';
ObjContext.fillText(FlagImagesArray[LoopCnt], CircleCenterCoordinates.PostionX, CircleCenterCoordinates.PostionY);
FlagCircleCenterCoordinates[LoopCnt] = CircleCenterCoordinates;
var ObjFlagCircle = {
Left : CircleCenterCoordinates.PostionX - FlagCircleRadius,
Top : CircleCenterCoordinates.PostionY - FlagCircleRadius,
Right : CircleCenterCoordinates.PostionX + FlagCircleRadius,
Bottom : CircleCenterCoordinates.PostionY + FlagCircleRadius,
FlagName : FlagImagesArray[LoopCnt]
}
FlagCircles[LoopCnt] = ObjFlagCircle;
}
$('#canvasFlag').mousemove(function (Event) {
debugger;
$(this).css('cursor', 'auto');
var ClickedX = Event.pageX - $('#canvasFlag').offset().left;
var ClickedY = Event.pageY - $('#canvasFlag').offset().top;
for (var Count = 0; Count < FlagCircles.length; Count++) {
if (ClickedX < FlagCircles[Count].Right &&
ClickedX > FlagCircles[Count].Left &&
ClickedY > FlagCircles[Count].Top &&
ClickedY < FlagCircles[Count].Bottom) {
$(this).css('cursor', 'pointer');
break;
}
}
});
$('#canvasFlag').click(function (Event) {
debugger;
$(this).css('cursor', 'auto');
var ClickedX = Event.pageX - $('#canvasFlag').offset().left;
var ClickedY = Event.pageY - $('#canvasFlag').offset().top;
for (var Count = 0; Count < FlagCircles.length; Count++) {
if (ClickedX < FlagCircles[Count].Right &&
ClickedX > FlagCircles[Count].Left &&
ClickedY > FlagCircles[Count].Top &&
ClickedY < FlagCircles[Count].Bottom) {
ObjContext.fillStyle = "#FFF";
ObjContext.beginPath();
ObjContext.arc(CenterX, CenterY, InnerCircleRadius - 1, 0, Math.PI * 2);
ObjContext.closePath();
ObjContext.fill();
ObjContext.fillStyle = "blue";
ObjContext.fillText(FlagCircles[Count].FlagName, CenterX, CenterY);
break;
}
}
});
}
}
catch (E) {
alert(E);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<canvas id="canvasFlag" width="200" height="200">
Your browser does not support the canvas
</canvas>
Oh man. Ok so first off, you'll need to get rid of the try/catch statement, it isn't doing anything.
Next you'll need to make all those vars you have outside of the function body, we need to be able to access them from another function
It looks like you have all your click functionality done, and it's working too. That's good, just go ahead and move both of the lines that start with jquery outside of the generateCanvas function. They only need to run once, and we're going to need to call generate canvas again.
Fourth, make a variable to toggle the outer circle off and on somewhere, and only draw the outer ring in generateCanvas() when that variable is true. You should also set another Global variable that gets set to Count, right before you break, so that you can remember it when you regenerate the canvas.
Take all the code you have in your click function to draw the inner circle with the number, and put that inside of generate canvas. Make sure that code only calls if the variable you used to remember Count is set to something (ie you had already clicked an outer number)
Next, add a generateCanvas() call in your click function.Now your click function sets the variable you use to represent the center value, redraws the canvas, and returns. You'll need more logic in your mouse down function in order to figure out when you clicked the center, but based on the code you already have you can figure that out, your main problem is just that this was set up to run once, not multiple times. That makes the canvas a lot more like an image instead of an active drawing element
Don't forget to add FlagElement.width = FlagElement.width to clear the canvas! (or just draw a background over it)

Raphael transform object diagonally and infinite setIntervals

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.

Creating horizontal linear gradient with Raphael

Im new to the Raphael library (looks great so far btw)
I was wondering how to create a horizontal linear gradient.
Here's my test code so far, mostly based from examples I've been looking at:-
$(function () {
var paper = Raphael(0, 0, $(window).width(), $(window).height());
var path = paper.path("M800,100 L800,600 Q801,610 802,600 T803,600 L803,100 Q802,110 801,100 T800,100").attr({
"fill": "90-#f00:5-#00f:100",
"fill-opacity": 0.5
});
var pathArray = path.attr("path");
handle = paper.circle(pathArray[0][1], 350, 5).attr({
fill: "black",
cursor: "pointer",
"stroke-width": 1,
stroke: "transparent"
});
var start = function () {
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 });
pathArray[0][1] = pathArray[1][1] = pathArray[6][1] = X;
path.attr({ path: pathArray });
},
up = function () {
this.dx = this.dy = 0;
};
handle.drag(move, start, up);
});
I see from the SVG spec on the w3 site there is an x1,x2,y1,y2 attribute in the actual linearGradient tag (although I'm not even sure if they handle orientation? http://www.w3.org/TR/SVG/pservers.html#LinearGradientElement).
I just haven't used Raphael enough to know how to set that in my path attribute.
Cheers,
wacka.
P.S.
EDIT: Adding the following helps, but only works in IE:-
$("linearGradient").attr("x1", "0");
$("linearGradient").attr("x2", "100%");
$("linearGradient").attr("y1", "0");
$("linearGradient").attr("y2", "0");
ANOTHER EDIT: Interestingly, the above only worked in IE but the following works in both (even though the HTML is the same):-
$("defs").children().attr("x1", "0");
$("defs").children().attr("x2", "100%");
$("defs").children().attr("y1", "0");
$("defs").children().attr("y2", "0");
For some reason the following is 1 in IE and 0 in chrome:-
$("lineargradient").length
Now, although this works, surely there must be a nicer way?
Here's an example of a rect with a horizontal and a vertical gradient.
paper.rect(100,100,200,200).attr({"fill":"0-#f00:5-#00f:100"});
paper.rect(300,300,200,200).attr({"fill":"90-#f00:5-#00f:100"});
the first value in the fill is the angle of your gradient. You can apply that to your path.

Categories

Resources